Team Fortress 2 Source Code as on 22/4/2020
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.

1115 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for HL1.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tfc_player.h"
  9. #include "tfc_gamerules.h"
  10. #include "KeyValues.h"
  11. #include "viewport_panel_names.h"
  12. #include "client.h"
  13. #include "team.h"
  14. #include "weapon_tfcbase.h"
  15. #include "tfc_client.h"
  16. #include "tfc_mapitems.h"
  17. #include "tfc_timer.h"
  18. #include "tfc_engineer.h"
  19. #include "tfc_team.h"
  20. #define TFC_PLAYER_MODEL "models/player/pyro.mdl"
  21. // -------------------------------------------------------------------------------- //
  22. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  23. // -------------------------------------------------------------------------------- //
  24. class CTEPlayerAnimEvent : public CBaseTempEntity
  25. {
  26. public:
  27. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  28. DECLARE_SERVERCLASS();
  29. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  30. {
  31. }
  32. CNetworkHandle( CBasePlayer, m_hPlayer );
  33. CNetworkVar( int, m_iEvent );
  34. CNetworkVar( int, m_nData );
  35. };
  36. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  37. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  38. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED )
  39. SendPropInt( SENDINFO( m_nData ), 32 )
  40. END_SEND_TABLE()
  41. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  42. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  43. {
  44. CPVSFilter filter( pPlayer->EyePosition() );
  45. // The player himself doesn't need to be sent his animation events
  46. // unless cs_showanimstate wants to show them.
  47. if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
  48. {
  49. filter.RemoveRecipient( pPlayer );
  50. }
  51. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  52. g_TEPlayerAnimEvent.m_iEvent = event;
  53. g_TEPlayerAnimEvent.m_nData = nData;
  54. g_TEPlayerAnimEvent.Create( filter, 0 );
  55. }
  56. // -------------------------------------------------------------------------------- //
  57. // Tables.
  58. // -------------------------------------------------------------------------------- //
  59. LINK_ENTITY_TO_CLASS( player, CTFCPlayer );
  60. PRECACHE_REGISTER(player);
  61. IMPLEMENT_SERVERCLASS_ST( CTFCPlayer, DT_TFCPlayer )
  62. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  63. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  64. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  65. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  66. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  67. // cs_playeranimstate and clientside animation takes care of these on the client
  68. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  69. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  70. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
  71. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
  72. SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
  73. END_SEND_TABLE()
  74. // -------------------------------------------------------------------------------- //
  75. void cc_CreatePredictionError_f()
  76. {
  77. CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
  78. pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
  79. }
  80. ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
  81. CTFCPlayer::CTFCPlayer()
  82. {
  83. m_PlayerAnimState = CreatePlayerAnimState( this );
  84. item_list = 0;
  85. UseClientSideAnimation();
  86. m_angEyeAngles.Init();
  87. m_pCurStateInfo = NULL;
  88. m_lifeState = LIFE_DEAD; // Start "dead".
  89. SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
  90. SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
  91. }
  92. void CTFCPlayer::TFCPlayerThink()
  93. {
  94. if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
  95. (this->*m_pCurStateInfo->pfnThink)();
  96. SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
  97. }
  98. CTFCPlayer::~CTFCPlayer()
  99. {
  100. m_PlayerAnimState->Release();
  101. }
  102. CTFCPlayer *CTFCPlayer::CreatePlayer( const char *className, edict_t *ed )
  103. {
  104. CTFCPlayer::s_PlayerEdict = ed;
  105. return (CTFCPlayer*)CreateEntityByName( className );
  106. }
  107. void CTFCPlayer::PostThink()
  108. {
  109. BaseClass::PostThink();
  110. QAngle angles = GetLocalAngles();
  111. angles[PITCH] = 0;
  112. SetLocalAngles( angles );
  113. // Store the eye angles pitch so the client can compute its animation state correctly.
  114. m_angEyeAngles = EyeAngles();
  115. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  116. }
  117. void CTFCPlayer::Precache()
  118. {
  119. for ( int i=0; i < PC_LASTCLASS; i++ )
  120. PrecacheModel( GetTFCClassInfo( i )->m_pModelName );
  121. PrecacheScriptSound( "Player.Spawn" );
  122. BaseClass::Precache();
  123. }
  124. void CTFCPlayer::InitialSpawn( void )
  125. {
  126. BaseClass::InitialSpawn();
  127. State_Enter( STATE_WELCOME );
  128. }
  129. void CTFCPlayer::Spawn()
  130. {
  131. SetModel( GetTFCClassInfo( m_Shared.GetPlayerClass() )->m_pModelName );
  132. SetMoveType( MOVETYPE_WALK );
  133. m_iLegDamage = 0;
  134. BaseClass::Spawn();
  135. // Kind of lame, but CBasePlayer::Spawn resets a lot of the state that we initially want on.
  136. // So if we're in the welcome state, call its enter function to reset
  137. if ( m_Shared.State_Get() == STATE_WELCOME )
  138. {
  139. State_Enter_WELCOME();
  140. }
  141. // If they were dead, then they're respawning. Put them in the active state.
  142. if ( m_Shared.State_Get() == STATE_DYING )
  143. {
  144. State_Transition( STATE_ACTIVE );
  145. }
  146. // If they're spawning into the world as fresh meat, give them items and stuff.
  147. if ( m_Shared.State_Get() == STATE_ACTIVE )
  148. {
  149. EmitSound( "Player.Spawn" );
  150. GiveDefaultItems();
  151. }
  152. }
  153. void CTFCPlayer::ForceRespawn()
  154. {
  155. //TFCTODO: goldsrc tfc has a big function for this.. doing what I'm doing here may not work.
  156. respawn( this, false );
  157. }
  158. void CTFCPlayer::GiveDefaultItems()
  159. {
  160. switch( m_Shared.GetPlayerClass() )
  161. {
  162. case PC_HWGUY:
  163. {
  164. GiveNamedItem( "weapon_crowbar" );
  165. GiveNamedItem( "weapon_minigun" );
  166. GiveAmmo( 176, TFC_AMMO_SHELLS );
  167. }
  168. break;
  169. case PC_PYRO:
  170. {
  171. GiveNamedItem( "weapon_crowbar" );
  172. }
  173. break;
  174. case PC_ENGINEER:
  175. {
  176. GiveNamedItem( "weapon_spanner" );
  177. GiveNamedItem( "weapon_super_shotgun" );
  178. GiveAmmo( 20, TFC_AMMO_SHELLS );
  179. }
  180. break;
  181. case PC_SCOUT:
  182. {
  183. GiveNamedItem( "weapon_crowbar" );
  184. GiveNamedItem( "weapon_shotgun" );
  185. GiveNamedItem( "weapon_nailgun" );
  186. GiveAmmo( 25, TFC_AMMO_SHELLS );
  187. GiveAmmo( 100, TFC_AMMO_NAILS );
  188. }
  189. break;
  190. case PC_SNIPER:
  191. {
  192. GiveNamedItem( "weapon_crowbar" );
  193. }
  194. break;
  195. case PC_SOLDIER:
  196. {
  197. GiveNamedItem( "weapon_crowbar" );
  198. }
  199. break;
  200. case PC_DEMOMAN:
  201. {
  202. GiveNamedItem( "weapon_crowbar" );
  203. }
  204. break;
  205. case PC_SPY:
  206. {
  207. GiveNamedItem( "weapon_knife" );
  208. }
  209. break;
  210. case PC_MEDIC:
  211. {
  212. GiveNamedItem( "weapon_medikit" );
  213. GiveNamedItem( "weapon_super_nailgun" );
  214. GiveAmmo( 100, TFC_AMMO_NAILS );
  215. }
  216. break;
  217. }
  218. }
  219. void CTFCPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  220. {
  221. m_PlayerAnimState->DoAnimationEvent( event, nData );
  222. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  223. }
  224. void CTFCPlayer::State_Transition( TFCPlayerState newState )
  225. {
  226. State_Leave();
  227. State_Enter( newState );
  228. }
  229. void CTFCPlayer::State_Enter( TFCPlayerState newState )
  230. {
  231. m_Shared.m_iPlayerState = newState;
  232. m_pCurStateInfo = State_LookupInfo( newState );
  233. // Initialize the new state.
  234. if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
  235. (this->*m_pCurStateInfo->pfnEnterState)();
  236. }
  237. void CTFCPlayer::State_Leave()
  238. {
  239. if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
  240. {
  241. (this->*m_pCurStateInfo->pfnLeaveState)();
  242. }
  243. }
  244. CPlayerStateInfo* CTFCPlayer::State_LookupInfo( TFCPlayerState state )
  245. {
  246. // This table MUST match the
  247. static CPlayerStateInfo playerStateInfos[] =
  248. {
  249. { STATE_ACTIVE, "STATE_ACTIVE", &CTFCPlayer::State_Enter_ACTIVE, NULL, NULL },
  250. { STATE_WELCOME, "STATE_WELCOME", &CTFCPlayer::State_Enter_WELCOME, NULL, NULL },
  251. { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CTFCPlayer::State_Enter_PICKINGTEAM, NULL, NULL },
  252. { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CTFCPlayer::State_Enter_PICKINGCLASS, NULL, NULL },
  253. { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CTFCPlayer::State_Enter_OBSERVER_MODE, NULL, NULL },
  254. { STATE_DYING, "STATE_DYING", &CTFCPlayer::State_Enter_DYING, NULL, NULL }
  255. };
  256. for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
  257. {
  258. if ( playerStateInfos[i].m_iPlayerState == state )
  259. return &playerStateInfos[i];
  260. }
  261. return NULL;
  262. }
  263. void CTFCPlayer::State_Enter_WELCOME()
  264. {
  265. SetMoveType( MOVETYPE_NONE );
  266. AddEffects( EF_NODRAW );
  267. AddSolidFlags( FSOLID_NOT_SOLID );
  268. PhysObjectSleep();
  269. // Show info panel (if it's not a simple demo map).
  270. KeyValues *data = new KeyValues("data");
  271. data->SetString( "title", "Message of the Day" ); // info panel title
  272. data->SetString( "type", "3" ); // show a file
  273. data->SetString( "msg", "motd.txt" ); // this file
  274. data->SetString( "cmd", "joingame" ); // exec this command if panel closed
  275. ShowViewPortPanel( "info", true, data );
  276. data->deleteThis();
  277. }
  278. void CTFCPlayer::State_Enter_PICKINGTEAM()
  279. {
  280. ShowViewPortPanel( PANEL_TEAM ); // show the team menu
  281. }
  282. void CTFCPlayer::State_Enter_PICKINGCLASS()
  283. {
  284. // go to spec mode, if dying keep deathcam
  285. if ( GetObserverMode() == OBS_MODE_DEATHCAM )
  286. {
  287. StartObserverMode( OBS_MODE_DEATHCAM );
  288. }
  289. else
  290. {
  291. StartObserverMode( OBS_MODE_ROAMING );
  292. }
  293. PhysObjectSleep();
  294. // show the class menu:
  295. ShowViewPortPanel( PANEL_CLASS );
  296. }
  297. void CTFCPlayer::State_Enter_OBSERVER_MODE()
  298. {
  299. StartObserverMode( m_iObserverLastMode );
  300. PhysObjectSleep();
  301. }
  302. void CTFCPlayer::State_Enter_ACTIVE()
  303. {
  304. SetMoveType( MOVETYPE_WALK );
  305. RemoveEffects( EF_NODRAW );
  306. RemoveSolidFlags( FSOLID_NOT_SOLID );
  307. m_Local.m_iHideHUD = 0;
  308. PhysObjectWake();
  309. }
  310. void CTFCPlayer::State_Enter_DYING()
  311. {
  312. SetMoveType( MOVETYPE_NONE );
  313. AddSolidFlags( FSOLID_NOT_SOLID );
  314. }
  315. void CTFCPlayer::PhysObjectSleep()
  316. {
  317. IPhysicsObject *pObj = VPhysicsGetObject();
  318. if ( pObj )
  319. pObj->Sleep();
  320. }
  321. void CTFCPlayer::PhysObjectWake()
  322. {
  323. IPhysicsObject *pObj = VPhysicsGetObject();
  324. if ( pObj )
  325. pObj->Wake();
  326. }
  327. void CTFCPlayer::HandleCommand_JoinTeam( const char *pTeamName )
  328. {
  329. int iTeam = TEAM_RED;
  330. if ( stricmp( pTeamName, "auto" ) == 0 )
  331. {
  332. iTeam = RandomInt( 0, 1 ) ? TEAM_RED : TEAM_BLUE;
  333. }
  334. else if ( stricmp( pTeamName, "spectate" ) == 0 )
  335. {
  336. iTeam = TEAM_SPECTATOR;
  337. }
  338. else
  339. {
  340. for ( int i=0; i < TEAM_MAXCOUNT; i++ )
  341. {
  342. if ( stricmp( pTeamName, teamnames[i] ) == 0 )
  343. {
  344. iTeam = i;
  345. break;
  346. }
  347. }
  348. }
  349. if ( iTeam == TEAM_SPECTATOR )
  350. {
  351. // Prevent this is the cvar is set
  352. if ( !mp_allowspectators.GetInt() && !IsHLTV() )
  353. {
  354. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
  355. return;
  356. }
  357. if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() )
  358. {
  359. CommitSuicide();
  360. // add 1 to frags to balance out the 1 subtracted for killing yourself
  361. IncrementFragCount( 1 );
  362. }
  363. ChangeTeam( TEAM_SPECTATOR );
  364. // do we have fadetoblack on? (need to fade their screen back in)
  365. if ( mp_fadetoblack.GetInt() )
  366. {
  367. color32_s clr = { 0,0,0,0 };
  368. UTIL_ScreenFade( this, clr, 0.001, 0, FFADE_IN );
  369. }
  370. }
  371. else
  372. {
  373. ChangeTeam( iTeam );
  374. State_Transition( STATE_PICKINGCLASS );
  375. }
  376. }
  377. void CTFCPlayer::ChangeTeam( int iTeamNum )
  378. {
  379. if ( !GetGlobalTeam( iTeamNum ) )
  380. {
  381. Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
  382. return;
  383. }
  384. int iOldTeam = GetTeamNumber();
  385. // if this is our current team, just abort
  386. if ( iTeamNum == iOldTeam )
  387. return;
  388. BaseClass::ChangeTeam( iTeamNum );
  389. if ( iTeamNum == TEAM_UNASSIGNED )
  390. {
  391. State_Transition( STATE_OBSERVER_MODE );
  392. }
  393. else if ( iTeamNum == TEAM_SPECTATOR )
  394. {
  395. State_Transition( STATE_OBSERVER_MODE );
  396. }
  397. else // active player
  398. {
  399. if ( iOldTeam == TEAM_SPECTATOR )
  400. {
  401. // If they're switching from being a spectator to ingame player
  402. GetIntoGame();
  403. }
  404. if ( !IsDead() && iOldTeam != TEAM_UNASSIGNED )
  405. {
  406. // Kill player if switching teams while alive
  407. CommitSuicide();
  408. }
  409. // Put up the class selection menu.
  410. State_Transition( STATE_PICKINGCLASS );
  411. }
  412. }
  413. void CTFCPlayer::HandleCommand_JoinClass( const char *pClassName )
  414. {
  415. int iClass = RandomInt( 0, PC_LAST_NORMAL_CLASS );
  416. if ( stricmp( pClassName, "random" ) != 0 )
  417. {
  418. for ( int i=0; i < PC_LASTCLASS; i++ )
  419. {
  420. if ( stricmp( pClassName, GetTFCClassInfo( i )->m_pClassName ) == 0 )
  421. {
  422. iClass = i;
  423. break;
  424. }
  425. }
  426. if ( i == PC_LAST_NORMAL_CLASS )
  427. {
  428. Warning( "HandleCommand_JoinClass( %s ) - invalid class name.\n", pClassName );
  429. }
  430. }
  431. m_Shared.SetPlayerClass( iClass );
  432. if ( !IsAlive() )
  433. GetIntoGame();
  434. }
  435. void CTFCPlayer::GetIntoGame()
  436. {
  437. State_Transition( STATE_ACTIVE );
  438. Spawn();
  439. }
  440. bool CTFCPlayer::ClientCommand( const CCommand& args )
  441. {
  442. const char *pcmd = args[0];
  443. if ( FStrEq( pcmd, "joingame" ) )
  444. {
  445. // player just closed MOTD dialog
  446. if ( m_Shared.m_iPlayerState == STATE_WELCOME )
  447. {
  448. State_Transition( STATE_PICKINGTEAM );
  449. }
  450. return true;
  451. }
  452. else if ( FStrEq( pcmd, "jointeam" ) )
  453. {
  454. if ( args.ArgC() >= 2 )
  455. {
  456. HandleCommand_JoinTeam( args[1] );
  457. return true;
  458. }
  459. }
  460. else if ( FStrEq( pcmd, "joinclass" ) )
  461. {
  462. if ( args.ArgC() < 2 )
  463. {
  464. Warning( "Player sent bad joinclass syntax\n" );
  465. }
  466. HandleCommand_JoinClass( args[1] );
  467. return true;
  468. }
  469. return BaseClass::ClientCommand( args );
  470. }
  471. bool CTFCPlayer::IsAlly( CBaseEntity *pEnt ) const
  472. {
  473. return pEnt->GetTeamNumber() == GetTeamNumber();
  474. }
  475. void CTFCPlayer::TF_AddFrags( int nFrags )
  476. {
  477. // TFCTODO: implement frags
  478. }
  479. void CTFCPlayer::ResetMenu()
  480. {
  481. current_menu = 0;
  482. }
  483. int CTFCPlayer::GetNumFlames() const
  484. {
  485. // TFCTODO: implement flames
  486. return 0;
  487. }
  488. void CTFCPlayer::SetNumFlames( int nFlames )
  489. {
  490. // TFCTODO: implement frags
  491. Assert( 0 );
  492. }
  493. int CTFCPlayer::TakeHealth( float flHealth, int bitsDamageType )
  494. {
  495. int bResult = false;
  496. // If the bit's set, ignore the monster's max health and add over it
  497. if ( bitsDamageType & DMG_IGNORE_MAXHEALTH )
  498. {
  499. int iDamage = g_pGameRules->Damage_GetTimeBased();
  500. m_bitsDamageType &= ~(bitsDamageType & ~iDamage);
  501. m_iHealth += flHealth;
  502. bResult = true;
  503. }
  504. else
  505. {
  506. bResult = BaseClass::TakeHealth( flHealth, bitsDamageType );
  507. }
  508. // Leg Healing
  509. if (m_iLegDamage > 0)
  510. {
  511. // Allow even at full health
  512. if ( GetHealth() >= (GetMaxHealth() - 5))
  513. m_iLegDamage = 0;
  514. else
  515. m_iLegDamage -= (GetHealth() + flHealth) / 20;
  516. if (m_iLegDamage < 1)
  517. m_iLegDamage = 0;
  518. TeamFortress_SetSpeed();
  519. bResult = true;
  520. }
  521. return bResult;
  522. }
  523. void CTFCPlayer::TeamFortress_SetSpeed()
  524. {
  525. int playerclass = m_Shared.GetPlayerClass();
  526. float maxfbspeed;
  527. // Spectators can move while in Classic Observer mode
  528. if ( IsObserver() )
  529. {
  530. if ( GetObserverMode() == OBS_MODE_ROAMING )
  531. SetMaxSpeed( GetTFCClassInfo( PC_SCOUT )->m_flMaxSpeed );
  532. else
  533. SetMaxSpeed( 0 );
  534. return;
  535. }
  536. // Check for any reason why they can't move at all
  537. if ( (m_Shared.GetStateFlags() & TFSTATE_CANT_MOVE) || (playerclass == PC_UNDEFINED) )
  538. {
  539. SetAbsVelocity( vec3_origin );
  540. SetMaxSpeed( 1 );
  541. return;
  542. }
  543. // First, get their max class speed
  544. maxfbspeed = GetTFCClassInfo( playerclass )->m_flMaxSpeed;
  545. // 2nd, see if any GoalItems are slowing them down
  546. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
  547. while ( pEnt )
  548. {
  549. CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt );
  550. if ( pGoal )
  551. {
  552. if ( pGoal->GetOwnerEntity() == this )
  553. {
  554. if (pGoal->goal_activation & TFGI_SLOW)
  555. {
  556. maxfbspeed = maxfbspeed / 2;
  557. }
  558. else if (pGoal->speed_reduction)
  559. {
  560. float flPercent = ((float)pGoal->speed_reduction) / 100.0;
  561. maxfbspeed = flPercent * maxfbspeed;
  562. }
  563. }
  564. }
  565. pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
  566. }
  567. // 3rd, See if they're tranquilised
  568. if (m_Shared.GetStateFlags() & TFSTATE_TRANQUILISED)
  569. {
  570. maxfbspeed = maxfbspeed / 2;
  571. }
  572. // 4th, check for leg wounds
  573. if (m_iLegDamage)
  574. {
  575. if (m_iLegDamage > 6)
  576. m_iLegDamage = 6;
  577. // reduce speed by 10% per leg wound
  578. maxfbspeed = (maxfbspeed * ((10 - m_iLegDamage) / 10));
  579. }
  580. // 5th, if they're a sniper, and they're aiming, their speed must be 80 or less
  581. if (m_Shared.GetStateFlags() & TFSTATE_AIMING)
  582. {
  583. if (maxfbspeed > 80)
  584. maxfbspeed = 80;
  585. }
  586. // Set the speed
  587. SetMaxSpeed( maxfbspeed );
  588. }
  589. void CTFCPlayer::Event_Killed( const CTakeDamageInfo &info )
  590. {
  591. DoAnimationEvent( PLAYERANIMEVENT_DIE );
  592. State_Transition( STATE_DYING ); // Transition into the dying state.
  593. // Remove all items..
  594. RemoveAllItems( true );
  595. BaseClass::Event_Killed( info );
  596. // Don't overflow the value for this.
  597. m_iHealth = 0;
  598. }
  599. void CTFCPlayer::ClientHearVox( const char *pSentence )
  600. {
  601. //TFCTODO: implement this.
  602. }
  603. //=========================================================================
  604. // Check all stats to make sure they're good for this class
  605. void CTFCPlayer::TeamFortress_CheckClassStats()
  606. {
  607. // Check armor
  608. if (armortype > armor_allowed)
  609. armortype = armor_allowed;
  610. if (ArmorValue() > GetClassInfo()->m_iMaxArmor)
  611. SetArmorValue( GetClassInfo()->m_iMaxArmor );
  612. if (ArmorValue() < 0)
  613. SetArmorValue( 0 );
  614. if (armortype < 0)
  615. armortype = 0;
  616. // Check ammo
  617. for ( int iAmmoType=0; iAmmoType < TFC_NUM_AMMO_TYPES; iAmmoType++ )
  618. {
  619. if ( GetAmmoCount( iAmmoType ) > GetClassInfo()->m_MaxAmmo[iAmmoType] )
  620. RemoveAmmo( GetAmmoCount( iAmmoType ) - GetClassInfo()->m_MaxAmmo[iAmmoType], iAmmoType );
  621. }
  622. // Check Grenades
  623. Assert( GetAmmoCount( TFC_AMMO_GRENADES1 ) >= 0 );
  624. Assert( GetAmmoCount( TFC_AMMO_GRENADES2 ) >= 0 );
  625. // Limit Nails
  626. if ( no_grenades_1() > g_nMaxGrenades[tp_grenades_1()] )
  627. RemoveAmmo( TFC_AMMO_GRENADES1, no_grenades_1() - g_nMaxGrenades[tp_grenades_1()] );
  628. if ( no_grenades_2() > g_nMaxGrenades[tp_grenades_2()] )
  629. RemoveAmmo( TFC_AMMO_GRENADES2, no_grenades_2() - g_nMaxGrenades[tp_grenades_2()] );
  630. // Check health
  631. if (GetHealth() > GetMaxHealth() && !(m_Shared.GetItemFlags() & IT_SUPERHEALTH))
  632. SetHealth( GetMaxHealth() );
  633. if (GetHealth() < 0)
  634. SetHealth( 0 );
  635. // Update armor picture
  636. m_Shared.RemoveItemFlags( IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3 );
  637. if (armortype >= 0.8)
  638. m_Shared.AddItemFlags( IT_ARMOR3 );
  639. else if (armortype >= 0.6)
  640. m_Shared.AddItemFlags( IT_ARMOR2 );
  641. else if (armortype >= 0.3)
  642. m_Shared.AddItemFlags( IT_ARMOR1 );
  643. }
  644. //======================================================================
  645. // DISGUISE HANDLING
  646. //======================================================================
  647. // Reset spy skin and color or remove invisibility
  648. void CTFCPlayer::Spy_RemoveDisguise()
  649. {
  650. if (m_Shared.GetPlayerClass() == PC_SPY)
  651. {
  652. if ( undercover_team || undercover_skin )
  653. ClientPrint( this, HUD_PRINTCENTER, "#Disguise_Lost" );
  654. // Set their color
  655. undercover_team = 0;
  656. undercover_skin = 0;
  657. immune_to_check = gpGlobals->curtime + 10;
  658. is_undercover = 0;
  659. // undisguise weapon
  660. TeamFortress_SetSkin();
  661. TeamFortress_SpyCalcName();
  662. Spy_ResetExternalWeaponModel();
  663. // get them out of any disguise menus
  664. if ( current_menu == MENU_SPY || current_menu == MENU_SPY_SKIN || current_menu == MENU_SPY_COLOR )
  665. {
  666. ResetMenu();
  667. }
  668. // Remove the Disguise timer
  669. CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DISGUISE );
  670. if (pTimer)
  671. {
  672. ClientPrint( this, HUD_PRINTCENTER, "#Disguise_stop" );
  673. Timer_Remove( pTimer );
  674. }
  675. }
  676. }
  677. // when the spy loses disguise reset his weapon
  678. void CTFCPlayer::Spy_ResetExternalWeaponModel( void )
  679. {
  680. // we don't show any weapon models if we're feigning
  681. if ( is_feigning )
  682. return;
  683. #ifdef TFCTODO // spy
  684. pev->weaponmodel = MAKE_STRING( m_pszSavedWeaponModel );
  685. strcpy( m_szAnimExtention, m_szSavedAnimExtention );
  686. m_iCurrentAnimationState = 0; // force the current animation sequence to be recalculated
  687. #endif
  688. }
  689. //=========================================================================
  690. // Try and find the player's name who's skin and team closest fit the
  691. // current disguise of the spy
  692. void CTFCPlayer::TeamFortress_SpyCalcName()
  693. {
  694. CBaseEntity *last_target = undercover_target;// don't redisguise self as this person
  695. undercover_target = NULL;
  696. // Find a player on the team the spy is disguised as to pretend to be
  697. if (undercover_team != 0)
  698. {
  699. CTFCPlayer *pPlayer = NULL;
  700. // Loop through players
  701. int i;
  702. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  703. {
  704. pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
  705. if ( pPlayer )
  706. {
  707. if ( pPlayer == last_target )
  708. {
  709. // choose someone else, we're trying to rid ourselves of a disguise as this one
  710. continue;
  711. }
  712. // First, try to find a player with same color and skins
  713. if (pPlayer->GetTeamNumber() == undercover_team && pPlayer->m_Shared.GetPlayerClass() == undercover_skin)
  714. {
  715. undercover_target = pPlayer;
  716. return;
  717. }
  718. }
  719. }
  720. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  721. {
  722. pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
  723. if ( pPlayer )
  724. {
  725. if (pPlayer->GetTeamNumber() == undercover_team)
  726. {
  727. undercover_target = pPlayer;
  728. return;
  729. }
  730. }
  731. }
  732. }
  733. }
  734. //=========================================================================
  735. // Set the skin of a player based on his/her class
  736. void CTFCPlayer::TeamFortress_SetSkin()
  737. {
  738. immune_to_check = gpGlobals->curtime + 10;
  739. // Find out whether we should show our actual class or a disguised class
  740. int iClassToUse = m_Shared.GetPlayerClass();
  741. if (iClassToUse == PC_SPY && undercover_skin != 0)
  742. iClassToUse = undercover_skin;
  743. int iTeamToUse = GetTeamNumber();
  744. if (m_Shared.GetPlayerClass() == PC_SPY && undercover_team != 0)
  745. iTeamToUse = undercover_team;
  746. // TFCTODO: handle replacement_model here.
  747. SetModel( GetTFCClassInfo( iClassToUse )->m_pModelName );
  748. // Skins in the models should be setup using the team IDs in tfc_shareddefs.h, subtracting 1
  749. // so they're 0-based.
  750. m_nSkin = iTeamToUse - 1;
  751. if ( FBitSet(GetFlags(), FL_DUCKING) )
  752. UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
  753. else
  754. UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
  755. }
  756. //=========================================================================
  757. // Displays the state of the items specified by the Goal passed in
  758. void CTFCPlayer::DisplayLocalItemStatus( CTFGoal *pGoal )
  759. {
  760. for (int i = 0; i < 4; i++)
  761. {
  762. if (pGoal->display_item_status[i] != 0)
  763. {
  764. CTFGoalItem *pItem = Finditem(pGoal->display_item_status[i]);
  765. if (pItem)
  766. DisplayItemStatus(pGoal, this, pItem);
  767. else
  768. ClientPrint( this, HUD_PRINTTALK, "#Item_missing" );
  769. }
  770. }
  771. }
  772. //=========================================================================
  773. // Removes all the Engineer's buildings
  774. void CTFCPlayer::Engineer_RemoveBuildings()
  775. {
  776. // If the player's building already, stop
  777. if (is_building == 1)
  778. {
  779. m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
  780. TeamFortress_SetSpeed();
  781. // Remove the timer
  782. CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_BUILD );
  783. if (pTimer)
  784. Timer_Remove(pTimer);
  785. // Remove the building
  786. UTIL_Remove( building );
  787. building = NULL;
  788. is_building = 0;
  789. // Stop Build Sound
  790. StopSound( "Engineer.Building" );
  791. //STOP_SOUND( ENT(pev), CHAN_STATIC, "weapons/building.wav" );
  792. if ( GetActiveWeapon() )
  793. GetActiveWeapon()->Deploy();
  794. }
  795. DestroyBuilding(this, "building_dispenser");
  796. DestroyBuilding(this, "building_sentrygun");
  797. DestroyTeleporter(this, BUILD_TELEPORTER_ENTRY);
  798. DestroyTeleporter(this, BUILD_TELEPORTER_EXIT);
  799. }
  800. //=========================================================================
  801. // Removes all grenades that persist for a period of time from the world
  802. void CTFCPlayer::TeamFortress_RemoveLiveGrenades( void )
  803. {
  804. RemoveOwnedEnt( "tf_weapon_napalmgrenade" );
  805. RemoveOwnedEnt( "tf_weapon_nailgrenade" );
  806. RemoveOwnedEnt( "tf_weapon_gasgrenade" );
  807. RemoveOwnedEnt( "tf_weapon_caltrop" );
  808. }
  809. //=========================================================================
  810. // Removes all rockets the player has fired into the world
  811. // (this prevents a team kill cheat where players would fire rockets
  812. // then change teams to kill their own team)
  813. void CTFCPlayer::TeamFortress_RemoveRockets( void )
  814. {
  815. RemoveOwnedEnt( "tf_rpg_rocket" );
  816. RemoveOwnedEnt( "tf_ic_rocket" );
  817. }
  818. // removes the player's pipebombs with no explosions
  819. void CTFCPlayer::RemovePipebombs( void )
  820. {
  821. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "tf_gl_pipebomb" );
  822. while ( pEnt )
  823. {
  824. CTFCPlayer *pOwner = ToTFCPlayer( pEnt->GetOwnerEntity() );
  825. if ( pOwner == this )
  826. {
  827. pOwner->m_iPipebombCount--;
  828. pEnt->AddFlag( FL_KILLME );
  829. }
  830. pEnt = gEntList.FindEntityByClassname( pEnt, "tf_gl_pipebomb" );
  831. }
  832. }
  833. //=========================================================================
  834. // Stops the setting of the detpack
  835. void CTFCPlayer::TeamFortress_DetpackStop( void )
  836. {
  837. CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DETPACKSET );
  838. if (!pTimer)
  839. return;
  840. ClientPrint( this, HUD_PRINTNOTIFY, "#Detpack_retrieve" );
  841. // Return the detpack
  842. GiveAmmo( 1, TFC_AMMO_DETPACK );
  843. Timer_Remove(pTimer);
  844. // Release player
  845. m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
  846. is_detpacking = 0;
  847. TeamFortress_SetSpeed();
  848. // Return their weapon
  849. if ( GetActiveWeapon() )
  850. GetActiveWeapon()->Deploy();
  851. }
  852. //=========================================================================
  853. // Removes any detpacks the player may have set
  854. BOOL CTFCPlayer::TeamFortress_RemoveDetpacks( void )
  855. {
  856. // Remove all detpacks owned by the player
  857. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "detpack" );
  858. while ( pEnt )
  859. {
  860. // if the player owns this detpack, remove it
  861. if ( pEnt->GetOwnerEntity() == this )
  862. {
  863. UTIL_Remove( pEnt );
  864. return TRUE;
  865. }
  866. pEnt = gEntList.FindEntityByClassname( pEnt, "detpack" );
  867. }
  868. return FALSE;
  869. }
  870. //=========================================================================
  871. // Remove all of an ent owned by this player
  872. void CTFCPlayer::RemoveOwnedEnt( char *pEntName )
  873. {
  874. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pEntName );
  875. while ( pEnt )
  876. {
  877. // if the player owns this entity, remove it
  878. if ( pEnt->GetOwnerEntity() == this )
  879. pEnt->AddFlag( FL_KILLME );
  880. pEnt = gEntList.FindEntityByClassname( pEnt, pEntName );
  881. }
  882. }