diff options
Diffstat (limited to 'game/server/tfc/tfc_player.cpp')
| -rw-r--r-- | game/server/tfc/tfc_player.cpp | 1115 |
1 files changed, 1115 insertions, 0 deletions
diff --git a/game/server/tfc/tfc_player.cpp b/game/server/tfc/tfc_player.cpp new file mode 100644 index 0000000..d006efc --- /dev/null +++ b/game/server/tfc/tfc_player.cpp @@ -0,0 +1,1115 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL1. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "tfc_player.h" +#include "tfc_gamerules.h" +#include "KeyValues.h" +#include "viewport_panel_names.h" +#include "client.h" +#include "team.h" +#include "weapon_tfcbase.h" +#include "tfc_client.h" +#include "tfc_mapitems.h" +#include "tfc_timer.h" +#include "tfc_engineer.h" +#include "tfc_team.h" + + +#define TFC_PLAYER_MODEL "models/player/pyro.mdl" + + +// -------------------------------------------------------------------------------- // +// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. +// -------------------------------------------------------------------------------- // + +class CTEPlayerAnimEvent : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity ); + DECLARE_SERVERCLASS(); + + CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name ) + { + } + + CNetworkHandle( CBasePlayer, m_hPlayer ); + CNetworkVar( int, m_iEvent ); + CNetworkVar( int, m_nData ); +}; + +IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent ) + SendPropEHandle( SENDINFO( m_hPlayer ) ), + SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ) + SendPropInt( SENDINFO( m_nData ), 32 ) +END_SEND_TABLE() + +static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" ); + +void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData ) +{ + CPVSFilter filter( pPlayer->EyePosition() ); + + // The player himself doesn't need to be sent his animation events + // unless cs_showanimstate wants to show them. + if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) ) + { + filter.RemoveRecipient( pPlayer ); + } + + g_TEPlayerAnimEvent.m_hPlayer = pPlayer; + g_TEPlayerAnimEvent.m_iEvent = event; + g_TEPlayerAnimEvent.m_nData = nData; + g_TEPlayerAnimEvent.Create( filter, 0 ); +} + + +// -------------------------------------------------------------------------------- // +// Tables. +// -------------------------------------------------------------------------------- // + +LINK_ENTITY_TO_CLASS( player, CTFCPlayer ); +PRECACHE_REGISTER(player); + +IMPLEMENT_SERVERCLASS_ST( CTFCPlayer, DT_TFCPlayer ) + SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), + SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ), + SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), + SendPropExclude( "DT_BaseEntity", "m_angRotation" ), + SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ), + + // cs_playeranimstate and clientside animation takes care of these on the client + SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ), + SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), + + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ), + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ), + + SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) ) +END_SEND_TABLE() + + +// -------------------------------------------------------------------------------- // + +void cc_CreatePredictionError_f() +{ + CBaseEntity *pEnt = CBaseEntity::Instance( 1 ); + pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) ); +} + +ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT ); + + +CTFCPlayer::CTFCPlayer() +{ + m_PlayerAnimState = CreatePlayerAnimState( this ); + item_list = 0; + + UseClientSideAnimation(); + m_angEyeAngles.Init(); + m_pCurStateInfo = NULL; + m_lifeState = LIFE_DEAD; // Start "dead". + + SetViewOffset( TFC_PLAYER_VIEW_OFFSET ); + + SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" ); +} + + +void CTFCPlayer::TFCPlayerThink() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink ) + (this->*m_pCurStateInfo->pfnThink)(); + + SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" ); +} + + +CTFCPlayer::~CTFCPlayer() +{ + m_PlayerAnimState->Release(); +} + + +CTFCPlayer *CTFCPlayer::CreatePlayer( const char *className, edict_t *ed ) +{ + CTFCPlayer::s_PlayerEdict = ed; + return (CTFCPlayer*)CreateEntityByName( className ); +} + + +void CTFCPlayer::PostThink() +{ + BaseClass::PostThink(); + + QAngle angles = GetLocalAngles(); + angles[PITCH] = 0; + SetLocalAngles( angles ); + + // Store the eye angles pitch so the client can compute its animation state correctly. + m_angEyeAngles = EyeAngles(); + + m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); +} + + +void CTFCPlayer::Precache() +{ + for ( int i=0; i < PC_LASTCLASS; i++ ) + PrecacheModel( GetTFCClassInfo( i )->m_pModelName ); + + PrecacheScriptSound( "Player.Spawn" ); + + BaseClass::Precache(); +} + + +void CTFCPlayer::InitialSpawn( void ) +{ + BaseClass::InitialSpawn(); + + State_Enter( STATE_WELCOME ); +} + + +void CTFCPlayer::Spawn() +{ + SetModel( GetTFCClassInfo( m_Shared.GetPlayerClass() )->m_pModelName ); + + SetMoveType( MOVETYPE_WALK ); + m_iLegDamage = 0; + + BaseClass::Spawn(); + + // Kind of lame, but CBasePlayer::Spawn resets a lot of the state that we initially want on. + // So if we're in the welcome state, call its enter function to reset + if ( m_Shared.State_Get() == STATE_WELCOME ) + { + State_Enter_WELCOME(); + } + + // If they were dead, then they're respawning. Put them in the active state. + if ( m_Shared.State_Get() == STATE_DYING ) + { + State_Transition( STATE_ACTIVE ); + } + + // If they're spawning into the world as fresh meat, give them items and stuff. + if ( m_Shared.State_Get() == STATE_ACTIVE ) + { + EmitSound( "Player.Spawn" ); + GiveDefaultItems(); + } +} + + +void CTFCPlayer::ForceRespawn() +{ + //TFCTODO: goldsrc tfc has a big function for this.. doing what I'm doing here may not work. + respawn( this, false ); +} + + +void CTFCPlayer::GiveDefaultItems() +{ + switch( m_Shared.GetPlayerClass() ) + { + case PC_HWGUY: + { + GiveNamedItem( "weapon_crowbar" ); + + GiveNamedItem( "weapon_minigun" ); + GiveAmmo( 176, TFC_AMMO_SHELLS ); + } + break; + + case PC_PYRO: + { + GiveNamedItem( "weapon_crowbar" ); + } + break; + + case PC_ENGINEER: + { + GiveNamedItem( "weapon_spanner" ); + GiveNamedItem( "weapon_super_shotgun" ); + GiveAmmo( 20, TFC_AMMO_SHELLS ); + } + break; + + case PC_SCOUT: + { + GiveNamedItem( "weapon_crowbar" ); + GiveNamedItem( "weapon_shotgun" ); + GiveNamedItem( "weapon_nailgun" ); + GiveAmmo( 25, TFC_AMMO_SHELLS ); + GiveAmmo( 100, TFC_AMMO_NAILS ); + } + break; + + case PC_SNIPER: + { + GiveNamedItem( "weapon_crowbar" ); + } + break; + + case PC_SOLDIER: + { + GiveNamedItem( "weapon_crowbar" ); + } + break; + + case PC_DEMOMAN: + { + GiveNamedItem( "weapon_crowbar" ); + } + break; + + case PC_SPY: + { + GiveNamedItem( "weapon_knife" ); + } + break; + + case PC_MEDIC: + { + GiveNamedItem( "weapon_medikit" ); + GiveNamedItem( "weapon_super_nailgun" ); + GiveAmmo( 100, TFC_AMMO_NAILS ); + } + break; + } +} + + +void CTFCPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + m_PlayerAnimState->DoAnimationEvent( event, nData ); + TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy. +} + + +void CTFCPlayer::State_Transition( TFCPlayerState newState ) +{ + State_Leave(); + State_Enter( newState ); +} + + +void CTFCPlayer::State_Enter( TFCPlayerState newState ) +{ + m_Shared.m_iPlayerState = newState; + m_pCurStateInfo = State_LookupInfo( newState ); + + // Initialize the new state. + if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) + (this->*m_pCurStateInfo->pfnEnterState)(); +} + + +void CTFCPlayer::State_Leave() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) + { + (this->*m_pCurStateInfo->pfnLeaveState)(); + } +} + + +CPlayerStateInfo* CTFCPlayer::State_LookupInfo( TFCPlayerState state ) +{ + // This table MUST match the + static CPlayerStateInfo playerStateInfos[] = + { + { STATE_ACTIVE, "STATE_ACTIVE", &CTFCPlayer::State_Enter_ACTIVE, NULL, NULL }, + { STATE_WELCOME, "STATE_WELCOME", &CTFCPlayer::State_Enter_WELCOME, NULL, NULL }, + { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CTFCPlayer::State_Enter_PICKINGTEAM, NULL, NULL }, + { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CTFCPlayer::State_Enter_PICKINGCLASS, NULL, NULL }, + { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CTFCPlayer::State_Enter_OBSERVER_MODE, NULL, NULL }, + { STATE_DYING, "STATE_DYING", &CTFCPlayer::State_Enter_DYING, NULL, NULL } + }; + + for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) + { + if ( playerStateInfos[i].m_iPlayerState == state ) + return &playerStateInfos[i]; + } + + return NULL; +} + + +void CTFCPlayer::State_Enter_WELCOME() +{ + SetMoveType( MOVETYPE_NONE ); + AddEffects( EF_NODRAW ); + AddSolidFlags( FSOLID_NOT_SOLID ); + PhysObjectSleep(); + + // Show info panel (if it's not a simple demo map). + KeyValues *data = new KeyValues("data"); + data->SetString( "title", "Message of the Day" ); // info panel title + data->SetString( "type", "3" ); // show a file + data->SetString( "msg", "motd.txt" ); // this file + data->SetString( "cmd", "joingame" ); // exec this command if panel closed + + ShowViewPortPanel( "info", true, data ); + + data->deleteThis(); +} + + +void CTFCPlayer::State_Enter_PICKINGTEAM() +{ + ShowViewPortPanel( PANEL_TEAM ); // show the team menu +} + + +void CTFCPlayer::State_Enter_PICKINGCLASS() +{ + // go to spec mode, if dying keep deathcam + if ( GetObserverMode() == OBS_MODE_DEATHCAM ) + { + StartObserverMode( OBS_MODE_DEATHCAM ); + } + else + { + StartObserverMode( OBS_MODE_ROAMING ); + } + + PhysObjectSleep(); + + // show the class menu: + ShowViewPortPanel( PANEL_CLASS ); +} + + +void CTFCPlayer::State_Enter_OBSERVER_MODE() +{ + StartObserverMode( m_iObserverLastMode ); + PhysObjectSleep(); +} + + +void CTFCPlayer::State_Enter_ACTIVE() +{ + SetMoveType( MOVETYPE_WALK ); + RemoveEffects( EF_NODRAW ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + m_Local.m_iHideHUD = 0; + PhysObjectWake(); +} + + +void CTFCPlayer::State_Enter_DYING() +{ + SetMoveType( MOVETYPE_NONE ); + AddSolidFlags( FSOLID_NOT_SOLID ); +} + + +void CTFCPlayer::PhysObjectSleep() +{ + IPhysicsObject *pObj = VPhysicsGetObject(); + if ( pObj ) + pObj->Sleep(); +} + + +void CTFCPlayer::PhysObjectWake() +{ + IPhysicsObject *pObj = VPhysicsGetObject(); + if ( pObj ) + pObj->Wake(); +} + + +void CTFCPlayer::HandleCommand_JoinTeam( const char *pTeamName ) +{ + int iTeam = TEAM_RED; + if ( stricmp( pTeamName, "auto" ) == 0 ) + { + iTeam = RandomInt( 0, 1 ) ? TEAM_RED : TEAM_BLUE; + } + else if ( stricmp( pTeamName, "spectate" ) == 0 ) + { + iTeam = TEAM_SPECTATOR; + } + else + { + for ( int i=0; i < TEAM_MAXCOUNT; i++ ) + { + if ( stricmp( pTeamName, teamnames[i] ) == 0 ) + { + iTeam = i; + break; + } + } + } + + if ( iTeam == TEAM_SPECTATOR ) + { + // Prevent this is the cvar is set + if ( !mp_allowspectators.GetInt() && !IsHLTV() ) + { + ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); + return; + } + + if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() ) + { + CommitSuicide(); + + // add 1 to frags to balance out the 1 subtracted for killing yourself + IncrementFragCount( 1 ); + } + + ChangeTeam( TEAM_SPECTATOR ); + + // do we have fadetoblack on? (need to fade their screen back in) + if ( mp_fadetoblack.GetInt() ) + { + color32_s clr = { 0,0,0,0 }; + UTIL_ScreenFade( this, clr, 0.001, 0, FFADE_IN ); + } + } + else + { + ChangeTeam( iTeam ); + State_Transition( STATE_PICKINGCLASS ); + } +} + + +void CTFCPlayer::ChangeTeam( int iTeamNum ) +{ + if ( !GetGlobalTeam( iTeamNum ) ) + { + Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum ); + return; + } + + int iOldTeam = GetTeamNumber(); + + // if this is our current team, just abort + if ( iTeamNum == iOldTeam ) + return; + + BaseClass::ChangeTeam( iTeamNum ); + + if ( iTeamNum == TEAM_UNASSIGNED ) + { + State_Transition( STATE_OBSERVER_MODE ); + } + else if ( iTeamNum == TEAM_SPECTATOR ) + { + State_Transition( STATE_OBSERVER_MODE ); + } + else // active player + { + if ( iOldTeam == TEAM_SPECTATOR ) + { + // If they're switching from being a spectator to ingame player + GetIntoGame(); + } + + if ( !IsDead() && iOldTeam != TEAM_UNASSIGNED ) + { + // Kill player if switching teams while alive + CommitSuicide(); + } + + // Put up the class selection menu. + State_Transition( STATE_PICKINGCLASS ); + } +} + + +void CTFCPlayer::HandleCommand_JoinClass( const char *pClassName ) +{ + int iClass = RandomInt( 0, PC_LAST_NORMAL_CLASS ); + if ( stricmp( pClassName, "random" ) != 0 ) + { + for ( int i=0; i < PC_LASTCLASS; i++ ) + { + if ( stricmp( pClassName, GetTFCClassInfo( i )->m_pClassName ) == 0 ) + { + iClass = i; + break; + } + } + if ( i == PC_LAST_NORMAL_CLASS ) + { + Warning( "HandleCommand_JoinClass( %s ) - invalid class name.\n", pClassName ); + } + } + + m_Shared.SetPlayerClass( iClass ); + + if ( !IsAlive() ) + GetIntoGame(); +} + + +void CTFCPlayer::GetIntoGame() +{ + State_Transition( STATE_ACTIVE ); + Spawn(); +} + + +bool CTFCPlayer::ClientCommand( const CCommand& args ) +{ + const char *pcmd = args[0]; + if ( FStrEq( pcmd, "joingame" ) ) + { + // player just closed MOTD dialog + if ( m_Shared.m_iPlayerState == STATE_WELCOME ) + { + State_Transition( STATE_PICKINGTEAM ); + } + return true; + } + else if ( FStrEq( pcmd, "jointeam" ) ) + { + if ( args.ArgC() >= 2 ) + { + HandleCommand_JoinTeam( args[1] ); + return true; + } + } + else if ( FStrEq( pcmd, "joinclass" ) ) + { + if ( args.ArgC() < 2 ) + { + Warning( "Player sent bad joinclass syntax\n" ); + } + + HandleCommand_JoinClass( args[1] ); + return true; + } + + return BaseClass::ClientCommand( args ); +} + + +bool CTFCPlayer::IsAlly( CBaseEntity *pEnt ) const +{ + return pEnt->GetTeamNumber() == GetTeamNumber(); +} + + +void CTFCPlayer::TF_AddFrags( int nFrags ) +{ + // TFCTODO: implement frags +} + + +void CTFCPlayer::ResetMenu() +{ + current_menu = 0; +} + + +int CTFCPlayer::GetNumFlames() const +{ + // TFCTODO: implement flames + return 0; +} + + +void CTFCPlayer::SetNumFlames( int nFlames ) +{ + // TFCTODO: implement frags + Assert( 0 ); +} + +int CTFCPlayer::TakeHealth( float flHealth, int bitsDamageType ) +{ + int bResult = false; + + // If the bit's set, ignore the monster's max health and add over it + if ( bitsDamageType & DMG_IGNORE_MAXHEALTH ) + { + int iDamage = g_pGameRules->Damage_GetTimeBased(); + m_bitsDamageType &= ~(bitsDamageType & ~iDamage); + m_iHealth += flHealth; + bResult = true; + } + else + { + bResult = BaseClass::TakeHealth( flHealth, bitsDamageType ); + } + + // Leg Healing + if (m_iLegDamage > 0) + { + // Allow even at full health + if ( GetHealth() >= (GetMaxHealth() - 5)) + m_iLegDamage = 0; + else + m_iLegDamage -= (GetHealth() + flHealth) / 20; + if (m_iLegDamage < 1) + m_iLegDamage = 0; + + TeamFortress_SetSpeed(); + bResult = true; + } + + return bResult; +} + + +void CTFCPlayer::TeamFortress_SetSpeed() +{ + int playerclass = m_Shared.GetPlayerClass(); + float maxfbspeed; + + // Spectators can move while in Classic Observer mode + if ( IsObserver() ) + { + if ( GetObserverMode() == OBS_MODE_ROAMING ) + SetMaxSpeed( GetTFCClassInfo( PC_SCOUT )->m_flMaxSpeed ); + else + SetMaxSpeed( 0 ); + + return; + } + + // Check for any reason why they can't move at all + if ( (m_Shared.GetStateFlags() & TFSTATE_CANT_MOVE) || (playerclass == PC_UNDEFINED) ) + { + SetAbsVelocity( vec3_origin ); + SetMaxSpeed( 1 ); + return; + } + + // First, get their max class speed + maxfbspeed = GetTFCClassInfo( playerclass )->m_flMaxSpeed; + + // 2nd, see if any GoalItems are slowing them down + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" ); + while ( pEnt ) + { + CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt ); + if ( pGoal ) + { + if ( pGoal->GetOwnerEntity() == this ) + { + if (pGoal->goal_activation & TFGI_SLOW) + { + maxfbspeed = maxfbspeed / 2; + } + else if (pGoal->speed_reduction) + { + float flPercent = ((float)pGoal->speed_reduction) / 100.0; + maxfbspeed = flPercent * maxfbspeed; + } + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" ); + } + + // 3rd, See if they're tranquilised + if (m_Shared.GetStateFlags() & TFSTATE_TRANQUILISED) + { + maxfbspeed = maxfbspeed / 2; + } + + // 4th, check for leg wounds + if (m_iLegDamage) + { + if (m_iLegDamage > 6) + m_iLegDamage = 6; + + // reduce speed by 10% per leg wound + maxfbspeed = (maxfbspeed * ((10 - m_iLegDamage) / 10)); + } + + // 5th, if they're a sniper, and they're aiming, their speed must be 80 or less + if (m_Shared.GetStateFlags() & TFSTATE_AIMING) + { + if (maxfbspeed > 80) + maxfbspeed = 80; + } + + // Set the speed + SetMaxSpeed( maxfbspeed ); +} + + +void CTFCPlayer::Event_Killed( const CTakeDamageInfo &info ) +{ + DoAnimationEvent( PLAYERANIMEVENT_DIE ); + State_Transition( STATE_DYING ); // Transition into the dying state. + + // Remove all items.. + RemoveAllItems( true ); + + BaseClass::Event_Killed( info ); + + // Don't overflow the value for this. + m_iHealth = 0; +} + + +void CTFCPlayer::ClientHearVox( const char *pSentence ) +{ + //TFCTODO: implement this. +} + + +//========================================================================= +// Check all stats to make sure they're good for this class +void CTFCPlayer::TeamFortress_CheckClassStats() +{ + // Check armor + if (armortype > armor_allowed) + armortype = armor_allowed; + + if (ArmorValue() > GetClassInfo()->m_iMaxArmor) + SetArmorValue( GetClassInfo()->m_iMaxArmor ); + + if (ArmorValue() < 0) + SetArmorValue( 0 ); + + if (armortype < 0) + armortype = 0; + + // Check ammo + for ( int iAmmoType=0; iAmmoType < TFC_NUM_AMMO_TYPES; iAmmoType++ ) + { + if ( GetAmmoCount( iAmmoType ) > GetClassInfo()->m_MaxAmmo[iAmmoType] ) + RemoveAmmo( GetAmmoCount( iAmmoType ) - GetClassInfo()->m_MaxAmmo[iAmmoType], iAmmoType ); + } + + // Check Grenades + Assert( GetAmmoCount( TFC_AMMO_GRENADES1 ) >= 0 ); + Assert( GetAmmoCount( TFC_AMMO_GRENADES2 ) >= 0 ); + + // Limit Nails + if ( no_grenades_1() > g_nMaxGrenades[tp_grenades_1()] ) + RemoveAmmo( TFC_AMMO_GRENADES1, no_grenades_1() - g_nMaxGrenades[tp_grenades_1()] ); + + if ( no_grenades_2() > g_nMaxGrenades[tp_grenades_2()] ) + RemoveAmmo( TFC_AMMO_GRENADES2, no_grenades_2() - g_nMaxGrenades[tp_grenades_2()] ); + + // Check health + if (GetHealth() > GetMaxHealth() && !(m_Shared.GetItemFlags() & IT_SUPERHEALTH)) + SetHealth( GetMaxHealth() ); + + if (GetHealth() < 0) + SetHealth( 0 ); + + // Update armor picture + m_Shared.RemoveItemFlags( IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3 ); + if (armortype >= 0.8) + m_Shared.AddItemFlags( IT_ARMOR3 ); + else if (armortype >= 0.6) + m_Shared.AddItemFlags( IT_ARMOR2 ); + else if (armortype >= 0.3) + m_Shared.AddItemFlags( IT_ARMOR1 ); +} + + +//====================================================================== +// DISGUISE HANDLING +//====================================================================== +// Reset spy skin and color or remove invisibility +void CTFCPlayer::Spy_RemoveDisguise() +{ + if (m_Shared.GetPlayerClass() == PC_SPY) + { + if ( undercover_team || undercover_skin ) + ClientPrint( this, HUD_PRINTCENTER, "#Disguise_Lost" ); + + // Set their color + undercover_team = 0; + undercover_skin = 0; + + immune_to_check = gpGlobals->curtime + 10; + is_undercover = 0; + + // undisguise weapon + TeamFortress_SetSkin(); + TeamFortress_SpyCalcName(); + + Spy_ResetExternalWeaponModel(); + + // get them out of any disguise menus + if ( current_menu == MENU_SPY || current_menu == MENU_SPY_SKIN || current_menu == MENU_SPY_COLOR ) + { + ResetMenu(); + } + + // Remove the Disguise timer + CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DISGUISE ); + if (pTimer) + { + ClientPrint( this, HUD_PRINTCENTER, "#Disguise_stop" ); + Timer_Remove( pTimer ); + } + } +} + + +// when the spy loses disguise reset his weapon +void CTFCPlayer::Spy_ResetExternalWeaponModel( void ) +{ + // we don't show any weapon models if we're feigning + if ( is_feigning ) + return; + +#ifdef TFCTODO // spy + pev->weaponmodel = MAKE_STRING( m_pszSavedWeaponModel ); + strcpy( m_szAnimExtention, m_szSavedAnimExtention ); + m_iCurrentAnimationState = 0; // force the current animation sequence to be recalculated +#endif +} + + +//========================================================================= +// Try and find the player's name who's skin and team closest fit the +// current disguise of the spy +void CTFCPlayer::TeamFortress_SpyCalcName() +{ + CBaseEntity *last_target = undercover_target;// don't redisguise self as this person + + undercover_target = NULL; + + // Find a player on the team the spy is disguised as to pretend to be + if (undercover_team != 0) + { + CTFCPlayer *pPlayer = NULL; + + // Loop through players + int i; + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) ); + if ( pPlayer ) + { + if ( pPlayer == last_target ) + { + // choose someone else, we're trying to rid ourselves of a disguise as this one + continue; + } + + // First, try to find a player with same color and skins + if (pPlayer->GetTeamNumber() == undercover_team && pPlayer->m_Shared.GetPlayerClass() == undercover_skin) + { + undercover_target = pPlayer; + return; + } + } + } + + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer ) + { + if (pPlayer->GetTeamNumber() == undercover_team) + { + undercover_target = pPlayer; + return; + } + } + } + } +} + + +//========================================================================= +// Set the skin of a player based on his/her class +void CTFCPlayer::TeamFortress_SetSkin() +{ + immune_to_check = gpGlobals->curtime + 10; + + // Find out whether we should show our actual class or a disguised class + int iClassToUse = m_Shared.GetPlayerClass(); + if (iClassToUse == PC_SPY && undercover_skin != 0) + iClassToUse = undercover_skin; + + int iTeamToUse = GetTeamNumber(); + if (m_Shared.GetPlayerClass() == PC_SPY && undercover_team != 0) + iTeamToUse = undercover_team; + +// TFCTODO: handle replacement_model here. + + SetModel( GetTFCClassInfo( iClassToUse )->m_pModelName ); + + // Skins in the models should be setup using the team IDs in tfc_shareddefs.h, subtracting 1 + // so they're 0-based. + m_nSkin = iTeamToUse - 1; + + if ( FBitSet(GetFlags(), FL_DUCKING) ) + UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + else + UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX); +} + + + +//========================================================================= +// Displays the state of the items specified by the Goal passed in +void CTFCPlayer::DisplayLocalItemStatus( CTFGoal *pGoal ) +{ + for (int i = 0; i < 4; i++) + { + if (pGoal->display_item_status[i] != 0) + { + CTFGoalItem *pItem = Finditem(pGoal->display_item_status[i]); + if (pItem) + DisplayItemStatus(pGoal, this, pItem); + else + ClientPrint( this, HUD_PRINTTALK, "#Item_missing" ); + } + } +} + + +//========================================================================= +// Removes all the Engineer's buildings +void CTFCPlayer::Engineer_RemoveBuildings() +{ + // If the player's building already, stop + if (is_building == 1) + { + m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE ); + TeamFortress_SetSpeed(); + + // Remove the timer + CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_BUILD ); + if (pTimer) + Timer_Remove(pTimer); + + // Remove the building + UTIL_Remove( building ); + building = NULL; + is_building = 0; + + // Stop Build Sound + StopSound( "Engineer.Building" ); + //STOP_SOUND( ENT(pev), CHAN_STATIC, "weapons/building.wav" ); + + if ( GetActiveWeapon() ) + GetActiveWeapon()->Deploy(); + } + + DestroyBuilding(this, "building_dispenser"); + DestroyBuilding(this, "building_sentrygun"); + DestroyTeleporter(this, BUILD_TELEPORTER_ENTRY); + DestroyTeleporter(this, BUILD_TELEPORTER_EXIT); +} + + +//========================================================================= +// Removes all grenades that persist for a period of time from the world +void CTFCPlayer::TeamFortress_RemoveLiveGrenades( void ) +{ + RemoveOwnedEnt( "tf_weapon_napalmgrenade" ); + RemoveOwnedEnt( "tf_weapon_nailgrenade" ); + RemoveOwnedEnt( "tf_weapon_gasgrenade" ); + RemoveOwnedEnt( "tf_weapon_caltrop" ); +} + + +//========================================================================= +// Removes all rockets the player has fired into the world +// (this prevents a team kill cheat where players would fire rockets +// then change teams to kill their own team) +void CTFCPlayer::TeamFortress_RemoveRockets( void ) +{ + RemoveOwnedEnt( "tf_rpg_rocket" ); + RemoveOwnedEnt( "tf_ic_rocket" ); +} + +// removes the player's pipebombs with no explosions +void CTFCPlayer::RemovePipebombs( void ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "tf_gl_pipebomb" ); + while ( pEnt ) + { + CTFCPlayer *pOwner = ToTFCPlayer( pEnt->GetOwnerEntity() ); + if ( pOwner == this ) + { + pOwner->m_iPipebombCount--; + pEnt->AddFlag( FL_KILLME ); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "tf_gl_pipebomb" ); + } +} + + +//========================================================================= +// Stops the setting of the detpack +void CTFCPlayer::TeamFortress_DetpackStop( void ) +{ + CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DETPACKSET ); + + if (!pTimer) + return; + + ClientPrint( this, HUD_PRINTNOTIFY, "#Detpack_retrieve" ); + + // Return the detpack + GiveAmmo( 1, TFC_AMMO_DETPACK ); + Timer_Remove(pTimer); + + // Release player + m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE ); + is_detpacking = 0; + TeamFortress_SetSpeed(); + + // Return their weapon + if ( GetActiveWeapon() ) + GetActiveWeapon()->Deploy(); +} + + +//========================================================================= +// Removes any detpacks the player may have set +BOOL CTFCPlayer::TeamFortress_RemoveDetpacks( void ) +{ + // Remove all detpacks owned by the player + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "detpack" ); + while ( pEnt ) + { + // if the player owns this detpack, remove it + if ( pEnt->GetOwnerEntity() == this ) + { + UTIL_Remove( pEnt ); + return TRUE; + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "detpack" ); + } + + return FALSE; +} + + +//========================================================================= +// Remove all of an ent owned by this player +void CTFCPlayer::RemoveOwnedEnt( char *pEntName ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pEntName ); + while ( pEnt ) + { + // if the player owns this entity, remove it + if ( pEnt->GetOwnerEntity() == this ) + pEnt->AddFlag( FL_KILLME ); + + pEnt = gEntList.FindEntityByClassname( pEnt, pEntName ); + } +} + + |