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.

636 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Multiplayer Player for HL1.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "hl1mp_player.h"
  9. #include "client.h"
  10. #include "team.h"
  11. class CTEPlayerAnimEvent : public CBaseTempEntity
  12. {
  13. public:
  14. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  15. DECLARE_SERVERCLASS();
  16. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  17. {
  18. }
  19. CNetworkHandle( CBasePlayer, m_hPlayer );
  20. CNetworkVar( int, m_iEvent );
  21. CNetworkVar( int, m_nData );
  22. };
  23. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  24. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  25. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  26. SendPropInt( SENDINFO( m_nData ), 32 )
  27. END_SEND_TABLE()
  28. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  29. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  30. {
  31. CPVSFilter filter( pPlayer->EyePosition() );
  32. // The player himself doesn't need to be sent his animation events
  33. // unless cs_showanimstate wants to show them.
  34. // if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
  35. {
  36. // filter.RemoveRecipient( pPlayer );
  37. }
  38. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  39. g_TEPlayerAnimEvent.m_iEvent = event;
  40. g_TEPlayerAnimEvent.m_nData = nData;
  41. g_TEPlayerAnimEvent.Create( filter, 0 );
  42. }
  43. //////////////////////////////////////////////////////////////////////////////////////////
  44. extern int gEvilImpulse101;
  45. LINK_ENTITY_TO_CLASS( player_mp, CHL1MP_Player );
  46. PRECACHE_REGISTER( player_mp );
  47. IMPLEMENT_SERVERCLASS_ST( CHL1MP_Player, DT_HL1MP_PLAYER )
  48. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  49. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  50. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  51. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  52. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  53. // cs_playeranimstate and clientside animation takes care of these on the client
  54. // SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  55. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  56. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
  57. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
  58. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  59. SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
  60. SendPropInt( SENDINFO( m_iRealSequence ), 9 ),
  61. // SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
  62. END_SEND_TABLE()
  63. void cc_CreatePredictionError_f()
  64. {
  65. CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
  66. pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
  67. }
  68. ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
  69. static const char * s_szModelPath = "models/player/mp/";
  70. CHL1MP_Player::CHL1MP_Player()
  71. {
  72. m_PlayerAnimState = CreatePlayerAnimState( this );
  73. // item_list = 0;
  74. UseClientSideAnimation();
  75. m_angEyeAngles.Init();
  76. // m_pCurStateInfo = NULL;
  77. m_lifeState = LIFE_DEAD; // Start "dead".
  78. m_iSpawnInterpCounter = 0;
  79. m_flNextModelChangeTime = 0;
  80. m_flNextTeamChangeTime = 0;
  81. // SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
  82. // SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
  83. }
  84. CHL1MP_Player::~CHL1MP_Player()
  85. {
  86. m_PlayerAnimState->Release();
  87. }
  88. void CHL1MP_Player::PostThink( void )
  89. {
  90. BaseClass::PostThink();
  91. QAngle angles = GetLocalAngles();
  92. angles[PITCH] = 0;
  93. SetLocalAngles( angles );
  94. // Store the eye angles pitch so the client can compute its animation state correctly.
  95. m_angEyeAngles = EyeAngles();
  96. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  97. }
  98. void CHL1MP_Player::Spawn( void )
  99. {
  100. if ( !IsObserver() )
  101. {
  102. RemoveEffects( EF_NODRAW );
  103. SetMoveType( MOVETYPE_WALK );
  104. RemoveSolidFlags( FSOLID_NOT_SOLID );
  105. // if no model, force one
  106. if ( !GetModelPtr() )
  107. SetModel( "models/player/mp/gordon/gordon.mdl" );
  108. }
  109. m_flNextModelChangeTime = 0;
  110. m_flNextTeamChangeTime = 0;
  111. BaseClass::Spawn();
  112. if ( !IsObserver() )
  113. {
  114. GiveDefaultItems();
  115. SetPlayerModel();
  116. }
  117. m_bHasLongJump = false;
  118. m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
  119. }
  120. void CHL1MP_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  121. {
  122. m_PlayerAnimState->DoAnimationEvent( event, nData );
  123. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  124. }
  125. void CHL1MP_Player::GiveDefaultItems( void )
  126. {
  127. GiveNamedItem( "weapon_crowbar" );
  128. GiveNamedItem( "weapon_glock" );
  129. CBasePlayer::GiveAmmo( 68, "9mmRound" );
  130. }
  131. void CHL1MP_Player::UpdateOnRemove( void )
  132. {
  133. if ( m_hRagdoll )
  134. {
  135. UTIL_RemoveImmediate( m_hRagdoll );
  136. m_hRagdoll = NULL;
  137. }
  138. BaseClass::UpdateOnRemove();
  139. }
  140. void CHL1MP_Player::DetonateSatchelCharges( void )
  141. {
  142. CBaseEntity *pSatchel = NULL;
  143. while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL)
  144. {
  145. if ( pSatchel->GetOwnerEntity() == this )
  146. {
  147. pSatchel->Use( this, this, USE_ON, 0 );
  148. }
  149. }
  150. }
  151. void CHL1MP_Player::Event_Killed( const CTakeDamageInfo &info )
  152. {
  153. DoAnimationEvent( PLAYERANIMEVENT_DIE );
  154. // SetNumAnimOverlays( 0 );
  155. // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
  156. // because we still want to transmit to the clients in our PVS.
  157. if ( !IsHLTV() )
  158. CreateRagdollEntity();
  159. DetonateSatchelCharges();
  160. BaseClass::Event_Killed( info );
  161. m_lifeState = LIFE_DEAD;
  162. RemoveEffects( EF_NODRAW ); // still draw player body
  163. }
  164. void CHL1MP_Player::SetAnimation( PLAYER_ANIM playerAnim )
  165. {
  166. // BaseClass::SetAnimation( playerAnim );
  167. if ( playerAnim == PLAYER_ATTACK1 )
  168. {
  169. DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN );
  170. }
  171. int animDesired;
  172. char szAnim[64];
  173. float speed;
  174. speed = GetAbsVelocity().Length2D();
  175. if (GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
  176. {
  177. speed = 0;
  178. playerAnim = PLAYER_IDLE;
  179. }
  180. if ( playerAnim == PLAYER_ATTACK1 )
  181. {
  182. if ( speed > 0 )
  183. {
  184. playerAnim = PLAYER_WALK;
  185. }
  186. else
  187. {
  188. playerAnim = PLAYER_IDLE;
  189. }
  190. }
  191. Activity idealActivity = ACT_WALK;// TEMP!!!!!
  192. // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
  193. if (playerAnim == PLAYER_JUMP)
  194. {
  195. idealActivity = ACT_HOP;
  196. }
  197. else if (playerAnim == PLAYER_SUPERJUMP)
  198. {
  199. idealActivity = ACT_LEAP;
  200. }
  201. else if (playerAnim == PLAYER_DIE)
  202. {
  203. if ( m_lifeState == LIFE_ALIVE )
  204. {
  205. idealActivity = ACT_DIERAGDOLL;
  206. }
  207. }
  208. else if (playerAnim == PLAYER_ATTACK1)
  209. {
  210. if ( GetActivity() == ACT_HOVER ||
  211. GetActivity() == ACT_SWIM ||
  212. GetActivity() == ACT_HOP ||
  213. GetActivity() == ACT_LEAP ||
  214. GetActivity() == ACT_DIESIMPLE )
  215. {
  216. idealActivity = GetActivity();
  217. }
  218. else
  219. {
  220. idealActivity = ACT_RANGE_ATTACK1;
  221. }
  222. }
  223. else if (playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK)
  224. {
  225. if ( !( GetFlags() & FL_ONGROUND ) && (GetActivity() == ACT_HOP || GetActivity() == ACT_LEAP) ) // Still jumping
  226. {
  227. idealActivity = GetActivity();
  228. }
  229. else if ( GetWaterLevel() > 1 )
  230. {
  231. if ( speed == 0 )
  232. idealActivity = ACT_HOVER;
  233. else
  234. idealActivity = ACT_SWIM;
  235. }
  236. else if ( speed > 0 )
  237. {
  238. idealActivity = ACT_WALK;
  239. }
  240. else
  241. {
  242. idealActivity = ACT_IDLE;
  243. }
  244. }
  245. if (idealActivity == ACT_RANGE_ATTACK1)
  246. {
  247. if ( GetFlags() & FL_DUCKING ) // crouching
  248. {
  249. Q_strncpy( szAnim, "crouch_shoot_" ,sizeof(szAnim));
  250. }
  251. else
  252. {
  253. Q_strncpy( szAnim, "ref_shoot_" ,sizeof(szAnim));
  254. }
  255. Q_strncat( szAnim, m_szAnimExtension ,sizeof(szAnim), COPY_ALL_CHARACTERS );
  256. animDesired = LookupSequence( szAnim );
  257. if (animDesired == -1)
  258. animDesired = 0;
  259. if ( GetSequence() != animDesired || !SequenceLoops() )
  260. {
  261. SetCycle( 0 );
  262. }
  263. // Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter
  264. //if (!SequenceLoops())
  265. //{
  266. // IncrementInterpolationFrame();
  267. //}
  268. SetActivity( idealActivity );
  269. ResetSequence( animDesired );
  270. }
  271. else if (idealActivity == ACT_IDLE)
  272. {
  273. if ( GetFlags() & FL_DUCKING )
  274. {
  275. animDesired = LookupSequence( "crouch_idle" );
  276. }
  277. else
  278. {
  279. animDesired = LookupSequence( "look_idle" );
  280. }
  281. if (animDesired == -1)
  282. animDesired = 0;
  283. SetActivity( ACT_IDLE );
  284. }
  285. else if ( idealActivity == ACT_WALK )
  286. {
  287. if ( GetFlags() & FL_DUCKING )
  288. {
  289. animDesired = SelectWeightedSequence( ACT_CROUCH );
  290. SetActivity( ACT_CROUCH );
  291. }
  292. else
  293. {
  294. animDesired = SelectWeightedSequence( ACT_RUN );
  295. SetActivity( ACT_RUN );
  296. }
  297. }
  298. else
  299. {
  300. if ( GetActivity() == idealActivity)
  301. return;
  302. SetActivity( idealActivity );
  303. animDesired = SelectWeightedSequence( GetActivity() );
  304. // Already using the desired animation?
  305. if (GetSequence() == animDesired)
  306. return;
  307. m_iRealSequence = animDesired;
  308. ResetSequence( animDesired );
  309. SetCycle( 0 );
  310. return;
  311. }
  312. // Already using the desired animation?
  313. if (GetSequence() == animDesired)
  314. return;
  315. m_iRealSequence = animDesired;
  316. //Msg( "Set animation to %d\n", animDesired );
  317. // Reset to first frame of desired animation
  318. ResetSequence( animDesired );
  319. SetCycle( 0 );
  320. }
  321. static ConVar sv_debugweaponpickup( "sv_debugweaponpickup", "0", FCVAR_CHEAT, "Prints descriptive reasons as to why pickup did not work." );
  322. // correct respawning of weapons
  323. bool CHL1MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
  324. { CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  325. // Can I have this weapon type?
  326. if ( !IsAllowedToPickupWeapons() )
  327. {
  328. if ( sv_debugweaponpickup.GetBool() )
  329. Msg("sv_debugweaponpickup: IsAllowedToPickupWeapons() returned false\n");
  330. return false;
  331. }
  332. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  333. {
  334. if ( sv_debugweaponpickup.GetBool() && pOwner )
  335. Msg("sv_debugweaponpickup: pOwner\n");
  336. if ( sv_debugweaponpickup.GetBool() && !Weapon_CanUse( pWeapon ) )
  337. Msg("sv_debugweaponpickup: Can't use weapon\n");
  338. if ( sv_debugweaponpickup.GetBool() && !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  339. Msg("sv_debugweaponpickup: Gamerules says player can't have item\n");
  340. if ( gEvilImpulse101 )
  341. {
  342. UTIL_Remove( pWeapon );
  343. }
  344. return false;
  345. }
  346. // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
  347. if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) )
  348. {
  349. if ( sv_debugweaponpickup.GetBool() && !FVisible( this, MASK_SOLID ) )
  350. Msg("sv_debugweaponpickup: Can't fetch weapon through a wall\n");
  351. if ( sv_debugweaponpickup.GetBool() && !(GetFlags() & FL_NOTARGET) )
  352. Msg("sv_debugweaponpickup: NoTarget\n");
  353. return false;
  354. }
  355. bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
  356. if ( bOwnsWeaponAlready == true )
  357. {
  358. //If we have room for the ammo, then "take" the weapon too.
  359. if ( Weapon_EquipAmmoOnly( pWeapon ) )
  360. {
  361. pWeapon->CheckRespawn();
  362. UTIL_Remove( pWeapon );
  363. if ( sv_debugweaponpickup.GetBool() )
  364. Msg("sv_debugweaponpickup: Picking up weapon\n");
  365. return true;
  366. }
  367. else
  368. {
  369. if ( sv_debugweaponpickup.GetBool() )
  370. Msg("sv_debugweaponpickup: Owns weapon already\n");
  371. return false;
  372. }
  373. }
  374. pWeapon->CheckRespawn();
  375. Weapon_Equip( pWeapon );
  376. if ( sv_debugweaponpickup.GetBool() )
  377. Msg("sv_debugweaponpickup: Picking up weapon\n");
  378. return true;
  379. }
  380. void CHL1MP_Player::ChangeTeam( int iTeamNum )
  381. {
  382. bool bKill = false;
  383. if ( g_pGameRules->IsTeamplay() == true )
  384. {
  385. if ( iTeamNum != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED )
  386. {
  387. bKill = true;
  388. }
  389. }
  390. BaseClass::ChangeTeam( iTeamNum );
  391. m_flNextTeamChangeTime = gpGlobals->curtime + 5;
  392. if ( g_pGameRules->IsTeamplay() == true )
  393. {
  394. SetPlayerTeamModel();
  395. }
  396. else
  397. {
  398. SetPlayerModel();
  399. }
  400. if ( bKill == true )
  401. {
  402. CommitSuicide();
  403. }
  404. }
  405. void CHL1MP_Player::SetPlayerTeamModel( void )
  406. {
  407. int iTeamNum = GetTeamNumber();
  408. if ( iTeamNum <= TEAM_SPECTATOR )
  409. return;
  410. CTeam * pTeam = GetGlobalTeam( iTeamNum );
  411. char szModelName[256];
  412. Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, pTeam->GetName(), pTeam->GetName() );
  413. // Check to see if the model was properly precached, do not error out if not.
  414. int i = modelinfo->GetModelIndex( szModelName );
  415. if ( i == -1 )
  416. {
  417. Warning("Model %s does not exist.\n", szModelName );
  418. return;
  419. }
  420. SetModel( szModelName );
  421. m_flNextModelChangeTime = gpGlobals->curtime + 5;
  422. }
  423. void CHL1MP_Player::SetPlayerModel( void )
  424. {
  425. char szBaseName[128];
  426. Q_FileBase( engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ), szBaseName, 128 );
  427. // Don't let it be 'none'; default to Barney
  428. if ( Q_stricmp( "none", szBaseName ) == 0 )
  429. {
  430. Q_strcpy( szBaseName, "gordon" );
  431. }
  432. char szModelName[256];
  433. Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, szBaseName, szBaseName );
  434. // Check to see if the model was properly precached, do not error out if not.
  435. int i = modelinfo->GetModelIndex( szModelName );
  436. if ( i == -1 )
  437. {
  438. SetModel( "models/player/mp/gordon/gordon.mdl" );
  439. engine->ClientCommand ( edict(), "cl_playermodel models/gordon.mdl\n" );
  440. return;
  441. }
  442. SetModel( szModelName );
  443. m_flNextModelChangeTime = gpGlobals->curtime + 5;
  444. }
  445. // -------------------------------------------------------------------------------- //
  446. // Ragdoll entities.
  447. // -------------------------------------------------------------------------------- //
  448. class CHL1MPRagdoll : public CBaseAnimatingOverlay
  449. {
  450. public:
  451. DECLARE_CLASS( CHL1MPRagdoll, CBaseAnimatingOverlay );
  452. DECLARE_SERVERCLASS();
  453. // Transmit ragdolls to everyone.
  454. virtual int UpdateTransmitState()
  455. {
  456. return SetTransmitState( FL_EDICT_ALWAYS );
  457. }
  458. public:
  459. // In case the client has the player entity, we transmit the player index.
  460. // In case the client doesn't have it, we transmit the player's model index, origin, and angles
  461. // so they can create a ragdoll in the right place.
  462. CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
  463. CNetworkVector( m_vecRagdollVelocity );
  464. CNetworkVector( m_vecRagdollOrigin );
  465. };
  466. LINK_ENTITY_TO_CLASS( hl1mp_ragdoll, CHL1MPRagdoll );
  467. IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL1MPRagdoll, DT_HL1MPRagdoll )
  468. SendPropVector ( SENDINFO( m_vecRagdollOrigin), -1, SPROP_COORD ),
  469. SendPropEHandle ( SENDINFO( m_hPlayer ) ),
  470. SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
  471. SendPropInt ( SENDINFO( m_nForceBone), 8, 0 ),
  472. SendPropVector ( SENDINFO( m_vecForce), -1, SPROP_NOSCALE ),
  473. SendPropVector ( SENDINFO( m_vecRagdollVelocity ) )
  474. END_SEND_TABLE()
  475. void CHL1MP_Player::CreateRagdollEntity( void )
  476. {
  477. if ( m_hRagdoll )
  478. {
  479. UTIL_RemoveImmediate( m_hRagdoll );
  480. m_hRagdoll = NULL;
  481. }
  482. // If we already have a ragdoll, don't make another one.
  483. CHL1MPRagdoll *pRagdoll = dynamic_cast< CHL1MPRagdoll* >(m_hRagdoll.Get() );
  484. if ( !pRagdoll )
  485. {
  486. // Create a new one
  487. pRagdoll = dynamic_cast< CHL1MPRagdoll* >( CreateEntityByName( "hl1mp_ragdoll" ) );
  488. }
  489. if ( pRagdoll )
  490. {
  491. pRagdoll->m_hPlayer = this;
  492. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  493. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  494. pRagdoll->m_nModelIndex = m_nModelIndex;
  495. pRagdoll->m_nForceBone = m_nForceBone;
  496. //pRagdoll->m_vecForce = m_vecTotalBulletForce;
  497. pRagdoll->SetAbsOrigin( GetAbsOrigin() );
  498. }
  499. m_hRagdoll = pRagdoll;
  500. }
  501. void CHL1MP_Player::CreateCorpse( void )
  502. {
  503. }