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.
3720 lines
99 KiB
3720 lines
99 KiB
//========= 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; |
|
}
|
|
|