You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3721 lines
99 KiB
3721 lines
99 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: TF2's player object.
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include <stdarg.h>
|
||
|
#include "player.h"
|
||
|
#include "tf_player.h"
|
||
|
#include "gamerules.h"
|
||
|
#include "trains.h"
|
||
|
#include "entitylist.h"
|
||
|
#include "menu_base.h"
|
||
|
#include "basecombatweapon.h"
|
||
|
#include "controlzone.h"
|
||
|
#include "tf_shareddefs.h"
|
||
|
#include "AmmoDef.h"
|
||
|
#include "techtree.h"
|
||
|
#include "in_buttons.h"
|
||
|
#include "tf_team.h"
|
||
|
#include "client.h"
|
||
|
#include "baseviewmodel.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "tf_obj.h"
|
||
|
#include "weapon_builder.h"
|
||
|
#include "orders.h"
|
||
|
#include "decals.h"
|
||
|
#include "tf_func_resource.h"
|
||
|
#include "resource_chunk.h"
|
||
|
#include "team_messages.h"
|
||
|
#include "tier0/dbg.h"
|
||
|
#include "tf_obj_respawn_station.h"
|
||
|
#include "tf_obj_resourcepump.h"
|
||
|
#include "tf_class_commando.h"
|
||
|
#include "tf_class_defender.h"
|
||
|
#include "tf_class_escort.h"
|
||
|
#include "tf_class_infiltrator.h"
|
||
|
#include "tf_class_medic.h"
|
||
|
#include "tf_class_recon.h"
|
||
|
#include "tf_class_sniper.h"
|
||
|
#include "tf_class_support.h"
|
||
|
#include "tf_class_sapper.h"
|
||
|
#include "sendproxy.h"
|
||
|
#include "ragdoll_shadow.h"
|
||
|
#include "vstdlib/random.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "bone_setup.h"
|
||
|
#include "weapon_combatshield.h"
|
||
|
#include "weapon_twohandedcontainer.h"
|
||
|
#include "NDebugOverlay.h"
|
||
|
#include "tier1/strtools.h"
|
||
|
#include "IEffects.h"
|
||
|
#include "info_act.h"
|
||
|
#include "ai_basehumanoid.h"
|
||
|
#include "tf_stats.h"
|
||
|
#include "iservervehicle.h"
|
||
|
#include "tf_vehicle_teleport_station.h"
|
||
|
#include "globals.h"
|
||
|
|
||
|
#define MAX_EXPLOSIVE_VELOCITY 600.0f
|
||
|
|
||
|
extern ConVar tf_knockdowntime;
|
||
|
|
||
|
extern ConVar inv_demo;
|
||
|
|
||
|
ConVar tf_autoteam( "tf_autoteam", "1", 0, "Automatically place players on the team with the least players." );
|
||
|
ConVar tf_destroyobjects( "tf_destroyobjects", "1", FCVAR_CHEAT, "Destroy objects when players change class or team." );
|
||
|
|
||
|
IMPLEMENT_SERVERCLASS_ST(CBaseTFPlayer, DT_BaseTFPlayer)
|
||
|
SendPropDataTable(SENDINFO_DT(m_TFLocal), &REFERENCE_SEND_TABLE(DT_TFLocal), SendProxy_SendLocalDataTable),
|
||
|
|
||
|
SendPropInt(SENDINFO(m_iPlayerClass), 4, SPROP_UNSIGNED),
|
||
|
|
||
|
// Class Data Tables
|
||
|
SendPropDataTable( SENDINFO_DT( m_PlayerClasses ), &REFERENCE_SEND_TABLE( DT_AllPlayerClasses ), SendProxy_SendLocalDataTable ),
|
||
|
|
||
|
SendPropEHandle( SENDINFO( m_hSelectedMCV ) ),
|
||
|
SendPropInt( SENDINFO(m_iCurrentZoneState ), 3 ),
|
||
|
SendPropInt( SENDINFO(m_iMaxHealth ), 8, SPROP_UNSIGNED ),
|
||
|
SendPropInt( SENDINFO(m_TFPlayerFlags), TF_PLAYER_NUMFLAGS, SPROP_UNSIGNED ),
|
||
|
SendPropInt( SENDINFO( m_bUnderAttack ), 1, SPROP_UNSIGNED ),
|
||
|
SendPropInt( SENDINFO( m_bIsBlocking ), 1, SPROP_UNSIGNED ),
|
||
|
|
||
|
// Sniper - will get moved to a class data table
|
||
|
SendPropVector( SENDINFO(m_vecDeployedAngles), -1, SPROP_COORD ),
|
||
|
SendPropInt( SENDINFO( m_bDeployed ), 1, SPROP_UNSIGNED ),
|
||
|
SendPropInt( SENDINFO( m_bDeploying ), 1, SPROP_UNSIGNED ),
|
||
|
SendPropInt( SENDINFO( m_bUnDeploying ), 1, SPROP_UNSIGNED ),
|
||
|
|
||
|
// Infiltrator - will get moved to a class data table
|
||
|
SendPropFloat( SENDINFO( m_flCamouflageAmount ), 7, SPROP_ROUNDDOWN, 0.0f, 100.0f ),
|
||
|
|
||
|
SendPropEHandle(SENDINFO(m_hSpawnPoint)),
|
||
|
|
||
|
SendPropExclude( "DT_BaseAnimating" , "m_flPoseParameter" ),
|
||
|
SendPropExclude( "DT_BaseAnimating" , "m_flPlaybackRate" ),
|
||
|
|
||
|
END_SEND_TABLE()
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( player, CBaseTFPlayer );
|
||
|
PRECACHE_REGISTER(player);
|
||
|
|
||
|
BEGIN_DATADESC( CBaseTFPlayer )
|
||
|
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "Respawn", InputRespawn ),
|
||
|
|
||
|
// Function Pointers
|
||
|
DEFINE_THINKFUNC( TFPlayerDeathThink ),
|
||
|
|
||
|
END_DATADESC()
|
||
|
|
||
|
|
||
|
BEGIN_PREDICTION_DATA_NO_BASE( CTFPlayerLocalData )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CBaseTFPlayer )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
|
||
|
bool IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot );
|
||
|
void respawn( CBaseEntity *pEdict, bool fCopyCorpse );
|
||
|
int TrainSpeed(int iSpeed, int iMax);
|
||
|
void BulletWizz( Vector vecSrc, Vector vecEndPos, edict_t *pShooter, bool isTracer );
|
||
|
|
||
|
extern float g_flNextReinforcementTime;
|
||
|
extern short g_sModelIndexFireball;
|
||
|
extern CBaseEntity *g_pLastSpawn;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Don't do anything for now
|
||
|
// Input : *pFormat -
|
||
|
// ... -
|
||
|
// Output : static void
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void StatusPrintf( bool clear, int destination, char *pFormat, ... )
|
||
|
{
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
va_list marker;
|
||
|
char msg[8192];
|
||
|
|
||
|
va_start(marker, pFormat);
|
||
|
Q_vsnprintf(msg, sizeof( msg ), pFormat, marker);
|
||
|
va_end(marker);
|
||
|
|
||
|
Msg( msg );
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
#pragma warning( disable : 4355 )
|
||
|
|
||
|
//=====================================================================
|
||
|
// PLAYER HANDLING
|
||
|
//=====================================================================
|
||
|
CBaseTFPlayer::CBaseTFPlayer() :
|
||
|
m_PlayerClasses( this ), m_PlayerAnimState( this )
|
||
|
{
|
||
|
// HACK because player's have pev set in baseclass constructor
|
||
|
// which triggers an assert that we want to keep.
|
||
|
{
|
||
|
edict_t *savepev = edict();
|
||
|
NetworkProp()->SetEdict( NULL );
|
||
|
UseClientSideAnimation();
|
||
|
NetworkProp()->SetEdict( savepev );
|
||
|
}
|
||
|
|
||
|
m_bWasMoving = false;
|
||
|
|
||
|
m_iLastSecondsToGo = -1;
|
||
|
m_TFLocal.m_nInTacticalView = 0;
|
||
|
m_TFLocal.m_pPlayer = this;
|
||
|
m_bSwitchingView = false;
|
||
|
ClearActiveWeapon();
|
||
|
|
||
|
m_iPlayerClass = TFCLASS_UNDECIDED;
|
||
|
SetPlayerClass( TFCLASS_UNDECIDED );
|
||
|
m_pCurrentMenu = NULL;
|
||
|
m_TFPlayerFlags = 0;
|
||
|
m_bDeploying = false;
|
||
|
m_bDeployed = false;
|
||
|
m_bUnDeploying = false;
|
||
|
m_flFinishedDeploying = 0;
|
||
|
SetOrder( NULL );
|
||
|
|
||
|
m_nPreferredTechnology = -1;
|
||
|
m_nMedicDamageBoosts = 0;
|
||
|
|
||
|
m_hSpawnPoint = NULL;
|
||
|
m_flLastTimeDamagedByEnemy = -1000;
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < MOMENTUM_MAXSIZE; i++ )
|
||
|
{
|
||
|
m_aMomentum[ i ] = 1.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::UpdateOnRemove( void )
|
||
|
{
|
||
|
if ( m_hSelectedOrder )
|
||
|
{
|
||
|
GetTFTeam()->RemoveOrdersToPlayer( this );
|
||
|
Assert( !m_hSelectedOrder.Get() );
|
||
|
}
|
||
|
|
||
|
ClearPlayerClass();
|
||
|
|
||
|
ClearClientRagdoll( false );
|
||
|
|
||
|
// Chain at end to mimic destructor unwind order
|
||
|
BaseClass::UpdateOnRemove();
|
||
|
}
|
||
|
|
||
|
CBaseTFPlayer::~CBaseTFPlayer()
|
||
|
{
|
||
|
SetPlayerClass( (TFClass)-1 );
|
||
|
}
|
||
|
|
||
|
bool CBaseTFPlayer::IsHidden() const
|
||
|
{
|
||
|
return (m_TFPlayerFlags & TF_PLAYER_HIDDEN) != 0;
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::SetHidden( bool bHidden )
|
||
|
{
|
||
|
if ( bHidden )
|
||
|
m_TFPlayerFlags |= TF_PLAYER_HIDDEN;
|
||
|
else
|
||
|
m_TFPlayerFlags &= ~TF_PLAYER_HIDDEN;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called everytime the player's respawned
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::Spawn( void )
|
||
|
{
|
||
|
m_bUnderAttack = false;
|
||
|
m_pCurrentZone = NULL;
|
||
|
ClearClientRagdoll( false );
|
||
|
|
||
|
g_pNotify->ReportNamedEvent( this, "PlayerSpawned" );
|
||
|
|
||
|
DeactivateMovementConstraint();
|
||
|
|
||
|
if ( IsInAVehicle() )
|
||
|
{
|
||
|
LeaveVehicle();
|
||
|
}
|
||
|
|
||
|
// If the player doesn't have a spawn station set, find one
|
||
|
if ( m_hSpawnPoint == NULL || !InSameTeam( m_hSpawnPoint ) )
|
||
|
{
|
||
|
m_hSpawnPoint = GetInitialSpawnPoint();
|
||
|
}
|
||
|
|
||
|
if ( inv_demo.GetBool() )
|
||
|
{
|
||
|
if ( !GetPlayerClass() )
|
||
|
{
|
||
|
ChangeClass( TFCLASS_MEDIC );
|
||
|
m_Local.m_iHideHUD |= HIDEHUD_MISCSTATUS;
|
||
|
engine->ServerCommand("r_DispEnableLOD 0\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Must be done before baseclass spawn, so it's correct for when we find a spawnpoint
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->SetPlayerHull();
|
||
|
}
|
||
|
|
||
|
// Use human commando model until we know our class
|
||
|
SetModel( "models/player/human_commando.mdl" );
|
||
|
|
||
|
BaseClass::Spawn();
|
||
|
|
||
|
m_flFractionalBoost = 0.0f;
|
||
|
|
||
|
// Create second view model ( for support/commando, etc )
|
||
|
CreateViewModel( 1 );
|
||
|
|
||
|
// Tell the PlayerClass that this player's just respawned
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
RemoveFlag( FL_NOTARGET );
|
||
|
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||
|
|
||
|
GetPlayerClass()->RespawnClass();
|
||
|
if ( GetActiveWeapon() )
|
||
|
{
|
||
|
// Holster weapon immediately, to allow it to cleanup
|
||
|
// GetActiveWeapon()->Holster( ); // NJS: test
|
||
|
|
||
|
if (GetActiveWeapon()->HasAnyAmmo())
|
||
|
{
|
||
|
Weapon_Switch( GetActiveWeapon() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SwitchToNextBestWeapon( GetActiveWeapon() );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SwitchToNextBestWeapon( NULL );
|
||
|
}
|
||
|
|
||
|
SetPlayerModel();
|
||
|
|
||
|
// Make sure they're not deployed
|
||
|
FinishUnDeploying();
|
||
|
|
||
|
// Remove my personal orders
|
||
|
if ( GetTFTeam() )
|
||
|
{
|
||
|
GetTFTeam()->RemoveOrdersToPlayer( this );
|
||
|
}
|
||
|
|
||
|
RemoveAllDecals();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No class? can't target this dude
|
||
|
AddFlag( FL_NOTARGET );
|
||
|
|
||
|
// Remove everything
|
||
|
RemoveAllItems( false );
|
||
|
|
||
|
// Set/unset m_bHidden instead to hide the tf player
|
||
|
SetHidden( true );
|
||
|
|
||
|
AddSolidFlags( FSOLID_NOT_SOLID );
|
||
|
SetMoveType( MOVETYPE_NONE );
|
||
|
|
||
|
SetModel( "models/player/human_commando.mdl" );
|
||
|
|
||
|
// If they're not in a team, bring up the Team Menu
|
||
|
if ( !IsInAnyTeam() )
|
||
|
{
|
||
|
if ( tf_autoteam.GetFloat() )
|
||
|
{
|
||
|
// Autoteam the player
|
||
|
PlacePlayerInTeam();
|
||
|
ForceRespawn();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Let players choose their team
|
||
|
m_pCurrentMenu = gMenus[MENU_TEAM];
|
||
|
}
|
||
|
}
|
||
|
else // Bring up the Class Menu
|
||
|
{
|
||
|
m_pCurrentMenu = gMenus[MENU_CLASS];
|
||
|
}
|
||
|
|
||
|
m_MenuRefreshTime = gpGlobals->curtime;
|
||
|
|
||
|
m_nPreferredTechnology = -1;
|
||
|
}
|
||
|
|
||
|
SetCantMove( false );
|
||
|
|
||
|
|
||
|
m_TFLocal.m_nInTacticalView = 0;
|
||
|
m_flLastTimeDamagedByEnemy = -1000;
|
||
|
|
||
|
// Purge resource chunks
|
||
|
for ( int i=0; i < m_TFLocal.m_iResourceAmmo.Count(); i++ )
|
||
|
m_TFLocal.m_iResourceAmmo.Set( i, 0 );
|
||
|
|
||
|
ResetKnockdown();
|
||
|
SetGagged( false );
|
||
|
SetUsingThermalVision( false );
|
||
|
ClearCamouflage();
|
||
|
SetIDEnt( NULL );
|
||
|
m_iPowerups = 0;
|
||
|
|
||
|
// MUST set the right player hull before placing the player somewhere.
|
||
|
if ( GetPlayerClass() )
|
||
|
GetPlayerClass()->SetPlayerHull();
|
||
|
|
||
|
g_pGameRules->GetPlayerSpawnSpot( this );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CleanupOnActStart( void )
|
||
|
{
|
||
|
// Tell all our weapons
|
||
|
for ( int i = 0; i < WeaponCount(); i++ )
|
||
|
{
|
||
|
if ( GetWeapon(i) )
|
||
|
{
|
||
|
((CBaseTFCombatWeapon*)GetWeapon(i))->CleanupOnActStart();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RecalculateSpeed( void )
|
||
|
{
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->SetMaxSpeed( GetPlayerClass()->GetMaxSpeed() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: I just killed another player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::KilledPlayer( CBaseTFPlayer *pVictim )
|
||
|
{
|
||
|
TFStats()->IncrementPlayerStat( this, TF_PLAYER_STAT_KILL_COUNT, 1 );
|
||
|
TFStats()->IncrementTeamStat( GetTeamNumber(), TF_TEAM_STAT_KILL_COUNT, 1 );
|
||
|
|
||
|
// Am I in a rampage?
|
||
|
if ( HasPowerup( POWERUP_RUSH ) && IsInRampage() )
|
||
|
{
|
||
|
// Extend my rush
|
||
|
AttemptToPowerup( POWERUP_RUSH, ADRENALIN_RAMPAGE_EXTEND );
|
||
|
|
||
|
// Let 'em know
|
||
|
EmitSound( "BaseTFPlayer.BloodSportKiller" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called only the first time a player's placed in the map
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::InitialSpawn( void )
|
||
|
{
|
||
|
BaseClass::InitialSpawn();
|
||
|
SetWeaponBuilder( NULL );
|
||
|
|
||
|
m_bFirstTeamSpawn = true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::Precache( void )
|
||
|
{
|
||
|
//!! hack for radar
|
||
|
BaseClass::Precache();
|
||
|
|
||
|
PrecacheScriptSound( "BaseTFPlayer.BloodSportKiller" );
|
||
|
PrecacheScriptSound( "Humans.Death" );
|
||
|
PrecacheScriptSound( "AlienCommando.Death" );
|
||
|
PrecacheScriptSound( "AlienMedic.Death" );
|
||
|
PrecacheScriptSound( "AlienDefender.Death" );
|
||
|
PrecacheScriptSound( "AlienEscort.Death" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.StartDeploying" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.StartUnDeploying" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.KnockedDown" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.ThermalOn" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.ThermalOff" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.PickupResources" );
|
||
|
PrecacheScriptSound( "BaseTFPlayer.DonateResources" );
|
||
|
|
||
|
// Class specific sounds
|
||
|
PrecacheScriptSound( "Commando.BootHit" );
|
||
|
PrecacheScriptSound( "Commando.BootSwing" );
|
||
|
PrecacheScriptSound( "Commando.BullRushScream" );
|
||
|
PrecacheScriptSound( "Commando.BullRushFlesh" );
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::UpdateClientData( void )
|
||
|
{
|
||
|
CTeam *pTeam = GetTeam();
|
||
|
if ( pTeam )
|
||
|
pTeam->UpdateClientData( this );
|
||
|
|
||
|
BaseClass::UpdateClientData();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ForceClientDllUpdate( void )
|
||
|
{
|
||
|
BaseClass::ForceClientDllUpdate();
|
||
|
|
||
|
// Force any active menu to be reset
|
||
|
m_MenuRefreshTime = 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Forces an immediate respawn of the player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ForceRespawn( void )
|
||
|
{
|
||
|
Spawn();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Input handler that forces a respawn of the player.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::InputRespawn( inputdata_t &inputdata )
|
||
|
{
|
||
|
ForceRespawn();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::InitHUD( void )
|
||
|
{
|
||
|
CSingleUserRecipientFilter user( this );
|
||
|
user.MakeReliable();
|
||
|
|
||
|
// If we're in an act, tell it to update the client
|
||
|
if ( g_hCurrentAct )
|
||
|
{
|
||
|
g_hCurrentAct->UpdateClient( this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Player has just tried to switch to a new weapon
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SelectItem( const char *pstr, int iSubType )
|
||
|
{
|
||
|
// can't change weapon while deployed
|
||
|
if ( IsPlayerLockedInPlace() || IsDeployed() || IsDeploying() )
|
||
|
return;
|
||
|
|
||
|
// Pass through to CBaseCombatWeapon code
|
||
|
BaseClass::SelectItem( pstr, iSubType );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Put the player in the specified team
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ChangeTeam( int iTeamNum )
|
||
|
{
|
||
|
// If we're changing team, clear my order
|
||
|
if ( iTeamNum != GetTeamNumber() )
|
||
|
{
|
||
|
SetOrder(NULL);
|
||
|
if ( tf_destroyobjects.GetFloat() )
|
||
|
{
|
||
|
RemoveAllObjects( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Force full tech tree update
|
||
|
for ( int i = 0 ; i < MAX_TECHNOLOGIES; i++ )
|
||
|
{
|
||
|
m_rgClientTechAvail[ i ].m_nAvailable = -1;
|
||
|
}
|
||
|
|
||
|
BaseClass::ChangeTeam( iTeamNum );
|
||
|
|
||
|
// Now handle resources:
|
||
|
// - If it's the first spawn ever, give the player the team's currently calculated resource amount
|
||
|
// - If the player has more resources than the team's joining amount, drop his resources to that amount. Otherwise, he can keep his current.
|
||
|
if ( GetGlobalTFTeam( iTeamNum ) )
|
||
|
{
|
||
|
float flJoiningResources = GetGlobalTFTeam( iTeamNum )->GetJoiningPlayerResources();
|
||
|
if ( m_bFirstTeamSpawn )
|
||
|
{
|
||
|
m_bFirstTeamSpawn = false;
|
||
|
SetBankResources( flJoiningResources );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( flJoiningResources < GetBankResources() )
|
||
|
{
|
||
|
SetBankResources( flJoiningResources );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clear the client ragdoll, when changing teams.
|
||
|
ClearClientRagdoll( false );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Automatically place the player in the most appropriate team
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PlacePlayerInTeam( void )
|
||
|
{
|
||
|
CTFTeam *pTargetTeam = NULL;
|
||
|
|
||
|
// Find the team with the least players in it
|
||
|
for ( int i = 0; i < MAX_TF_TEAMS; i++ )
|
||
|
{
|
||
|
CTFTeam *pTeam = GetGlobalTFTeam(i);
|
||
|
|
||
|
if ( pTargetTeam )
|
||
|
{
|
||
|
if ( pTeam->GetNumPlayers() < pTargetTeam->GetNumPlayers() )
|
||
|
pTargetTeam = pTeam;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pTargetTeam = pTeam;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ChangeTeam( pTargetTeam->GetTeamNumber() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return true if the specified class is available to this player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsClassAvailable( TFClass iClass )
|
||
|
{
|
||
|
char str[128];
|
||
|
Q_snprintf( str, sizeof( str ), "class_%s", GetTFClassInfo( iClass )->m_pClassName );
|
||
|
return HasNamedTechnology( str );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ChangeClass( TFClass iClass )
|
||
|
{
|
||
|
// If they've got a playerclass, kill it
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
if ( tf_destroyobjects.GetFloat() )
|
||
|
{
|
||
|
RemoveAllObjects( false, iClass );
|
||
|
}
|
||
|
|
||
|
ClearPlayerClass();
|
||
|
}
|
||
|
|
||
|
// can't change class if we have no team
|
||
|
if ( !IsInAnyTeam() )
|
||
|
return;
|
||
|
|
||
|
// Make sure client .dll can find out about it.
|
||
|
SetPlayerClass( iClass );
|
||
|
|
||
|
// Clear out current vote....
|
||
|
CTFTeam *pTFTeam = GetTFTeam();
|
||
|
SetPreferredTechnology( pTFTeam->m_pTechnologyTree, -1 );
|
||
|
|
||
|
// Force a respawn if they're alive
|
||
|
if ( IsAlive() )
|
||
|
{
|
||
|
ForceRespawn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Reset player class
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ClearPlayerClass( void )
|
||
|
{
|
||
|
// Remove all weapons & items
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
RemoveAllItems( false );
|
||
|
m_hWeaponCombatShield = NULL;
|
||
|
}
|
||
|
|
||
|
m_iPowerups = 0;
|
||
|
SetPlayerClass( TFCLASS_UNDECIDED );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set the player's model to the correct one, taking into account
|
||
|
// class, gender, team, and disguise.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetPlayerModel( void )
|
||
|
{
|
||
|
if (!GetPlayerClass())
|
||
|
{
|
||
|
SetHidden( true );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
string_t sModel = GetPlayerClass()->GetClassModel( GetTeamNumber() );
|
||
|
|
||
|
// If they don't have a model, make the player invisible
|
||
|
if ( !sModel )
|
||
|
{
|
||
|
SetHidden( true );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Make the player visible
|
||
|
SetHidden( false );
|
||
|
|
||
|
// Set the model
|
||
|
SetModel( STRING( sModel ) );
|
||
|
|
||
|
if ( GetFlags() & FL_DUCKING )
|
||
|
UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
|
||
|
else
|
||
|
UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PlayerRespawn( void )
|
||
|
{
|
||
|
m_nButtons = 0;
|
||
|
m_iRespawnFrames = 0;
|
||
|
|
||
|
// don't copy a corpse if we're in deathcam.
|
||
|
respawn( this, !IsObserver() );
|
||
|
SetThink( NULL );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Play a sound when we die
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::DeathSound( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
if ( GetTeamNumber() == TEAM_HUMANS )
|
||
|
{
|
||
|
EmitSound( "Humans.Death" );
|
||
|
}
|
||
|
else if ( GetTeamNumber() == TEAM_ALIENS )
|
||
|
{
|
||
|
switch( PlayerClass() )
|
||
|
{
|
||
|
case TFCLASS_COMMANDO:
|
||
|
EmitSound( "AlienCommando.Death" );
|
||
|
break;
|
||
|
|
||
|
case TFCLASS_MEDIC:
|
||
|
EmitSound( "AlienMedic.Death" );
|
||
|
break;
|
||
|
|
||
|
case TFCLASS_DEFENDER:
|
||
|
EmitSound( "AlienDefender.Death" );
|
||
|
break;
|
||
|
|
||
|
case TFCLASS_ESCORT:
|
||
|
EmitSound( "AlienEscort.Death" );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ItemPostFrame()
|
||
|
{
|
||
|
// Don't process items while in a vehicle.
|
||
|
if ( IsInAVehicle() )
|
||
|
{
|
||
|
IServerVehicle *pVehicle = GetVehicle();
|
||
|
Assert( pVehicle );
|
||
|
|
||
|
// NOTE: We *have* to do this before ItemPostFrame because ItemPostFrame
|
||
|
// may dump us out of the vehicle
|
||
|
int nRole = pVehicle->GetPassengerRole( this );
|
||
|
bool bUsingStandardWeapons = pVehicle->IsPassengerUsingStandardWeapons( nRole );
|
||
|
|
||
|
pVehicle->ItemPostFrame( this );
|
||
|
|
||
|
// Fall through and check weapons, etc. if we're using them
|
||
|
if (!bUsingStandardWeapons || !IsInAVehicle())
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If we're attaching a sapper, handle player use only
|
||
|
if ( m_TFLocal.m_bAttachingSapper )
|
||
|
{
|
||
|
PlayerUse();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BaseClass::ItemPostFrame();
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ItemPostFrame(); // Let the player class handle it.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::Jump( void )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PreThink(void)
|
||
|
{
|
||
|
CheckBuffs();
|
||
|
|
||
|
// Riding a vehicle?
|
||
|
if ( IsInAVehicle() )
|
||
|
{
|
||
|
BaseClass::PreThink();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CheckDeployFinish();
|
||
|
CheckKnockdown();
|
||
|
CheckCamouflage();
|
||
|
CheckSapperAttaching();
|
||
|
|
||
|
// Update reinforcement state
|
||
|
if (m_lifeState >= LIFE_DYING)
|
||
|
{
|
||
|
// After 3 seconds, move them to the Tactical Map
|
||
|
if ( (gpGlobals->curtime - m_flTimeOfDeath) > 3.0 )
|
||
|
{
|
||
|
if ( m_TFLocal.m_nInTacticalView == false )
|
||
|
{
|
||
|
ShowTacticalView( 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ROBIN: Maps will define whether or not teams reinforce
|
||
|
/*
|
||
|
// Aliens respawn in waves
|
||
|
if ( GetTeamNumber() == TEAM_ALIENS )
|
||
|
{
|
||
|
int iSecondsToGo = (int)(g_flNextReinforcementTime - gpGlobals->curtime);
|
||
|
if ( iSecondsToGo != m_iLastSecondsToGo && iSecondsToGo >= 1 )
|
||
|
{
|
||
|
m_iLastSecondsToGo = iSecondsToGo;
|
||
|
ClientPrint( this, HUD_PRINTCENTER, UTIL_VarArgs("\nReinforcing in %d %s\n", iSecondsToGo, iSecondsToGo > 1 ? "seconds" : "second" ) );
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
TFPlayerDeathThink();
|
||
|
}
|
||
|
|
||
|
// Update zone state
|
||
|
if ( m_pCurrentZone )
|
||
|
{
|
||
|
m_iCurrentZoneState = m_pCurrentZone->GetControllingTeam();
|
||
|
if ( m_iCurrentZoneState != ZONE_CONTESTED )
|
||
|
{
|
||
|
// Set the Zone state to the correct one
|
||
|
if ( m_iCurrentZoneState == GetTeamNumber() )
|
||
|
m_iCurrentZoneState = ZONE_FRIENDLY;
|
||
|
else
|
||
|
m_iCurrentZoneState = ZONE_ENEMY;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_iCurrentZoneState = 0;
|
||
|
}
|
||
|
|
||
|
BaseClass::PreThink();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PostThink()
|
||
|
{
|
||
|
BaseClass::PostThink();
|
||
|
|
||
|
// Make sure we have a valid MCV id.
|
||
|
CVehicleTeleportStation *pMCV = GetSelectedMCV();
|
||
|
if ( !pMCV || !pMCV->IsDeployed() )
|
||
|
{
|
||
|
m_hSelectedMCV = CVehicleTeleportStation::GetFirstDeployedMCV( GetTeamNumber() );
|
||
|
}
|
||
|
|
||
|
// Tell the client if our damage is boosted so it can do a smurfy effect on the weapon.
|
||
|
if ( GetAttackDamageScale( NULL ) == 1 )
|
||
|
m_TFPlayerFlags &= ~TF_PLAYER_DAMAGE_BOOST;
|
||
|
else
|
||
|
m_TFPlayerFlags |= TF_PLAYER_DAMAGE_BOOST;
|
||
|
|
||
|
m_PlayerAnimState.Update();
|
||
|
// SetLocalAngles( m_PlayerAnimState.GetRenderAngles() );
|
||
|
|
||
|
float flTimeSinceAttacked = gpGlobals->curtime - LastTimeDamagedByEnemy();
|
||
|
m_bUnderAttack = ((flTimeSinceAttacked >= 0.0f) && (flTimeSinceAttacked < 1.0f));
|
||
|
|
||
|
// TODO: This collision hull is set in the base class PostThink (so this is
|
||
|
// redundant), but I don't wanna re-write the whole thing at this point.
|
||
|
// We will just have to deal with a little redundancy for now.
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->SetPlayerHull();
|
||
|
}
|
||
|
|
||
|
// Menus
|
||
|
MenuDisplay();
|
||
|
|
||
|
// Player class Think
|
||
|
if (GetPlayerClass())
|
||
|
{
|
||
|
GetPlayerClass()->ClassThink();
|
||
|
}
|
||
|
|
||
|
if ( m_bSwitchingView )
|
||
|
{
|
||
|
m_bSwitchingView = false;
|
||
|
SetMoveType( m_TFLocal.m_nInTacticalView ? MOVETYPE_ISOMETRIC : MOVETYPE_WALK );
|
||
|
}
|
||
|
|
||
|
FollowClientRagdoll();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: selects a valid point that the player can spawn at
|
||
|
// Output : edict_t - the point in the world to spawn at
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseEntity *CBaseTFPlayer::EntSelectSpawnPoint( void )
|
||
|
{
|
||
|
// If we're in a team, ask the team for a spawnpoint
|
||
|
if ( GetTeam() )
|
||
|
{
|
||
|
CBaseEntity *entity = NULL;
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
// Let individual player classes override the respawn point
|
||
|
entity = GetPlayerClass()->SelectSpawnPoint();
|
||
|
if ( entity )
|
||
|
{
|
||
|
return entity;
|
||
|
}
|
||
|
|
||
|
// Do we have a selected spawn point (from a respawn station)?
|
||
|
entity = m_hSpawnPoint;
|
||
|
if (entity && (entity->GetTeam() == GetTeam()))
|
||
|
{
|
||
|
PlayRespawnEffect( entity );
|
||
|
return entity;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
entity = GetTeam()->SpawnPlayer( this );
|
||
|
if ( entity )
|
||
|
return entity;
|
||
|
}
|
||
|
|
||
|
// If we're not in a team, or the team didn't have a spawnpoint for us,
|
||
|
// fall back to the basic spawnpoint code.
|
||
|
return BaseClass::EntSelectSpawnPoint();
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::RemoveShieldOverlays( void )
|
||
|
{
|
||
|
RemoveGesture( ACT_OVERLAY_SHIELD_UP );
|
||
|
RemoveGesture( ACT_OVERLAY_SHIELD_DOWN );
|
||
|
RemoveGesture( ACT_OVERLAY_SHIELD_UP_IDLE );
|
||
|
RemoveGesture( ACT_OVERLAY_SHIELD_ATTACK );
|
||
|
RemoveGesture( ACT_OVERLAY_SHIELD_KNOCKBACK );
|
||
|
}
|
||
|
|
||
|
static bool IsShieldOverlay( Activity activity )
|
||
|
{
|
||
|
switch ( activity )
|
||
|
{
|
||
|
default:
|
||
|
return false;
|
||
|
case ACT_OVERLAY_SHIELD_UP:
|
||
|
case ACT_OVERLAY_SHIELD_DOWN:
|
||
|
case ACT_OVERLAY_SHIELD_UP_IDLE:
|
||
|
case ACT_OVERLAY_SHIELD_ATTACK:
|
||
|
case ACT_OVERLAY_SHIELD_KNOCKBACK:
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int CBaseTFPlayer::RemoveShieldOverlaysExcept( Activity activity, bool addifnotpresent /*= true */ )
|
||
|
{
|
||
|
int skip = FindGestureLayer( activity );
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < CBaseAnimatingOverlay::MAX_OVERLAYS; i++ )
|
||
|
{
|
||
|
if ( i == skip )
|
||
|
continue;
|
||
|
|
||
|
if ( IsShieldOverlay( GetLayerActivity( i ) ) )
|
||
|
{
|
||
|
RemoveLayer( i, 0.0, 0.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add it in if it's not present already
|
||
|
if ( addifnotpresent && ( skip == -1 ) )
|
||
|
{
|
||
|
return AddGesture( activity );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return skip;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : activity - i/o : can be changed to a new activity
|
||
|
// overlayindex - o: if an overlay is picked, this gets changed
|
||
|
// Rest of paramters are input only
|
||
|
// moving - is player moving
|
||
|
// ducked - is player ducking
|
||
|
// overlay - animation choices for this state (either full body crouch/stand, or overlay on top of base crouch/stand )
|
||
|
// crouch -
|
||
|
// normal -
|
||
|
// Overlay parameters
|
||
|
// autokill - if false, overlay will loop indefinitely
|
||
|
// blendin - amount of time over which to blend in (0.0f for snap)
|
||
|
// blendout - same but for blending out instead
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PickShieldAnimation( Activity& activity, int& overlayindex, bool moving, bool ducked,
|
||
|
Activity overlay, Activity crouch, Activity normal,
|
||
|
bool autokill /*=true*/, float blendin /*=0.0f*/, float blendout /*=0.0f*/ )
|
||
|
{
|
||
|
if ( moving )
|
||
|
{
|
||
|
overlayindex = RemoveShieldOverlaysExcept( overlay );
|
||
|
if ( overlayindex != -1 )
|
||
|
{
|
||
|
if ( blendin > 0.0f )
|
||
|
{
|
||
|
SetLayerBlendIn( overlayindex, blendin );
|
||
|
}
|
||
|
|
||
|
if ( blendout > 0.0f )
|
||
|
{
|
||
|
SetLayerBlendOut( overlayindex, blendout );
|
||
|
}
|
||
|
|
||
|
if ( !autokill )
|
||
|
{
|
||
|
SetLayerAutokill( overlayindex, false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
activity = ducked ? crouch : normal;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Activity CBaseTFPlayer::ShieldTranslateActivity( Activity activity )
|
||
|
{
|
||
|
CWeaponTwoHandedContainer *container = dynamic_cast< CWeaponTwoHandedContainer * >( GetActiveWeapon() );
|
||
|
if ( !container )
|
||
|
return activity;
|
||
|
|
||
|
CWeaponCombatShield *pShield = dynamic_cast< CWeaponCombatShield * >( container->GetLeftWeapon() );
|
||
|
if ( !pShield )
|
||
|
{
|
||
|
pShield = dynamic_cast< CWeaponCombatShield * >( container->GetRightWeapon() );
|
||
|
if ( !pShield )
|
||
|
{
|
||
|
return activity;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float speed = GetAbsVelocity().Length2D();
|
||
|
bool isMoving = speed != 0 ? true : false;
|
||
|
//bool isRunning = speed > 75 ? true : false;
|
||
|
bool isDucked = ( GetFlags() & FL_DUCKING ) ? true : false;
|
||
|
|
||
|
int shieldState = pShield->GetShieldState();
|
||
|
|
||
|
float startframe = 0.0f;
|
||
|
|
||
|
bool movechanged = isMoving ^ m_bWasMoving;
|
||
|
if ( movechanged)
|
||
|
{
|
||
|
// Grab frame from overlay
|
||
|
if ( !isMoving )
|
||
|
{
|
||
|
for ( int i = 0; i < MAX_OVERLAYS; i++ )
|
||
|
{
|
||
|
if ( IsShieldOverlay( GetLayerActivity( i ) ) )
|
||
|
{
|
||
|
startframe = GetLayerCycle( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemoveShieldOverlays();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch ( GetActivity() )
|
||
|
{
|
||
|
case ACT_SHIELD_UP:
|
||
|
case ACT_SHIELD_DOWN:
|
||
|
case ACT_SHIELD_UP_IDLE:
|
||
|
case ACT_SHIELD_ATTACK:
|
||
|
//case ACT_SHIELD_KNOCKBACK:
|
||
|
case ACT_CROUCHING_SHIELD_UP:
|
||
|
case ACT_CROUCHING_SHIELD_DOWN:
|
||
|
case ACT_CROUCHING_SHIELD_UP_IDLE:
|
||
|
case ACT_CROUCHING_SHIELD_ATTACK:
|
||
|
//case ACT_CROUCHING_SHIELD_KNOCKBACK:
|
||
|
startframe = GetCycle();
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Asume we should fix up animation based on move/stationary state change
|
||
|
bool fixup = true;
|
||
|
// Assume no overlay
|
||
|
int idx = -1;
|
||
|
|
||
|
switch ( shieldState )
|
||
|
{
|
||
|
default:
|
||
|
case SS_DOWN:
|
||
|
case SS_UNAVAILABLE:
|
||
|
RemoveShieldOverlays();
|
||
|
// By default, remove shield overlays and don't do fixup
|
||
|
fixup = false;
|
||
|
break;
|
||
|
case SS_LOWERING:
|
||
|
{
|
||
|
PickShieldAnimation( activity, idx, isMoving, isDucked,
|
||
|
ACT_OVERLAY_SHIELD_DOWN, ACT_CROUCHING_SHIELD_DOWN, ACT_SHIELD_DOWN,
|
||
|
true, 0.0f, 0.2f );
|
||
|
}
|
||
|
break;
|
||
|
case SS_RAISING:
|
||
|
{
|
||
|
PickShieldAnimation( activity, idx, isMoving, isDucked,
|
||
|
ACT_OVERLAY_SHIELD_UP, ACT_CROUCHING_SHIELD_UP, ACT_SHIELD_UP,
|
||
|
true, 0.2f, 0.0f );
|
||
|
}
|
||
|
break;
|
||
|
case SS_UP:
|
||
|
{
|
||
|
PickShieldAnimation( activity, idx, isMoving, isDucked,
|
||
|
ACT_OVERLAY_SHIELD_UP_IDLE, ACT_CROUCHING_SHIELD_UP_IDLE, ACT_SHIELD_UP_IDLE,
|
||
|
false );
|
||
|
}
|
||
|
break;
|
||
|
case SS_PARRYING:
|
||
|
{
|
||
|
PickShieldAnimation( activity, idx, isMoving, isDucked,
|
||
|
ACT_OVERLAY_SHIELD_ATTACK, ACT_CROUCHING_SHIELD_ATTACK, ACT_SHIELD_ATTACK,
|
||
|
true, 0.1f, 0.1f );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// If started or stopped moving and still using shield, match the cycle to/from the overlay/base animation
|
||
|
// being used beforehand
|
||
|
if ( movechanged && fixup )
|
||
|
{
|
||
|
// Fixup overlay frame
|
||
|
if ( idx != -1 )
|
||
|
{
|
||
|
SetLayerCycle( idx, startframe );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Force animation blend
|
||
|
ResetSequenceInfo();
|
||
|
|
||
|
// Match start frame
|
||
|
SetCycle( startframe );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remember previous state
|
||
|
m_bWasMoving = isMoving;
|
||
|
|
||
|
// Return translated activity
|
||
|
return activity;
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::StoreCycle( void )
|
||
|
{
|
||
|
m_flStoredCycle = GetCycle(); // !!!!!
|
||
|
}
|
||
|
|
||
|
float CBaseTFPlayer::RetrieveCycle( void )
|
||
|
{
|
||
|
return m_flStoredCycle;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Certain activities have matched cycles
|
||
|
// Input : newActivity -
|
||
|
// currentActivity -
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::ShouldMatchCycles( Activity newActivity, Activity currentActivity )
|
||
|
{
|
||
|
if ( ( newActivity == ACT_WALK || newActivity == ACT_RUN ) &&
|
||
|
( currentActivity == ACT_WALK || currentActivity == ACT_RUN ) )
|
||
|
{
|
||
|
// Don't blend either
|
||
|
IncrementInterpolationFrame();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#define ARBITRARY_RUN_SPEED 75.0f
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set the activity based on an event or current state
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetAnimation( PLAYER_ANIM playerAnim )
|
||
|
{
|
||
|
// Assume no change
|
||
|
Activity idealActivity = GetActivity();
|
||
|
int animDesired = GetSequence();
|
||
|
|
||
|
float speed = GetAbsVelocity().Length2D();
|
||
|
|
||
|
bool isMoving = ( speed != 0.0f ) ? true : false;
|
||
|
bool isRunning = false;
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
// FIXME: TF2 makes no distinction between walking and running for now,
|
||
|
// use the run animation always
|
||
|
if ( speed > 10.0f )
|
||
|
{
|
||
|
isRunning = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( speed > ARBITRARY_RUN_SPEED )
|
||
|
{
|
||
|
isRunning = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool isDucked = ( GetFlags() & FL_DUCKING ) ? true : false;
|
||
|
bool isStillJumping = !( GetFlags() & FL_ONGROUND ) && ( GetActivity() == ACT_HOP );
|
||
|
|
||
|
StoreCycle();
|
||
|
|
||
|
// Decide upon an animation activity based upon the desired Player animation
|
||
|
switch ( playerAnim )
|
||
|
{
|
||
|
default:
|
||
|
case PLAYER_RELOAD:
|
||
|
case PLAYER_ATTACK1:
|
||
|
case PLAYER_IDLE:
|
||
|
case PLAYER_WALK:
|
||
|
// Are we still jumping?
|
||
|
// If so, keep playing the jump animation.
|
||
|
if ( !isStillJumping )
|
||
|
{
|
||
|
idealActivity = ACT_WALK;
|
||
|
|
||
|
if ( isDucked )
|
||
|
{
|
||
|
idealActivity = !isMoving ? ACT_CROUCHIDLE : ACT_CROUCH;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
if ( isRunning )
|
||
|
{
|
||
|
idealActivity = ACT_RUN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
idealActivity = isMoving ? ACT_WALK : ACT_IDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allow shield to override
|
||
|
idealActivity = ShieldTranslateActivity( idealActivity );
|
||
|
// Allow body yaw to override for standing and turning in place
|
||
|
idealActivity = m_PlayerAnimState.BodyYawTranslateActivity( idealActivity );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case PLAYER_IN_VEHICLE:
|
||
|
// For now, use manned gun pose for all vehicles
|
||
|
idealActivity = ACT_RIDE_MANNED_GUN;
|
||
|
break;
|
||
|
|
||
|
case PLAYER_JUMP:
|
||
|
idealActivity = ACT_HOP;
|
||
|
break;
|
||
|
|
||
|
case PLAYER_DIE:
|
||
|
// Uses Ragdoll now???
|
||
|
idealActivity = ACT_DIESIMPLE;
|
||
|
break;
|
||
|
|
||
|
// FIXME: Use overlays for reload, start/leave aiming, attacking
|
||
|
case PLAYER_START_AIMING:
|
||
|
case PLAYER_LEAVE_AIMING:
|
||
|
idealActivity = ACT_WALK;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// No change requested?
|
||
|
if ( ( GetActivity() == idealActivity ) && ( GetSequence() != -1 ) )
|
||
|
return;
|
||
|
|
||
|
bool useStoredCycle = ShouldMatchCycles( idealActivity, GetActivity() );
|
||
|
|
||
|
animDesired = SelectWeightedSequence( idealActivity );
|
||
|
|
||
|
SetActivity( idealActivity );
|
||
|
|
||
|
// Already using the desired animation?
|
||
|
if ( GetSequence() == animDesired )
|
||
|
return;
|
||
|
|
||
|
ResetSequence( animDesired );
|
||
|
|
||
|
// Reset to first frame of desired animation or match previous animation if activities are
|
||
|
// meant to synchronize
|
||
|
SetCycle( useStoredCycle ? RetrieveCycle() : 0 );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CheatImpulseCommands( int iImpulse )
|
||
|
{
|
||
|
switch(iImpulse)
|
||
|
{
|
||
|
case 101:
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ResupplyAmmo( 1.0f, RESUPPLY_ALL_FROM_STATION );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 150:
|
||
|
if ( GetTFTeam() )
|
||
|
GetTFTeam()->PostMessage( TEAMMSG_REINFORCEMENTS_ARRIVED );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
BaseClass::CheatImpulseCommands(iImpulse);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetRespawnStation( CBaseEntity* pRespawnStation )
|
||
|
{
|
||
|
// This can happen because the object may get killed and its index reused
|
||
|
// between time the message was sent and the
|
||
|
if( !pRespawnStation || !FClassnameIs( pRespawnStation, "obj_respawn_station" ) )
|
||
|
return;
|
||
|
|
||
|
// Team could have changed (stolen object)
|
||
|
if ( GetTeam() != pRespawnStation->GetTeam() )
|
||
|
return;
|
||
|
|
||
|
// If the respawn station is the same one, then unselect!
|
||
|
if ( pRespawnStation != m_hSpawnPoint )
|
||
|
{
|
||
|
// Make sure the respawn station is a respawn station; it could be some
|
||
|
m_hSpawnPoint = pRespawnStation;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hSpawnPoint = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Find a starting respawn station
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseEntity *CBaseTFPlayer::GetInitialSpawnPoint( void )
|
||
|
{
|
||
|
if ( !GetTFTeam() )
|
||
|
return NULL;
|
||
|
|
||
|
CBaseEntity *pFirstStation = NULL;
|
||
|
|
||
|
// Cycle through all the respawn stations on my team
|
||
|
for ( int i = 0; i < GetTFTeam()->GetNumObjects(); i++ )
|
||
|
{
|
||
|
CBaseObject *pObject = GetTFTeam()->GetObject(i);
|
||
|
if ( pObject->GetType() == OBJ_RESPAWN_STATION )
|
||
|
{
|
||
|
// Store off the first station we find
|
||
|
if ( !pFirstStation )
|
||
|
{
|
||
|
pFirstStation = pObject;
|
||
|
}
|
||
|
|
||
|
// Map specified initial spawnpoint?
|
||
|
if ( ((CObjectRespawnStation*)pObject)->IsInitialSpawnPoint() )
|
||
|
return pObject;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pFirstStation;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull );
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::ClientCommand( const CCommand &args )
|
||
|
{
|
||
|
if( HasClass() )
|
||
|
{
|
||
|
if ( GetPlayerClass()->ClientCommand( args ) )
|
||
|
return true;
|
||
|
|
||
|
const char *cmd = args[0];
|
||
|
if ( FStrEq( cmd, "emp" ) )
|
||
|
{
|
||
|
Msg( "Self-inflicted EMP: testing\n" );
|
||
|
float flTime = 10;
|
||
|
if ( args.ArgC() == 2 )
|
||
|
{
|
||
|
flTime = atof( args[ 1 ] );
|
||
|
}
|
||
|
|
||
|
AttemptToPowerup( POWERUP_EMP, flTime );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( FStrEq( cmd, "emp_target" ) )
|
||
|
{
|
||
|
CBaseEntity *pEntity = FindEntityForward( this, true );
|
||
|
if ( pEntity && pEntity->CanBePoweredUp() )
|
||
|
{
|
||
|
float flTime = 10;
|
||
|
if ( args.ArgC() == 2 )
|
||
|
{
|
||
|
flTime = atof( args[ 1 ] );
|
||
|
}
|
||
|
pEntity->AttemptToPowerup( POWERUP_EMP, flTime );
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( FStrEq( cmd, "dmg_target" ) )
|
||
|
{
|
||
|
CBaseEntity *pEntity = FindEntityForward( this, true );
|
||
|
if ( pEntity && pEntity->m_takedamage )
|
||
|
{
|
||
|
float flDamage = 1;
|
||
|
if ( args.ArgC() == 2 )
|
||
|
{
|
||
|
flDamage = atof( args[ 1 ] );
|
||
|
}
|
||
|
CBaseEntity *world = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) );
|
||
|
if ( world )
|
||
|
{
|
||
|
pEntity->OnTakeDamage( CTakeDamageInfo( world, world, flDamage, DMG_GENERIC ) );
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !stricmp( cmd, "kd" ) )
|
||
|
{
|
||
|
Vector force( 0, 0, 0 );
|
||
|
if ( args.ArgC() == 1 )
|
||
|
{
|
||
|
force.x = random->RandomFloat( 0.5, 1.0 );
|
||
|
force.y = random->RandomFloat( 0.5, 1.0 );
|
||
|
|
||
|
if ( random->RandomFloat( 0, 1 ) > 0.5 )
|
||
|
{
|
||
|
force.x *= -1.0f;
|
||
|
}
|
||
|
if ( random->RandomFloat( 0, 1 ) > 0.5 )
|
||
|
{
|
||
|
force.y *= -1.0f;
|
||
|
}
|
||
|
force.z = random->RandomFloat( 0.5, 1.0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Vector fwd;
|
||
|
Vector right;
|
||
|
AngleVectors( GetAbsAngles(), &fwd, &right, NULL );
|
||
|
|
||
|
if ( !stricmp( args[ 1 ], "f" ) )
|
||
|
{
|
||
|
force = fwd * -1.0f;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "b" ) )
|
||
|
{
|
||
|
force = fwd;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "r" ) )
|
||
|
{
|
||
|
force = right * -1.0f;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "l" ) )
|
||
|
{
|
||
|
force = right;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "fr" ) )
|
||
|
{
|
||
|
force = fwd * -1.0f;
|
||
|
force += right * -1.0f;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "br" ) )
|
||
|
{
|
||
|
force = fwd;
|
||
|
force += right * -1.0f;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "fl" ) )
|
||
|
{
|
||
|
force = fwd * -1.0f;
|
||
|
force += right;
|
||
|
}
|
||
|
else if ( !stricmp( args[ 1 ], "bl" ) )
|
||
|
{
|
||
|
force = fwd;
|
||
|
force += right;
|
||
|
}
|
||
|
|
||
|
force.z = 0.8f;
|
||
|
VectorNormalize( force );
|
||
|
}
|
||
|
|
||
|
KnockDownPlayer( force, 500.0f, 3.0f );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( FStrEq( cmd, "veryweak" ) )
|
||
|
{
|
||
|
int ouch = m_iHealth - 1;
|
||
|
|
||
|
CBaseEntity *world = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) );
|
||
|
if ( world )
|
||
|
{
|
||
|
OnTakeDamage( CTakeDamageInfo( world, world, (float)ouch, DMG_GENERIC ) );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( FStrEq( cmd, "ragdoll" ) )
|
||
|
{
|
||
|
bool on = true;
|
||
|
|
||
|
if ( args.ArgC() >= 2 )
|
||
|
{
|
||
|
on = atoi( args[ 1 ] ) ? true : false;
|
||
|
}
|
||
|
|
||
|
if ( on )
|
||
|
{
|
||
|
Vector force = RandomVector( -500, 500 );
|
||
|
force.z = fabs( force.z );
|
||
|
force.z = MIN( 200.0f, force.z );
|
||
|
|
||
|
BecomeRagdollOnClient( force );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ClearClientRagdoll( true );
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( FStrEq( cmd, "hbset" ) )
|
||
|
{
|
||
|
if ( args.ArgC() >= 2 )
|
||
|
{
|
||
|
SetHitboxSet( atoi( args[ 1 ] ) );
|
||
|
Msg( "Hitboxset forced to %i %s\n", GetHitboxSet(), GetHitboxSetName() );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return BaseClass::ClientCommand( args );
|
||
|
}
|
||
|
|
||
|
|
||
|
//=========================================================
|
||
|
// Purpose: Override base TraceAttack
|
||
|
//=========================================================
|
||
|
void CBaseTFPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
|
||
|
{
|
||
|
if ( m_takedamage )
|
||
|
{
|
||
|
// Prevent team damage here so blood doesn't appear
|
||
|
if ( info.GetAttacker() )
|
||
|
{
|
||
|
// Take damage from myself
|
||
|
if ( InSameTeam( info.GetAttacker() ) && info.GetAttacker() != this )
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If we hit our shield, ignore the damage
|
||
|
float flDamage = info.GetDamage();
|
||
|
if ( IsHittingShield( vecDir, &flDamage ))
|
||
|
return;
|
||
|
|
||
|
// Shield may have blocked some
|
||
|
CTakeDamageInfo subInfo = info;
|
||
|
subInfo.SetDamage( flDamage );
|
||
|
|
||
|
SetLastHitGroup( ptr->hitgroup );
|
||
|
|
||
|
// Hit groups aren't evaluated here, like base TraceAttack.
|
||
|
// Weapons factor hit location into flDamage before it gets here
|
||
|
/* //SpawnBlood( ptr->endpos - (vecDir * 5), BloodColor(), subInfo.GetDamage() );
|
||
|
//TraceBleed( subInfo.GetDamage(), vecDir, ptr, subInfo.GetDamageType() );
|
||
|
|
||
|
// Show the personal shield effect.
|
||
|
// What we do here is collide the trace line with an ellipse that is slightly larger
|
||
|
// than the player and put the effect there.
|
||
|
|
||
|
// Translate the line so the player's (and the ellipse's) center is at the origin.
|
||
|
Vector vCenter = Center();
|
||
|
Vector vStart = ptr->startpos - vCenter;
|
||
|
Vector vEnd = ptr->endpos - vCenter;
|
||
|
|
||
|
// Figure out the ellipse dimensions.
|
||
|
Vector vDims = (WorldAlignMaxs() - WorldAlignMins()) * 0.5f;
|
||
|
Vector vEllipse = vDims * 1.5;
|
||
|
|
||
|
// Squash the line we're testing so we're testing against a sphere of radius 1 at the origin.
|
||
|
vStart /= vEllipse;
|
||
|
vEnd /= vEllipse;
|
||
|
|
||
|
// See where the line hits the sphere.
|
||
|
Vector vLineDir = vEnd - vStart;
|
||
|
float f1, f2;
|
||
|
if ( IntersectInfiniteRayWithSphere( vStart, vLineDir, vec3_origin, 1, &f1, &f2 ) )
|
||
|
{
|
||
|
// Use the closest hit point on the sphere.
|
||
|
float fMin = MIN( f1, f2 );
|
||
|
Vector vPos = vStart + vLineDir * fMin;
|
||
|
|
||
|
// Unsquash back to the ellipse's dimensions.
|
||
|
vPos *= vEllipse;
|
||
|
|
||
|
ShowPersonalShieldEffect( vPos, vecDir, subInfo.GetDamage() );
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
AddMultiDamage( subInfo, this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Applies a force on the player when he takes damage
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ApplyDamageForce( const CTakeDamageInfo &info, int nDamageToDo )
|
||
|
{
|
||
|
if (nDamageToDo <= 0)
|
||
|
return;
|
||
|
|
||
|
if ( (info.GetDamageType() & (DMG_ENERGYBEAM | DMG_BLAST)) == 0 )
|
||
|
return;
|
||
|
|
||
|
if ( !info.GetInflictor() || (info.GetInflictor() == this) || info.GetAttacker()->IsSolidFlagSet(FSOLID_TRIGGER) )
|
||
|
return;
|
||
|
|
||
|
// Don't blow ragdolls around
|
||
|
if ( IsClientRagdoll() )
|
||
|
return;
|
||
|
|
||
|
// Don't bother with crouched players, or classes that have other rules about it
|
||
|
if ( GetFlags() & FL_DUCKING )
|
||
|
return;
|
||
|
|
||
|
if (!GetPlayerClass() || !GetPlayerClass()->ShouldApplyDamageForce( info ))
|
||
|
return;
|
||
|
|
||
|
Vector vecDir;
|
||
|
// If the inflictor isn't moving, use the delta between it & me. If it's moving, use it's velocity.
|
||
|
Vector vecInflictorVelocity;
|
||
|
info.GetInflictor()->GetVelocity( &vecInflictorVelocity, NULL );
|
||
|
|
||
|
// Explosives never use the velocity of the inflictor
|
||
|
if ( !(info.GetDamageType() & DMG_BLAST) && vecInflictorVelocity != vec3_origin )
|
||
|
{
|
||
|
vecDir = vecInflictorVelocity;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vecDir = WorldSpaceCenter( );
|
||
|
vecDir -= info.GetInflictor()->WorldSpaceCenter( );
|
||
|
}
|
||
|
VectorNormalize( vecDir );
|
||
|
|
||
|
float flForce = (nDamageToDo * 2) + 20;
|
||
|
if (flForce > 1000.0)
|
||
|
flForce = 1000.0;
|
||
|
|
||
|
// Escorts get knocked half as far
|
||
|
if ( PlayerClass() == TFCLASS_ESCORT )
|
||
|
{
|
||
|
flForce *= 0.5;
|
||
|
}
|
||
|
|
||
|
vecDir *= flForce;
|
||
|
|
||
|
if ( (GetMoveType() != MOVETYPE_FLY) && (GetMoveType() != MOVETYPE_FLYGRAVITY) && ((GetFlags() & FL_ONGROUND) != 0) )
|
||
|
{
|
||
|
// Need large x-y component to overcome walking friction
|
||
|
vecDir.x *= 3;
|
||
|
vecDir.y *= 3;
|
||
|
}
|
||
|
|
||
|
Vector vecNewVelocity = GetAbsVelocity();
|
||
|
vecNewVelocity += vecDir;
|
||
|
|
||
|
Vector vecTestVel = vecNewVelocity;
|
||
|
float flLen = VectorNormalize( vecTestVel );
|
||
|
if (flLen > MAX_EXPLOSIVE_VELOCITY)
|
||
|
VectorMultiply( vecTestVel, MAX_EXPLOSIVE_VELOCITY, vecNewVelocity );
|
||
|
|
||
|
SetAbsVelocity( vecNewVelocity );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Deal damage to the player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::OnTakeDamage( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
if ( !IsAlive() )
|
||
|
return 0;
|
||
|
|
||
|
//if ( GetFlags() & FL_GODMODE )
|
||
|
//return 0;
|
||
|
|
||
|
// Generate a global order event.
|
||
|
COrderEvent_PlayerDamaged event;
|
||
|
event.m_pPlayerDamaged = this;
|
||
|
event.m_TakeDamageInfo = info;
|
||
|
GlobalOrderEvent( &event );
|
||
|
|
||
|
// Don't do damage if the player's in a vehicle, in a non-damagable spot.
|
||
|
if ( IsInAVehicle() && m_hVehicle.Get() )
|
||
|
{
|
||
|
IServerVehicle* pVehicle = m_hVehicle.Get()->GetServerVehicle();
|
||
|
Assert( pVehicle );
|
||
|
int nRole = pVehicle->GetPassengerRole(this);
|
||
|
|
||
|
if( ( nRole < 0 )
|
||
|
|| !pVehicle->IsPassengerVisible(nRole)
|
||
|
|| !pVehicle->IsPassengerDamagable(nRole) )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check teams
|
||
|
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)info.GetAttacker();
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
// Take damage from myself
|
||
|
if ( pPlayer != this )
|
||
|
{
|
||
|
if ( InSameTeam(pPlayer) )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Store off the last time we were damaged by an enemy so commandos can
|
||
|
// get orders to assist.
|
||
|
m_flLastTimeDamagedByEnemy = gpGlobals->curtime;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CTakeDamageInfo subInfo = info;
|
||
|
|
||
|
// Let the playerclass at it
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
subInfo.SetDamage( GetPlayerClass()->OnTakeDamage( subInfo ) );
|
||
|
}
|
||
|
|
||
|
if ( !subInfo.GetDamage() )
|
||
|
return 0;
|
||
|
|
||
|
//Msg( "Weapon did: %f\n", flDamage );
|
||
|
|
||
|
int iDamageToDo = Ceil2Int( subInfo.GetDamage() );
|
||
|
|
||
|
if ( !(GetFlags() & FL_GODMODE) )
|
||
|
{
|
||
|
// Only certain damage types knock players around
|
||
|
ApplyDamageForce( info, iDamageToDo );
|
||
|
|
||
|
m_iHealth = MAX(0, m_iHealth - iDamageToDo);
|
||
|
}
|
||
|
|
||
|
//Msg( "m_iHealth: %d\n\n", m_iHealth );
|
||
|
|
||
|
// Dead?
|
||
|
if ( m_iHealth < 1 )
|
||
|
{
|
||
|
Event_Killed( subInfo );
|
||
|
}
|
||
|
|
||
|
// Let the client know
|
||
|
// Try and figure out where the damage is coming from
|
||
|
Vector vecDamageOrigin = info.GetReportedPosition();
|
||
|
// If we didn't get an origin to use, try using the attacker's origin
|
||
|
if ( vecDamageOrigin == vec3_origin && info.GetAttacker() )
|
||
|
{
|
||
|
vecDamageOrigin = info.GetAttacker()->GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
CSingleUserRecipientFilter user( this );
|
||
|
UserMessageBegin( user, "Damage" );
|
||
|
WRITE_BYTE( clamp( iDamageToDo, 0, 255 ) );
|
||
|
WRITE_FLOAT( vecDamageOrigin.x ); // BUG: Should be fixed point (to hud) not floats
|
||
|
WRITE_FLOAT( vecDamageOrigin.y ); // BUG: However, the HUD does _not_ implement bitfield messages (yet)
|
||
|
WRITE_FLOAT( vecDamageOrigin.z ); // BUG: We use WRITE_VEC3COORD for everything else
|
||
|
MessageEnd();
|
||
|
|
||
|
// Do special explosion damage effect
|
||
|
if ( info.GetDamageType() & DMG_BLAST )
|
||
|
{
|
||
|
OnDamagedByExplosion( info );
|
||
|
}
|
||
|
|
||
|
return iDamageToDo;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CBaseTFPlayer::ShowPersonalShieldEffect(
|
||
|
const Vector &vOffsetFromEnt,
|
||
|
const Vector &vIncomingDirection,
|
||
|
float flDamage )
|
||
|
{
|
||
|
Vector vNormalized = vIncomingDirection;
|
||
|
VectorNormalize( vNormalized );
|
||
|
|
||
|
EntityMessageBegin( this );
|
||
|
WRITE_BYTE( PLAYER_MSG_PERSONAL_SHIELD );
|
||
|
WRITE_VEC3COORD( vOffsetFromEnt );
|
||
|
WRITE_VEC3NORMAL( vNormalized );
|
||
|
WRITE_SHORT( (short)flDamage );
|
||
|
MessageEnd();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Player is being healed
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::TakeHealth( float flHealth, int bitsDamageType )
|
||
|
{
|
||
|
if ( m_iHealth == m_iMaxHealth )
|
||
|
return 0;
|
||
|
|
||
|
// Heal the location
|
||
|
float flAmountToHeal = flHealth;
|
||
|
if ( flAmountToHeal > (m_iMaxHealth - m_iHealth) )
|
||
|
flAmountToHeal = (m_iMaxHealth - m_iHealth);
|
||
|
m_iHealth += flAmountToHeal;
|
||
|
|
||
|
//Msg( "Health: %d\n", m_iHealth );
|
||
|
|
||
|
return flAmountToHeal;
|
||
|
}
|
||
|
|
||
|
|
||
|
//=====================================================================
|
||
|
// MENU HANDLING
|
||
|
//=====================================================================
|
||
|
void CBaseTFPlayer::MenuDisplay( void )
|
||
|
{
|
||
|
if ( !m_pCurrentMenu )
|
||
|
{
|
||
|
m_MenuRefreshTime = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( m_MenuRefreshTime > gpGlobals->curtime )
|
||
|
{
|
||
|
// guard against sudden clock changes
|
||
|
m_MenuRefreshTime = MIN( m_MenuRefreshTime, gpGlobals->curtime + MENU_UPDATETIME );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_MenuRefreshTime = gpGlobals->curtime + MENU_UPDATETIME;
|
||
|
|
||
|
if ( m_pCurrentMenu )
|
||
|
m_pCurrentMenu->Display( this );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::MenuInput( int iInput )
|
||
|
{
|
||
|
if ( m_pCurrentMenu )
|
||
|
{
|
||
|
return m_pCurrentMenu->Input( this, iInput );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::MenuReset( void )
|
||
|
{
|
||
|
CSingleUserRecipientFilter user( this );
|
||
|
user.MakeReliable();
|
||
|
|
||
|
UserMessageBegin( user, "ShowMenu" );
|
||
|
WRITE_SHORT( 0 );
|
||
|
WRITE_CHAR( 0 ); // display time (-1 means unlimited)
|
||
|
WRITE_BYTE( false ); // is there more message to come? no
|
||
|
WRITE_STRING( "" );
|
||
|
MessageEnd();
|
||
|
|
||
|
Q_strncpy( m_MenuStringBuffer, "" , sizeof(m_MenuStringBuffer) );
|
||
|
m_MenuRefreshTime = m_MenuDisplayTime = 0;
|
||
|
|
||
|
m_pCurrentMenu = NULL;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Enables/disables tactical/map view for the player
|
||
|
// Input : bTactical - true == enable it
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ShowTacticalView( bool bTactical )
|
||
|
{
|
||
|
// TODO: Decide if we are going to keep the tactical view in TF2
|
||
|
if ( !inv_demo.GetBool() )
|
||
|
return;
|
||
|
|
||
|
m_bSwitchingView = true;
|
||
|
m_TFLocal.m_nInTacticalView = bTactical ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// returns true if we're in tactical view
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsInTacticalView( void ) const
|
||
|
{
|
||
|
return m_TFLocal.m_nInTacticalView;
|
||
|
}
|
||
|
|
||
|
int CBaseTFPlayer::UpdateTransmitState()
|
||
|
{
|
||
|
return SetTransmitState( FL_EDICT_FULLCHECK );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Note, an entity can override the send table ( e.g., to send less data or to send minimal data for
|
||
|
// objects ( prob. players ) that are not in the pvs.
|
||
|
// Input : **ppSendTable -
|
||
|
// *recipient -
|
||
|
// *pvs -
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
||
|
{
|
||
|
// Don't transmit if we have no team or class
|
||
|
if ((PlayerClass() == TFCLASS_UNDECIDED) || (GetTeamNumber() == 0))
|
||
|
return FL_EDICT_DONTSEND;
|
||
|
|
||
|
// Thermal vision in effect, if so, cull some players who are too far away
|
||
|
CBaseTFPlayer *pPlayer = ( ( CBaseTFPlayer * )CBaseEntity::Instance( pInfo->m_pClientEnt ) );
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
if ( pPlayer->IsUsingThermalVision() )
|
||
|
{
|
||
|
// Do a radius check, and force sending of guys nearby (so we can see them through walls)
|
||
|
Vector dist = GetAbsOrigin() - pPlayer->GetAbsOrigin();
|
||
|
if ( dist.Length() < THERMAL_VISION_RADIUS )
|
||
|
return FL_EDICT_ALWAYS;
|
||
|
}
|
||
|
|
||
|
// If the player we might see is camouflaged and not on our team, we can preclude based
|
||
|
// on distance
|
||
|
if ( IsCamouflaged() && !InSameTeam( pPlayer ) )
|
||
|
{
|
||
|
Vector dist = GetAbsOrigin() - pPlayer->GetAbsOrigin();
|
||
|
if ( dist.Length() > CAMO_OUTER_RADIUS )
|
||
|
{
|
||
|
return FL_EDICT_ALWAYS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Use default pvs etc. rules
|
||
|
return BaseClass::ShouldTransmit( pInfo );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTechnologyTree *CBaseTFPlayer::GetTechTree( void )
|
||
|
{
|
||
|
CTFTeam *pTeam = GetTFTeam();
|
||
|
if ( pTeam )
|
||
|
return pTeam->m_pTechnologyTree;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::PlayerClass( void )
|
||
|
{
|
||
|
return m_iPlayerClass;
|
||
|
}
|
||
|
|
||
|
CPlayerClass *CBaseTFPlayer::GetPlayerClass()
|
||
|
{
|
||
|
return m_PlayerClasses.GetPlayerClass( PlayerClass() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *name -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetPreferredTechnology( CTechnologyTree *pTechnologyTree, int iTechIndex )
|
||
|
{
|
||
|
Assert( pTechnologyTree );
|
||
|
|
||
|
// Have to be on a team to vote for tech
|
||
|
CTFTeam *pTeam = GetTFTeam();
|
||
|
if ( !pTeam )
|
||
|
return;
|
||
|
|
||
|
if ( iTechIndex == -1 )
|
||
|
{
|
||
|
m_nPreferredTechnology = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( iTechIndex < 0 || iTechIndex >= pTechnologyTree->GetNumberTechnologies() )
|
||
|
{
|
||
|
Msg( "%s tried to set voting preference to unknown technology index : %d\n",
|
||
|
GetPlayerName(), iTechIndex );
|
||
|
return;
|
||
|
}
|
||
|
CBaseTechnology *tech = pTechnologyTree->GetTechnology( iTechIndex );
|
||
|
if ( !tech )
|
||
|
return;
|
||
|
|
||
|
// Has the tech got incomplete dependancies?
|
||
|
if ( tech->HasInactiveDependencies() )
|
||
|
return;
|
||
|
// Already have it?
|
||
|
if ( tech->GetAvailable() )
|
||
|
return;
|
||
|
// Can't prefer a hidden tech
|
||
|
if ( tech->IsHidden() )
|
||
|
return;
|
||
|
|
||
|
m_nPreferredTechnology = iTechIndex;
|
||
|
}
|
||
|
|
||
|
pTeam->RecomputePreferences();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : int
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetPreferredTechnology( void )
|
||
|
{
|
||
|
return m_nPreferredTechnology;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *name -
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::HasNamedTechnology( const char *name )
|
||
|
{
|
||
|
if ( GetTFTeam() == NULL )
|
||
|
return false;
|
||
|
|
||
|
return GetTFTeam()->HasNamedTechnology( name );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Networking is about to update this player, let it override and specify it's own pvs
|
||
|
// Input : **pvs -
|
||
|
// **pas -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
|
||
|
{
|
||
|
// Normal PVS
|
||
|
BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
|
||
|
|
||
|
// PVS has an additional origin
|
||
|
if ( m_vecAdditionalPVSOrigin != vec3_origin )
|
||
|
{
|
||
|
// Add an additional origin to the pvs
|
||
|
engine->AddOriginToPVS( m_vecAdditionalPVSOrigin );
|
||
|
}
|
||
|
|
||
|
if ( m_vecCameraPVSOrigin != vec3_origin )
|
||
|
{
|
||
|
engine->AddOriginToPVS( m_vecCameraPVSOrigin );
|
||
|
}
|
||
|
|
||
|
// If in tactical mode, merge in pvs from all of our teammates, too
|
||
|
// send all the others team info
|
||
|
if ( m_TFLocal.m_nInTacticalView )
|
||
|
{
|
||
|
int i;
|
||
|
for ( i = 1; i <= gpGlobals->maxClients; i++ )
|
||
|
{
|
||
|
CBaseTFPlayer *plr = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
|
||
|
if ( plr &&
|
||
|
( plr != this ) &&
|
||
|
( plr->TeamID() == TeamID() ) )
|
||
|
{
|
||
|
Vector org;
|
||
|
org = plr->EyePosition();
|
||
|
|
||
|
engine->AddOriginToPVS( org );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
// ORDERS
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Assign the player to the specified order
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetOrder( COrder *pOrder )
|
||
|
{
|
||
|
if ( m_hSelectedOrder.Get() && m_hSelectedOrder != pOrder )
|
||
|
{
|
||
|
m_hSelectedOrder->SetOwner( NULL );
|
||
|
}
|
||
|
m_hSelectedOrder = pOrder;
|
||
|
}
|
||
|
|
||
|
|
||
|
int CBaseTFPlayer::GetNumResourceZoneOrders()
|
||
|
{
|
||
|
return GetTFTeam()->CountOrders( COUNTORDERS_TYPE | COUNTORDERS_OWNER, ORDER_ATTACK, 0, this ) +
|
||
|
GetTFTeam()->CountOrders( COUNTORDERS_TYPE | COUNTORDERS_OWNER, ORDER_DEFEND, 0, this ) +
|
||
|
GetTFTeam()->CountOrders( COUNTORDERS_TYPE | COUNTORDERS_OWNER, ORDER_CAPTURE, 0, this );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CBaseTFPlayer::KillResourceZoneOrders()
|
||
|
{
|
||
|
if( GetNumResourceZoneOrders() )
|
||
|
GetTFTeam()->RemoveOrdersToPlayer( this );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
// DEPLOYMENT
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::StartDeploying( void )
|
||
|
{
|
||
|
if ( !GetPlayerClass() )
|
||
|
return;
|
||
|
|
||
|
m_bDeploying = true;
|
||
|
m_vecDeployedAngles = GetLocalAngles();
|
||
|
|
||
|
// No pitch or roll, though
|
||
|
m_vecDeployedAngles.SetX( 0 );
|
||
|
m_vecDeployedAngles.SetZ( 0 );
|
||
|
|
||
|
SetCantMove( true );
|
||
|
m_flFinishedDeploying = gpGlobals->curtime + GetPlayerClass()->GetDeployTime();
|
||
|
|
||
|
SetAnimation( PLAYER_START_AIMING );
|
||
|
|
||
|
EmitSound( "BaseTFPlayer.StartDeploying" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::StartUnDeploying( void )
|
||
|
{
|
||
|
if ( !GetPlayerClass() )
|
||
|
return;
|
||
|
|
||
|
m_bUnDeploying = true;
|
||
|
SetCantMove( true );
|
||
|
m_flFinishedDeploying = gpGlobals->curtime + GetPlayerClass()->GetDeployTime();
|
||
|
SetAnimation( PLAYER_LEAVE_AIMING );
|
||
|
|
||
|
EmitSound( "BaseTFPlayer.StartUnDeploying" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CheckDeployFinish( void )
|
||
|
{
|
||
|
// Check to see if deployment has finished
|
||
|
if ( m_bDeploying )
|
||
|
{
|
||
|
if ( gpGlobals->curtime > m_flFinishedDeploying )
|
||
|
{
|
||
|
FinishDeploying();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Check to see if un-deployment has finished
|
||
|
if ( m_bUnDeploying )
|
||
|
{
|
||
|
if ( gpGlobals->curtime > m_flFinishedDeploying )
|
||
|
{
|
||
|
FinishUnDeploying();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Check to see if the player's trying to move while deployed
|
||
|
if ( IsAlive() && m_bDeployed )
|
||
|
{
|
||
|
if ( m_nButtons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) )
|
||
|
{
|
||
|
StartUnDeploying();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::FinishDeploying( void )
|
||
|
{
|
||
|
m_bDeploying = false;
|
||
|
m_bDeployed = true;
|
||
|
SetCantMove( true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::FinishUnDeploying( void )
|
||
|
{
|
||
|
m_bUnDeploying = false;
|
||
|
m_bDeployed = false;
|
||
|
SetCantMove( false );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsDeployed( void )
|
||
|
{
|
||
|
return m_bDeployed;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsDeploying( void )
|
||
|
{
|
||
|
return (m_bDeploying || m_bUnDeploying);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsUnDeploying( void )
|
||
|
{
|
||
|
return m_bUnDeploying;
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::OnVehicleStart()
|
||
|
{
|
||
|
// Do any class-specific stuff
|
||
|
if (GetPlayerClass())
|
||
|
{
|
||
|
GetPlayerClass()->OnVehicleStart();
|
||
|
}
|
||
|
|
||
|
IServerVehicle *pVehicle = GetVehicle();
|
||
|
CBaseCombatWeapon *weapon = GetActiveWeapon();
|
||
|
if ( pVehicle && weapon )
|
||
|
{
|
||
|
// Get Role for this player
|
||
|
int role = pVehicle->GetPassengerRole( this );
|
||
|
bool allowweapons = pVehicle->IsPassengerUsingStandardWeapons( role );
|
||
|
if ( !allowweapons )
|
||
|
{
|
||
|
weapon->Holster();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBaseTFPlayer::OnVehicleEnd( Vector &playerDestPosition )
|
||
|
{
|
||
|
// Do any class-specific stuff
|
||
|
if (GetPlayerClass())
|
||
|
{
|
||
|
GetPlayerClass()->OnVehicleEnd();
|
||
|
}
|
||
|
|
||
|
Vector vNewPos;
|
||
|
if ( !EntityPlacementTest( this, playerDestPosition, vNewPos, true) )
|
||
|
{
|
||
|
Warning("Can't find valid place to exit vehicle.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Move the player up a bit to be safe
|
||
|
playerDestPosition = vNewPos + Vector(0,0,16);
|
||
|
|
||
|
CBaseCombatWeapon *weapon = GetActiveWeapon();
|
||
|
if ( weapon )
|
||
|
{
|
||
|
weapon->Deploy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::CanGetInVehicle( void )
|
||
|
{
|
||
|
// Class-specific?
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
return GetPlayerClass()->CanGetInVehicle();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
CVehicleTeleportStation* CBaseTFPlayer::GetSelectedMCV() const
|
||
|
{
|
||
|
return dynamic_cast< CVehicleTeleportStation* >( m_hSelectedMCV.Get() );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CBaseTFPlayer::SetSelectedMCV( CVehicleTeleportStation *pMCV )
|
||
|
{
|
||
|
m_hSelectedMCV = pMCV;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Restore this player's ammo count to it's starting state
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::ResupplyAmmo( float flPercentage, ResupplyReason_t reason )
|
||
|
{
|
||
|
if ( !GetPlayerClass() )
|
||
|
return false;
|
||
|
|
||
|
return GetPlayerClass()->ResupplyAmmo(flPercentage, reason);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Medic has provided this player with a health boost
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::TakeHealthBoost( int iHealthBoost, int iTarget, int iDuration )
|
||
|
{
|
||
|
m_iHealth += iHealthBoost;
|
||
|
m_iHealthBoostTarget = iTarget;
|
||
|
m_flHealthBoostDecrement = ceil((m_iHealth - iTarget) / (float)iDuration);
|
||
|
|
||
|
// Start the health ticking down
|
||
|
m_flHealthBoostTime = gpGlobals->curtime + 1.0;
|
||
|
|
||
|
if ( iTarget >= m_iMaxHealth )
|
||
|
{
|
||
|
m_bBuffHealthBoost = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove health buffs
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RemoveHealthBoost( void )
|
||
|
{
|
||
|
m_bBuffHealthBoost = false;
|
||
|
m_iHealthBoostTarget = 0;
|
||
|
m_flHealthBoostTime = 0;
|
||
|
|
||
|
if ( m_iHealth > m_iMaxHealth )
|
||
|
{
|
||
|
m_iHealth = m_iMaxHealth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Check the state of all buffs on this player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CheckBuffs( void )
|
||
|
{
|
||
|
// Health boost?
|
||
|
if ( m_bBuffHealthBoost )
|
||
|
{
|
||
|
// Dropped below normal max health?
|
||
|
if ( m_iHealth <= m_iHealthBoostTarget )
|
||
|
{
|
||
|
RemoveHealthBoost();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( m_flHealthBoostTime < gpGlobals->curtime )
|
||
|
{
|
||
|
// Ticking down from a boost? or suffering poison damage?
|
||
|
if ( m_iHealth > m_iMaxHealth )
|
||
|
{
|
||
|
// Drop back to normal health in 20 seconds
|
||
|
m_iHealth -= m_flHealthBoostDecrement;
|
||
|
}
|
||
|
|
||
|
m_flHealthBoostTime = gpGlobals->curtime + 1.0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Powerup has just started
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
|
||
|
{
|
||
|
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
|
||
|
|
||
|
switch( iPowerup )
|
||
|
{
|
||
|
case POWERUP_BOOST:
|
||
|
{
|
||
|
m_hLastBoostEntity = pAttacker;
|
||
|
|
||
|
// Power up their shield
|
||
|
if ( GetCombatShield() )
|
||
|
{
|
||
|
GetCombatShield()->AddShieldHealth( 0.06 );
|
||
|
}
|
||
|
|
||
|
// Let their playerclass know
|
||
|
GetPlayerClass()->PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case POWERUP_EMP:
|
||
|
{
|
||
|
// Let the playerclass know about it
|
||
|
GetPlayerClass()->PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case POWERUP_RUSH:
|
||
|
{
|
||
|
// Speed up
|
||
|
// We need to set this here so RecalculateSpeed() can check HasPowerup(POWERUP_RUSH)
|
||
|
m_iPowerups |= (1 << iPowerup);
|
||
|
RecalculateSpeed();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
BaseClass::PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Powerup has just started
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::PowerupEnd( int iPowerup )
|
||
|
{
|
||
|
switch( iPowerup )
|
||
|
{
|
||
|
case POWERUP_EMP:
|
||
|
{
|
||
|
GetPlayerClass()->PowerupEnd(iPowerup);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case POWERUP_RUSH:
|
||
|
{
|
||
|
// Slow down
|
||
|
RecalculateSpeed();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
BaseClass::PowerupEnd( iPowerup );
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
// OBJECTS
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return true if this player's allowed to build another one of the specified object
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::CanBuild( int iObjectType )
|
||
|
{
|
||
|
if ( iObjectType < 0 || iObjectType >= OBJ_LAST )
|
||
|
return CB_NOT_RESEARCHED;
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
return GetPlayerClass()->CanBuild( iObjectType );
|
||
|
}
|
||
|
|
||
|
return CB_NOT_RESEARCHED;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Get the number of objects of the specified type that this player has
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetNumObjects( int iObjectType )
|
||
|
{
|
||
|
int iCount = 0;
|
||
|
for (int i = 0; i < GetObjectCount(); i++)
|
||
|
{
|
||
|
if ( !GetObject(i) )
|
||
|
continue;
|
||
|
|
||
|
if ( GetObject(i)->GetType() == iObjectType )
|
||
|
{
|
||
|
iCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return iCount;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetObjectCount( void )
|
||
|
{
|
||
|
return m_TFLocal.m_aObjects.Count();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseObject *CBaseTFPlayer::GetObject( int index )
|
||
|
{
|
||
|
return m_TFLocal.m_aObjects[index].Get();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Returns true if this player is building something
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsBuilding( void )
|
||
|
{
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
return pBuilder->IsBuilding();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Object built by this player has been destroyed
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::OwnedObjectDestroyed( CBaseObject *pObject )
|
||
|
{
|
||
|
TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::OwnedObjectDestroyed player %s object %p:%s\n", gpGlobals->curtime,
|
||
|
GetPlayerName(),
|
||
|
pObject,
|
||
|
pObject->GetClassname() ) );
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->OwnedObjectDestroyed( pObject );
|
||
|
}
|
||
|
|
||
|
RemoveObject( pObject );
|
||
|
|
||
|
// Tell our builder weapon so it recalculates the state of the build icons
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->GainedNewTechnology( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Removes an object from the player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RemoveObject( CBaseObject *pObject )
|
||
|
{
|
||
|
TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::RemoveObject %p:%s from player %s\n", gpGlobals->curtime,
|
||
|
pObject,
|
||
|
pObject->GetClassname(),
|
||
|
GetPlayerName() ) );
|
||
|
|
||
|
Assert( pObject );
|
||
|
for (int i = m_TFLocal.m_aObjects.Count(); --i >= 0; )
|
||
|
{
|
||
|
// Also, while we're at it, remove all other bogus ones too...
|
||
|
if ((!m_TFLocal.m_aObjects[i].Get()) || (m_TFLocal.m_aObjects[i] == pObject))
|
||
|
{
|
||
|
m_TFLocal.m_aObjects.FastRemove(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pObject -
|
||
|
// *pNewOwner -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::OwnedObjectChangeTeam( CBaseObject *pObject, CBaseTFPlayer *pNewOwner )
|
||
|
{
|
||
|
TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::OwnedObjectChangeTeam player %s object %p:%s new player %s\n", gpGlobals->curtime,
|
||
|
GetPlayerName(),
|
||
|
pObject,
|
||
|
pObject->GetClassname(),
|
||
|
pNewOwner->GetPlayerName() ) );
|
||
|
|
||
|
if ( pNewOwner && pNewOwner->GetPlayerClass() )
|
||
|
{
|
||
|
pNewOwner->GetPlayerClass()->OwnedObjectChangeFromTeam( pObject, this );
|
||
|
}
|
||
|
|
||
|
// Remove from my list of objects
|
||
|
RemoveObject( pObject );
|
||
|
|
||
|
// Add to new team
|
||
|
if ( pNewOwner )
|
||
|
{
|
||
|
pNewOwner->AddObject( pObject );
|
||
|
}
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->OwnedObjectChangeToTeam( pObject, pNewOwner );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pZone -
|
||
|
// Output : int
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::NumPumpsOnResourceZone( CResourceZone *pZone )
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
for( int iObj=0; iObj < GetObjectCount(); iObj++ )
|
||
|
{
|
||
|
CBaseObject *pObj = GetObject(iObj);
|
||
|
|
||
|
if( pObj->GetType() == OBJ_RESOURCEPUMP )
|
||
|
{
|
||
|
CObjectResourcePump *pPump = (CObjectResourcePump*)pObj;
|
||
|
|
||
|
// Ok, this guy already has a pump here.
|
||
|
if( pPump->GetResourceZone() == pZone )
|
||
|
++ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove all the player's objects
|
||
|
// If bForceAll is set, remove all of them immediately.
|
||
|
// Otherwise, make them all deteriorate over time.
|
||
|
// If iClass is passed in, don't remove any objects that can be built
|
||
|
// by that class. If bReturnResources is set, the cost of any destroyed
|
||
|
// objects will be returned to the player.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RemoveAllObjects( bool bForceAll, int iClass, bool bReturnResources )
|
||
|
{
|
||
|
// Remove all the player's objects
|
||
|
int iSize = GetObjectCount();
|
||
|
for (int i = iSize-1; i >= 0; i--)
|
||
|
{
|
||
|
CBaseObject *obj = GetObject(i);
|
||
|
Assert( obj );
|
||
|
if ( !obj )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !bForceAll )
|
||
|
{
|
||
|
if ( iClass )
|
||
|
{
|
||
|
// Can our new class build this object?
|
||
|
if ( ClassCanBuild( iClass, obj->GetType() ) )
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Vehicles don't deteriorate when their owner changes teams/leaves.
|
||
|
// They'll deteriorate naturally if they're unused for a while.
|
||
|
if ( IsObjectAVehicle(obj->GetType()) )
|
||
|
{
|
||
|
RemoveObject( obj );
|
||
|
|
||
|
// Just remove it from my list
|
||
|
obj->SetBuilder( NULL );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return the cost of the object?
|
||
|
if ( bReturnResources )
|
||
|
{
|
||
|
GetPlayerClass()->PickupObject( obj );
|
||
|
}
|
||
|
|
||
|
// Remove or deteriorate?
|
||
|
if ( bForceAll )
|
||
|
{
|
||
|
UTIL_Remove( obj );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
OwnedObjectDestroyed( obj );
|
||
|
obj->StartDeteriorating();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::StopPlacement( void )
|
||
|
{
|
||
|
// Tell our builder weapon
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->StopPlacement();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Player has started building an object
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::StartedBuildingObject( int iObjectType )
|
||
|
{
|
||
|
// Tell our playerclass
|
||
|
if ( GetPlayerClass() )
|
||
|
return GetPlayerClass()->StartedBuildingObject( iObjectType );
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Player has aborted building something
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::StoppedBuilding( int iObjectType )
|
||
|
{
|
||
|
// Tell our playerclass
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->StoppedBuilding( iObjectType );
|
||
|
}
|
||
|
|
||
|
// Tell our builder weapon
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->StoppedBuilding( iObjectType );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Object has been built by this player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::FinishedObject( CBaseObject *pObject )
|
||
|
{
|
||
|
AddObject( pObject );
|
||
|
|
||
|
// Tell our playerclass
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->FinishedObject( pObject );
|
||
|
}
|
||
|
|
||
|
// Tell our builder weapon
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->FinishedObject();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Add the specified object to this player's object list.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::AddObject( CBaseObject *pObject )
|
||
|
{
|
||
|
TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::AddObject adding object %p:%s to player %s\n", gpGlobals->curtime, pObject, pObject->GetClassname(), GetPlayerName() ) );
|
||
|
|
||
|
// Make a handle out of it
|
||
|
CHandle<CBaseObject> hObject;
|
||
|
hObject = pObject;
|
||
|
|
||
|
// Make sure it's not in the list already
|
||
|
bool alreadyInList = (m_TFLocal.m_aObjects.Find( hObject ) != -1);
|
||
|
Assert( !alreadyInList );
|
||
|
if ( !alreadyInList )
|
||
|
{
|
||
|
m_TFLocal.m_aObjects.AddToTail( hObject );
|
||
|
}
|
||
|
|
||
|
// Stop it deterioating, if it is
|
||
|
pObject->StopDeteriorating();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetWeaponBuilder( CWeaponBuilder *pBuilder )
|
||
|
{
|
||
|
m_hWeaponBuilder = pBuilder;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CWeaponBuilder *CBaseTFPlayer::GetWeaponBuilder( void )
|
||
|
{
|
||
|
return m_hWeaponBuilder;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *attacker -
|
||
|
// sourceDir -
|
||
|
// duration -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::KnockDownPlayer( const Vector& sourceDir, float magnitude, float duration )
|
||
|
{
|
||
|
// Already knocked down
|
||
|
if ( m_TFLocal.m_bKnockedDown )
|
||
|
return;
|
||
|
// In a vehicle
|
||
|
if ( IsInAVehicle() )
|
||
|
return;
|
||
|
|
||
|
m_TFLocal.m_bKnockedDown = true;
|
||
|
|
||
|
// Randomize it a bit
|
||
|
Vector jitter( 0, 0, 0 );
|
||
|
//jitter.Random( -0.1, 0.1 );
|
||
|
|
||
|
Vector dir = sourceDir + jitter;
|
||
|
|
||
|
VectorNormalize( dir );
|
||
|
|
||
|
Vector force = dir * magnitude;
|
||
|
ApplyAbsVelocityImpulse( force );
|
||
|
|
||
|
VectorAngles( dir, m_TFLocal.m_vecKnockDownDir.GetForModify() );
|
||
|
|
||
|
QAngle ang = GetAbsAngles();
|
||
|
Vector forward, right;
|
||
|
AngleVectors( ang, &forward, &right, NULL );
|
||
|
|
||
|
float dotFwd = dir.Dot( forward );
|
||
|
float dotRight = dir.Dot( right );
|
||
|
|
||
|
if ( dotFwd >= 0)
|
||
|
{
|
||
|
// if get hit from behind, pitch down a bit
|
||
|
m_TFLocal.m_vecKnockDownDir.SetX( dotFwd * 20.0f );
|
||
|
// look in the direction you fell
|
||
|
m_TFLocal.m_vecKnockDownDir.SetZ( dotRight * 80.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Invert knock yaw if hit from front, so you are looking straight up at the direction
|
||
|
// the hit cam efrom
|
||
|
m_TFLocal.m_vecKnockDownDir += QAngle( 0, 180, 0 );
|
||
|
// Look up in the air
|
||
|
m_TFLocal.m_vecKnockDownDir.SetX( fabs( dotFwd ) * -60.0f );
|
||
|
// And a bit to the side the hit came from
|
||
|
m_TFLocal.m_vecKnockDownDir.SetZ( dotRight * 20.0f );
|
||
|
}
|
||
|
|
||
|
m_flKnockdownEndTime = gpGlobals->curtime + duration;
|
||
|
|
||
|
// Play some kind of knockdown sound
|
||
|
EmitSound( "BaseTFPlayer.KnockedDown" );
|
||
|
|
||
|
if ( BecomeRagdollOnClient( force ) )
|
||
|
{
|
||
|
// We we are using ragdoll flight, then don't change underlying player
|
||
|
// velocity
|
||
|
ApplyAbsVelocityImpulse( -force );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ResetKnockdown( void )
|
||
|
{
|
||
|
// Don't get up if I'm dead
|
||
|
if ( IsAlive() )
|
||
|
{
|
||
|
if ( !ClearClientRagdoll( true ) )
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_TFLocal.m_bKnockedDown = false;
|
||
|
m_TFLocal.m_vecKnockDownDir.Init();
|
||
|
m_flKnockdownEndTime = 0.0f;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsKnockedDown( void )
|
||
|
{
|
||
|
return m_TFLocal.m_bKnockedDown;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CheckKnockdown( void )
|
||
|
{
|
||
|
if ( !m_TFLocal.m_bKnockedDown )
|
||
|
return;
|
||
|
|
||
|
if ( gpGlobals->curtime < m_flKnockdownEndTime )
|
||
|
return;
|
||
|
|
||
|
// Remove knockdown
|
||
|
ResetKnockdown();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsGagged( void )
|
||
|
{
|
||
|
return m_bGagged;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : gag -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetGagged( bool gag )
|
||
|
{
|
||
|
m_bGagged = gag;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::CanSpeak( void )
|
||
|
{
|
||
|
return !IsGagged();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsUsingThermalVision( void )
|
||
|
{
|
||
|
return m_TFLocal.m_bThermalVision;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetIDEnt( CBaseEntity *pEntity )
|
||
|
{
|
||
|
if ( pEntity )
|
||
|
m_TFLocal.m_iIDEntIndex = pEntity->entindex();
|
||
|
else
|
||
|
m_TFLocal.m_iIDEntIndex = 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : thermal -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetUsingThermalVision( bool thermal )
|
||
|
{
|
||
|
// Play sounds if we're changing
|
||
|
if ( m_TFLocal.m_bThermalVision != thermal )
|
||
|
{
|
||
|
if ( thermal )
|
||
|
{
|
||
|
EmitSound( "BaseTFPlayer.ThermalOn" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EmitSound( "BaseTFPlayer.ThermalOff" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_TFLocal.m_bThermalVision = thermal;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Add the specified number of resource chunks to the player. Return true if he can carry it all.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::AddResourceChunks( int iChunks, bool bProcessed )
|
||
|
{
|
||
|
// Am I allowed to carry any more chunks?
|
||
|
int iCurrentCount = GetTotalResourceChunks();
|
||
|
// Somewhat hacky
|
||
|
int iIndex = GetAmmoDef()->Index("ResourceChunks");
|
||
|
int iMax = GetAmmoDef()->MaxCarry( iIndex );
|
||
|
if ( iCurrentCount >= iMax )
|
||
|
{
|
||
|
bool bSwapped = false;
|
||
|
|
||
|
// If this is a processed chunk, see if we can swap it for an unprocessed chunk
|
||
|
if ( bProcessed )
|
||
|
{
|
||
|
if ( m_TFLocal.m_iResourceAmmo[ NORMAL_RESOURCES ] )
|
||
|
{
|
||
|
// Drop this unprocessed chunk
|
||
|
Vector vecVelocity = Vector( random->RandomFloat( -250,250 ), random->RandomFloat( -250,250 ), random->RandomFloat( 200,450 ) );
|
||
|
CResourceChunk::Create( false, GetAbsOrigin() + Vector(0,0,32), vecVelocity );
|
||
|
RemoveResourceChunks( 1, false );
|
||
|
bSwapped = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !bSwapped )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_TFLocal.m_iResourceAmmo.Set( bProcessed, MIN( iMax, m_TFLocal.m_iResourceAmmo[ bProcessed ] + iChunks ) );
|
||
|
SetAmmoCount( GetTotalResourceChunks(), iIndex );
|
||
|
CPASAttenuationFilter filter( this,"BaseTFPlayer.PickupResources" );
|
||
|
EmitSound( filter, entindex(),"BaseTFPlayer.PickupResources" );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove the specified number of resources chunks from the player.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RemoveResourceChunks( int iChunks, bool bProcessed )
|
||
|
{
|
||
|
// Remove the amount
|
||
|
m_TFLocal.m_iResourceAmmo.Set( bProcessed, MAX( 0, m_TFLocal.m_iResourceAmmo[ bProcessed ] - iChunks ) );
|
||
|
int iIndex = GetAmmoDef()->Index("ResourceChunks");
|
||
|
SetAmmoCount( GetTotalResourceChunks(), iIndex );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Get the number of resource chunks of this type the player's carrying
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetResourceChunkCount( bool bProcessed )
|
||
|
{
|
||
|
return m_TFLocal.m_iResourceAmmo[ bProcessed ];
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Get the total number of resource chunks being carried by the player
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetTotalResourceChunks( void )
|
||
|
{
|
||
|
int iCurrentCount = 0;
|
||
|
for ( int i = 0; i < RESOURCE_TYPES; i++ )
|
||
|
{
|
||
|
iCurrentCount += m_TFLocal.m_iResourceAmmo[i];
|
||
|
}
|
||
|
|
||
|
return iCurrentCount;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Drop some resource chunks
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::DropAllResourceChunks( void )
|
||
|
{
|
||
|
Vector vecOrigin = GetAbsOrigin() + Vector(0,0,32);
|
||
|
|
||
|
TFStats()->IncrementTeamStat( GetTeamNumber(), TF_TEAM_STAT_RESOURCE_CHUNKS_DROPPED, resource_chunk_value.GetFloat() );
|
||
|
|
||
|
// Drop a resource chunk
|
||
|
Vector vecVelocity = Vector( random->RandomFloat( -250,250 ), random->RandomFloat( -250,250 ), random->RandomFloat( 200,450 ) );
|
||
|
CResourceChunk *pChunk = CResourceChunk::Create( FALSE, vecOrigin, vecVelocity );
|
||
|
pChunk->ChangeTeam( GetTeamNumber() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------------------------------------
|
||
|
// RESOURCE BANK
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Get the amount of a resource in this player's bank
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::GetBankResources( void )
|
||
|
{
|
||
|
return m_TFLocal.ResourceCount();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetBankResources( int iAmount )
|
||
|
{
|
||
|
int nOldAmount = m_TFLocal.ResourceCount();
|
||
|
|
||
|
TFStats()->IncrementPlayerStat( this, TF_PLAYER_STAT_RESOURCES_ACQUIRED, iAmount - nOldAmount );
|
||
|
|
||
|
m_TFLocal.SetResources( iAmount );
|
||
|
|
||
|
// Tell the player's builder weapon to update
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->GainedNewTechnology( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Add resources to this player's Bank
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::AddBankResources( int iAmount )
|
||
|
{
|
||
|
m_TFLocal.AddResources( iAmount );
|
||
|
|
||
|
// Tell the player's builder weapon to update
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->GainedNewTechnology( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove resources to this player's Bank
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::RemoveBankResources( int iAmount, bool bSpent )
|
||
|
{
|
||
|
m_TFLocal.RemoveResources( iAmount );
|
||
|
|
||
|
// Tell the player's builder weapon to update
|
||
|
CWeaponBuilder *pBuilder = GetWeaponBuilder();
|
||
|
if ( pBuilder )
|
||
|
{
|
||
|
pBuilder->GainedNewTechnology( NULL );
|
||
|
}
|
||
|
|
||
|
if (bSpent)
|
||
|
{
|
||
|
TFStats()->IncrementPlayerStat( this, TF_PLAYER_STAT_RESOURCES_SPENT, iAmount );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsCamouflaged( void )
|
||
|
{
|
||
|
return ( m_flCamouflageAmount > 0.0f ) ? true : false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Change state over time
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::CheckCamouflage( void )
|
||
|
{
|
||
|
if ( m_flCamouflageAmount == m_flGoalCamouflageAmount )
|
||
|
return;
|
||
|
|
||
|
float remaining = m_flGoalCamouflageAmount - m_flCamouflageAmount;
|
||
|
float maxstep = m_flGoalCamouflageChangeRate * gpGlobals->frametime;
|
||
|
|
||
|
if ( remaining > 0.0f )
|
||
|
{
|
||
|
m_flCamouflageAmount += MIN( remaining, maxstep );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
remaining = -remaining;
|
||
|
m_flCamouflageAmount -= MIN( remaining, maxstep );
|
||
|
}
|
||
|
|
||
|
m_flCamouflageAmount = MAX( 0.0f, m_flCamouflageAmount );
|
||
|
m_flCamouflageAmount = MIN( 100.0f, m_flCamouflageAmount );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Goal % and rate in percent/second to achieve the goal
|
||
|
// Input : percentage -
|
||
|
// changerate -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetCamouflaged( int percentage, float changerate )
|
||
|
{
|
||
|
m_flGoalCamouflageAmount = (float)percentage;
|
||
|
m_flGoalCamouflageChangeRate = changerate;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove the player's camo
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ClearCamouflage( void )
|
||
|
{
|
||
|
SetCamouflaged( 0, 1000 );
|
||
|
|
||
|
// Tell the playerclass
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ClearCamouflage();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Confirm powerup durations
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CBaseTFPlayer::PowerupDuration( int iPowerup, float flTime )
|
||
|
{
|
||
|
// Medics are never EMPed for long
|
||
|
if ( PlayerClass() == TFCLASS_MEDIC && iPowerup == POWERUP_EMP )
|
||
|
return 0.2;
|
||
|
|
||
|
return BaseClass::PowerupDuration( iPowerup, flTime );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return the player's anim speed multiplier. Used for speeding up viewmodels while rushed.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CBaseTFPlayer::GetDefaultAnimSpeed( void )
|
||
|
{
|
||
|
if ( HasPowerup( POWERUP_RUSH ) )
|
||
|
return ADRENALIN_ANIM_SPEED;
|
||
|
|
||
|
// Weapons may modify animation times
|
||
|
if ( GetActiveWeapon() )
|
||
|
return GetActiveWeapon()->GetDefaultAnimSpeed();
|
||
|
|
||
|
return 1.0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Donate resources to a teammate
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::DonateResources( CBaseTFPlayer *pTarget, int pCount )
|
||
|
{
|
||
|
Assert( pTarget );
|
||
|
|
||
|
int nTotalCountDonated = 0;
|
||
|
int nDonationCount = GetBankResources();
|
||
|
if ( pCount < nDonationCount )
|
||
|
nDonationCount = pCount;
|
||
|
|
||
|
if (nDonationCount)
|
||
|
{
|
||
|
RemoveBankResources( nDonationCount, false );
|
||
|
pTarget->AddBankResources( nDonationCount );
|
||
|
nTotalCountDonated += nDonationCount;
|
||
|
}
|
||
|
|
||
|
if (nTotalCountDonated > 0)
|
||
|
{
|
||
|
char buf[1024];
|
||
|
Q_snprintf( buf, sizeof( buf ), "%s has donated %d resources to you\n",
|
||
|
GetPlayerName(), nTotalCountDonated );
|
||
|
ClientPrint( pTarget, HUD_PRINTCENTER, buf );
|
||
|
|
||
|
CSingleUserRecipientFilter filter( this );
|
||
|
EmitSound( filter, entindex(), "BaseTFPlayer.DonateResources" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Infilitrator's can +use a corpse to consume it
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||
|
{
|
||
|
if ( pActivator->IsPlayer() )
|
||
|
{
|
||
|
CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>(pActivator);
|
||
|
|
||
|
if ( InSameTeam( pActivator ))
|
||
|
{
|
||
|
// Resource donation
|
||
|
pPlayer->DonateResources( this, 25 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::Use( pActivator, pCaller, useType, value );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// The player's usable...
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::ObjectCaps( void )
|
||
|
{
|
||
|
return ( (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::CantMove( void )
|
||
|
{
|
||
|
return m_bCantMove;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetCantMove( bool bCantMove )
|
||
|
{
|
||
|
m_bCantMove = bCantMove;
|
||
|
RecalculateSpeed();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ResetViewOffset( void )
|
||
|
{
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ResetViewOffset();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetViewOffset( VEC_VIEW_SCALED( this ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: If ragdolling, move the player along the path that the ragdoll takes
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::FollowClientRagdoll( void )
|
||
|
{
|
||
|
if (( m_hRagdollShadow == NULL ) || ( GetPlayerClass() == NULL ))
|
||
|
return;
|
||
|
|
||
|
Vector vecMin, vecMax;
|
||
|
GetPlayerClass()->GetPlayerHull( ( ( GetFlags() & FL_DUCKING ) == 1 ), vecMin, vecMax );
|
||
|
|
||
|
// Follow shadow object
|
||
|
trace_t tr;
|
||
|
|
||
|
UTIL_TraceHull(
|
||
|
m_hRagdollShadow->GetAbsOrigin() + Vector(0,0,18),
|
||
|
m_hRagdollShadow->GetAbsOrigin(),
|
||
|
vecMin,
|
||
|
vecMax,
|
||
|
MASK_PLAYERSOLID,
|
||
|
m_hRagdollShadow,
|
||
|
COLLISION_GROUP_NONE,
|
||
|
&tr );
|
||
|
|
||
|
// Only move if we can find a valid spot under where shadow rolled
|
||
|
if ( !tr.allsolid )
|
||
|
{
|
||
|
UTIL_SetOrigin( this, tr.endpos );
|
||
|
VectorCopy( tr.endpos, m_vecLastGoodRagdollPos );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Stop being a ragdoll
|
||
|
// Input : moveplayertofinalspot -
|
||
|
// Output : return whether or not the ragdoll was cleared
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::ClearClientRagdoll( bool moveplayertofinalspot )
|
||
|
{
|
||
|
if ( m_hRagdollShadow )
|
||
|
{
|
||
|
if ( GetContainingEntity( edict() ) )
|
||
|
{
|
||
|
if ( moveplayertofinalspot )
|
||
|
{
|
||
|
// Move player to resting spot of shadow object
|
||
|
FollowClientRagdoll();
|
||
|
|
||
|
// Check for a valid standing position. If an entity is blocking impart some
|
||
|
// velocity to them and check again.
|
||
|
trace_t trace;
|
||
|
if ( CheckRagdollToStand( trace ) )
|
||
|
{
|
||
|
// Switch back to normal movement and kill off ragdoll bone setup on client
|
||
|
SetMoveType( MOVETYPE_WALK );
|
||
|
m_nRenderFX = kRenderFxNone;
|
||
|
//RemoveSolidFlags( FSOLID_NOTSOLID );
|
||
|
Assert( GetPlayerClass() != NULL );
|
||
|
Vector vecMin, vecMax;
|
||
|
GetPlayerClass()->GetPlayerHull( ( ( GetFlags() & FL_DUCKING ) == 1 ), vecMin, vecMax );
|
||
|
UTIL_SetSize( this, vecMin, vecMax );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CBaseEntity *pEntity = trace.m_pEnt;
|
||
|
if ( pEntity != GetContainingEntity( INDEXENT( 0 ) ) )
|
||
|
{
|
||
|
// Check for a physics object and apply force!
|
||
|
IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
|
||
|
if ( pPhysObject )
|
||
|
{
|
||
|
Vector vecDirection( random->RandomFloat( 0.0f, 1.0f ),
|
||
|
random->RandomFloat( 0.0f, 1.0f ),
|
||
|
random->RandomFloat( 0.0f, 1.0f ) );
|
||
|
vecDirection *= 40000.0f;
|
||
|
pPhysObject->ApplyForceCenter( vecDirection );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UTIL_SetOrigin( this, Vector( m_vecLastGoodRagdollPos.x, m_vecLastGoodRagdollPos.y, m_vecLastGoodRagdollPos.z + 18.0f ) );
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Kill the shadow object
|
||
|
UTIL_Remove( m_hRagdollShadow );
|
||
|
m_hRagdollShadow = NULL;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::CheckRagdollToStand( trace_t &trace )
|
||
|
{
|
||
|
Assert( GetPlayerClass() != NULL );
|
||
|
Vector vecMin, vecMax;
|
||
|
GetPlayerClass()->GetPlayerHull( ( ( GetFlags() & FL_DUCKING ) == 1 ), vecMin, vecMax );
|
||
|
|
||
|
// Write this better -- this is just a test to get things started.
|
||
|
UTIL_TraceHull(
|
||
|
m_vecLastGoodRagdollPos + Vector( 0, 0, 18 ),
|
||
|
m_vecLastGoodRagdollPos,
|
||
|
vecMin,
|
||
|
vecMax,
|
||
|
MASK_PLAYERSOLID,
|
||
|
m_hRagdollShadow,
|
||
|
COLLISION_GROUP_NONE,
|
||
|
&trace );
|
||
|
|
||
|
if ( !trace.allsolid )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Start being a ragdoll, creates client ragdoll object and server
|
||
|
// physics shadow object
|
||
|
// Input : &force -
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::BecomeRagdollOnClient( const Vector &force )
|
||
|
{
|
||
|
// Defender doesn't support it yet
|
||
|
if ( PlayerClass() == TFCLASS_INFILTRATOR )
|
||
|
return false;
|
||
|
|
||
|
// Initialize the good ragdoll position.
|
||
|
VectorCopy( GetAbsOrigin(), m_vecLastGoodRagdollPos );
|
||
|
|
||
|
bool bret = BaseClass::BecomeRagdollOnClient( force );
|
||
|
|
||
|
// ROBIN: Disabled ragdoll shadows for now.
|
||
|
// We'll re-enable them if we need to know the end position again
|
||
|
// If we re-enable them, we need to fix the ragdoll shadow not having the correct mass
|
||
|
return bret;
|
||
|
|
||
|
AddSolidFlags( FSOLID_NOT_SOLID );
|
||
|
|
||
|
// Clear any old shadow object ( should never occur )
|
||
|
ClearClientRagdoll( false );
|
||
|
|
||
|
// Create new shadow object
|
||
|
m_hRagdollShadow = CRagdollShadow::Create( this, force );
|
||
|
|
||
|
return bret;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// AddGesture - add a gesture into the animation queue
|
||
|
//=========================================================
|
||
|
int CBaseTFPlayer::AddGesture( Activity activity, bool autokill /*= true*/ )
|
||
|
{
|
||
|
int layer = BaseClass::AddGesture( activity, autokill );
|
||
|
SetLayerBlendIn( layer, 0.0 );
|
||
|
SetLayerBlendOut( layer, 0.0 );
|
||
|
return layer;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Class specific touch functionality!
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::ClassTouch( CBaseEntity *pTouched )
|
||
|
{
|
||
|
if ( m_pfnClassTouch && HasClass() )
|
||
|
{
|
||
|
(GetPlayerClass()->*m_pfnClassTouch)( pTouched );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* CBaseTFPlayer::GetClassModelString( int iClass, int iTeam )
|
||
|
{
|
||
|
return m_PlayerClasses.GetPlayerClass( iClass )->GetClassModelString( iTeam );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : bRampage -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetRampage( bool bRampage )
|
||
|
{
|
||
|
m_bRampage = bRampage;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CBaseTFPlayer::IsInRampage( void )
|
||
|
{
|
||
|
return m_bRampage;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::SetPlayerClass( TFClass iClass )
|
||
|
{
|
||
|
if ( m_iPlayerClass != iClass )
|
||
|
{
|
||
|
m_Timer.End();
|
||
|
|
||
|
if ( m_iPlayerClass >= 0 && m_iPlayerClass < TFCLASS_CLASS_COUNT )
|
||
|
{
|
||
|
void AddPlayerClassTime( int classnum, float seconds );
|
||
|
|
||
|
AddPlayerClassTime( m_iPlayerClass, m_Timer.GetDuration().GetSeconds() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_iPlayerClass >= 0 )
|
||
|
{
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ClassDeactivate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_iPlayerClass = iClass;
|
||
|
|
||
|
if ( m_iPlayerClass >= 0 )
|
||
|
{
|
||
|
SetPlayerModel();
|
||
|
|
||
|
m_Timer.Start();
|
||
|
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->ClassActivate();
|
||
|
// Setup the class on initial spawn
|
||
|
GetPlayerClass()->CreateClass();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CBaseTFPlayer::ClassCostAdjustment( ResupplyBuyType_t nType )
|
||
|
{
|
||
|
if ( m_iPlayerClass >= 0 )
|
||
|
{
|
||
|
return GetPlayerClass()->ClassCostAdjustment( nType );
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// Player Physics Shadow Code
|
||
|
//
|
||
|
class CPhysicsTFPlayerCallback : public IPhysicsPlayerControllerEvent
|
||
|
{
|
||
|
public:
|
||
|
int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
|
||
|
{
|
||
|
CBaseTFPlayer *pPlayer = ( CBaseTFPlayer* )pObject->GetGameData();
|
||
|
if ( pPlayer->TouchedPhysics() )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static CPhysicsTFPlayerCallback TFPlayerCallback;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CBaseTFPlayer::InitVCollision( void )
|
||
|
{
|
||
|
if ( GetPlayerClass() )
|
||
|
{
|
||
|
GetPlayerClass()->InitVCollision();
|
||
|
}
|
||
|
|
||
|
// Setup the HL2 specific callback.
|
||
|
if ( GetPhysicsController() )
|
||
|
{
|
||
|
GetPhysicsController()->SetEventHandler( &TFPlayerCallback );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return the entity that should receive the score
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBasePlayer *CBaseTFPlayer::GetScorer( void )
|
||
|
{
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return the entity that should get assistance credit
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBasePlayer *CBaseTFPlayer::GetAssistant( void )
|
||
|
{
|
||
|
// If I'm in a vehicle, the builder gets credit
|
||
|
if ( IsInAVehicle() )
|
||
|
{
|
||
|
CBaseObject *pObject = dynamic_cast<CBaseObject*>( GetVehicle() );
|
||
|
if ( pObject )
|
||
|
{
|
||
|
CBasePlayer *pBuilder = pObject->GetBuilder();
|
||
|
if ( pBuilder && pBuilder != this )
|
||
|
return pBuilder;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If I'm boosted, someone's getting the assist
|
||
|
if ( HasPowerup( POWERUP_BOOST ) && m_hLastBoostEntity.Get() )
|
||
|
{
|
||
|
// I may have boosted myself
|
||
|
if ( m_hLastBoostEntity.Get() != this )
|
||
|
{
|
||
|
if ( m_hLastBoostEntity->IsPlayer() )
|
||
|
return (CBasePlayer*)m_hLastBoostEntity.Get();
|
||
|
|
||
|
// If it's an object, give the builder the assist (i.e. buff station)
|
||
|
CBaseObject *pObject = dynamic_cast<CBaseObject*>( m_hLastBoostEntity.Get() );
|
||
|
if ( pObject )
|
||
|
{
|
||
|
CBasePlayer *pBuilder = pObject->GetBuilder();
|
||
|
if ( pBuilder && pBuilder != this )
|
||
|
return pBuilder;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|