//========= 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( 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 ); } }