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.

8267 lines
217 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for HL1.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "cs_player.h"
  9. #include "cs_gamerules.h"
  10. #include "trains.h"
  11. #include "vcollide_parse.h"
  12. #include "in_buttons.h"
  13. #include "igamemovement.h"
  14. #include "ai_hull.h"
  15. #include "ndebugoverlay.h"
  16. #include "weapon_csbase.h"
  17. #include "decals.h"
  18. #include "cs_ammodef.h"
  19. #include "IEffects.h"
  20. #include "cs_client.h"
  21. #include "client.h"
  22. #include "cs_shareddefs.h"
  23. #include "shake.h"
  24. #include "team.h"
  25. #include "weapon_c4.h"
  26. #include "weapon_parse.h"
  27. #include "weapon_knife.h"
  28. #include "movehelper_server.h"
  29. #include "tier0/vprof.h"
  30. #include "te_effect_dispatch.h"
  31. #include "vphysics/player_controller.h"
  32. #include "weapon_hegrenade.h"
  33. #include "weapon_flashbang.h"
  34. #include "weapon_csbasegun.h"
  35. #include "weapon_smokegrenade.h"
  36. #include <KeyValues.h>
  37. #include "engine/IEngineSound.h"
  38. #include "bot.h"
  39. #include "studio.h"
  40. #include <coordsize.h>
  41. #include "predicted_viewmodel.h"
  42. #include "props_shared.h"
  43. #include "tier0/icommandline.h"
  44. #include "info_camera_link.h"
  45. #include "hintmessage.h"
  46. #include "obstacle_pushaway.h"
  47. #include "movevars_shared.h"
  48. #include "death_pose.h"
  49. #include "basecsgrenade_projectile.h"
  50. #include "SoundEmitterSystem/isoundemittersystembase.h"
  51. #include "CRagdollMagnet.h"
  52. #include "datacache/imdlcache.h"
  53. #include "npcevent.h"
  54. #include "cs_gamestats.h"
  55. #include "gamestats.h"
  56. #include "holiday_gift.h"
  57. #include "../../shared/cstrike/cs_achievement_constants.h"
  58. //=============================================================================
  59. // HPE_BEGIN
  60. //=============================================================================
  61. // [dwenger] Needed for global hostage list
  62. #include "cs_simple_hostage.h"
  63. // [dwenger] Needed for weapon type used tracking
  64. #include "../../shared/cstrike/cs_weapon_parse.h"
  65. #define REPORT_PLAYER_DAMAGE 0
  66. //=============================================================================
  67. // HPE_END
  68. //=============================================================================
  69. // memdbgon must be the last include file in a .cpp file!!!
  70. #include "tier0/memdbgon.h"
  71. #pragma optimize( "", off )
  72. #pragma warning( disable : 4355 )
  73. // Minimum interval between rate-limited commands that players can run.
  74. #define CS_COMMAND_MAX_RATE 0.3
  75. const float CycleLatchInterval = 0.2f;
  76. #define CS_PUSHAWAY_THINK_CONTEXT "CSPushawayThink"
  77. ConVar cs_ShowStateTransitions( "cs_ShowStateTransitions", "-2", FCVAR_CHEAT, "cs_ShowStateTransitions <ent index or -1 for all>. Show player state transitions." );
  78. ConVar sv_max_usercmd_future_ticks( "sv_max_usercmd_future_ticks", "8", 0, "Prevents clients from running usercmds too far in the future. Prevents speed hacks." );
  79. ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." );
  80. //=============================================================================
  81. // HPE_BEGIN:
  82. // [Forrest] Allow MVP to be turned off for a server
  83. // [Forrest] Allow freezecam to be turned off for a server
  84. // [Forrest] Allow win panel to be turned off for a server
  85. //=============================================================================
  86. static void SvNoMVPChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
  87. {
  88. ConVarRef var( pConVar );
  89. if ( var.IsValid() && var.GetBool() )
  90. {
  91. // Clear the MVPs of all players when MVP is turned off.
  92. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  93. {
  94. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  95. if ( pPlayer )
  96. {
  97. pPlayer->SetNumMVPs( 0 );
  98. }
  99. }
  100. }
  101. }
  102. ConVar sv_nomvp( "sv_nomvp", "0", 0, "Disable MVP awards.", SvNoMVPChangeCallback );
  103. ConVar sv_disablefreezecam( "sv_disablefreezecam", "0", FCVAR_REPLICATED, "Turn on/off freezecam on server" );
  104. ConVar sv_nowinpanel( "sv_nowinpanel", "0", FCVAR_REPLICATED, "Turn on/off win panel on server" );
  105. //=============================================================================
  106. // HPE_END
  107. //=============================================================================
  108. // ConVar bot_mimic( "bot_mimic", "0", FCVAR_CHEAT );
  109. ConVar bot_freeze( "bot_freeze", "0", FCVAR_CHEAT );
  110. ConVar bot_crouch( "bot_crouch", "0", FCVAR_CHEAT );
  111. ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "180", FCVAR_CHEAT );
  112. ConVar sv_legacy_grenade_damage( "sv_legacy_grenade_damage", "0", FCVAR_REPLICATED, "Enable to replicate grenade damage behavior of the original Counter-Strike Source game." );
  113. extern ConVar mp_autokick;
  114. extern ConVar mp_holiday_nogifts;
  115. extern ConVar sv_turbophysics;
  116. //=============================================================================
  117. // HPE_BEGIN:
  118. // [menglish] Added in convars for freeze cam time length
  119. //=============================================================================
  120. extern ConVar spec_freeze_time;
  121. extern ConVar spec_freeze_traveltime;
  122. //=============================================================================
  123. // HPE_END
  124. //=============================================================================
  125. extern ConVar ammo_hegrenade_max;
  126. extern ConVar ammo_flashbang_max;
  127. extern ConVar ammo_smokegrenade_max;
  128. #define THROWGRENADE_COUNTER_BITS 3
  129. EHANDLE g_pLastCTSpawn;
  130. EHANDLE g_pLastTerroristSpawn;
  131. void TE_RadioIcon( IRecipientFilter& filter, float delay, CBaseEntity *pPlayer );
  132. // -------------------------------------------------------------------------------- //
  133. // Classes
  134. // -------------------------------------------------------------------------------- //
  135. class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
  136. {
  137. public:
  138. int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
  139. {
  140. CCSPlayer *pPlayer = (CCSPlayer *)pObject->GetGameData();
  141. if ( pPlayer )
  142. {
  143. if ( pPlayer->TouchedPhysics() )
  144. {
  145. return 0;
  146. }
  147. }
  148. return 1;
  149. }
  150. };
  151. static CPhysicsPlayerCallback playerCallback;
  152. // -------------------------------------------------------------------------------- //
  153. // Ragdoll entities.
  154. // -------------------------------------------------------------------------------- //
  155. class CCSRagdoll : public CBaseAnimatingOverlay
  156. {
  157. public:
  158. DECLARE_CLASS( CCSRagdoll, CBaseAnimatingOverlay );
  159. DECLARE_SERVERCLASS();
  160. // Transmit ragdolls to everyone.
  161. virtual int UpdateTransmitState()
  162. {
  163. return SetTransmitState( FL_EDICT_ALWAYS );
  164. }
  165. void Init( void )
  166. {
  167. SetSolid( SOLID_BBOX );
  168. SetMoveType( MOVETYPE_STEP );
  169. SetFriction( 1.0f );
  170. SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
  171. m_takedamage = DAMAGE_NO;
  172. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  173. SetAbsOrigin( m_hPlayer->GetAbsOrigin() );
  174. SetAbsVelocity( m_hPlayer->GetAbsVelocity() );
  175. AddSolidFlags( FSOLID_NOT_SOLID );
  176. ChangeTeam( m_hPlayer->GetTeamNumber() );
  177. UseClientSideAnimation();
  178. }
  179. public:
  180. // In case the client has the player entity, we transmit the player index.
  181. // In case the client doesn't have it, we transmit the player's model index, origin, and angles
  182. // so they can create a ragdoll in the right place.
  183. CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
  184. CNetworkVector( m_vecRagdollVelocity );
  185. CNetworkVector( m_vecRagdollOrigin );
  186. CNetworkVar(int, m_iDeathPose );
  187. CNetworkVar(int, m_iDeathFrame );
  188. };
  189. LINK_ENTITY_TO_CLASS( cs_ragdoll, CCSRagdoll );
  190. IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll )
  191. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  192. SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ),
  193. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  194. SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
  195. SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
  196. SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
  197. SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
  198. SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS, SPROP_UNSIGNED ),
  199. SendPropInt( SENDINFO( m_iDeathFrame ), 5 ),
  200. SendPropInt( SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0),
  201. SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
  202. END_SEND_TABLE()
  203. // -------------------------------------------------------------------------------- //
  204. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  205. // -------------------------------------------------------------------------------- //
  206. class CTEPlayerAnimEvent : public CBaseTempEntity
  207. {
  208. public:
  209. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  210. DECLARE_SERVERCLASS();
  211. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  212. {
  213. }
  214. CNetworkHandle( CBasePlayer, m_hPlayer );
  215. CNetworkVar( int, m_iEvent );
  216. CNetworkVar( int, m_nData );
  217. };
  218. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  219. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  220. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  221. SendPropInt( SENDINFO( m_nData ), 32 )
  222. END_SEND_TABLE()
  223. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  224. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  225. {
  226. CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
  227. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  228. g_TEPlayerAnimEvent.m_iEvent = event;
  229. g_TEPlayerAnimEvent.m_nData = nData;
  230. g_TEPlayerAnimEvent.Create( filter, 0 );
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: Filters updates to a variable so that only non-local players see
  234. // the changes. This is so we can send a low-res origin to non-local players
  235. // while sending a hi-res one to the local player.
  236. // Input : *pVarData -
  237. // *pOut -
  238. // objectID -
  239. //-----------------------------------------------------------------------------
  240. void* SendProxy_SendNonLocalDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  241. {
  242. pRecipients->SetAllRecipients();
  243. pRecipients->ClearRecipient( objectID - 1 );
  244. return ( void * )pVarData;
  245. }
  246. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalDataTable );
  247. // -------------------------------------------------------------------------------- //
  248. // Tables.
  249. // -------------------------------------------------------------------------------- //
  250. LINK_ENTITY_TO_CLASS( player, CCSPlayer );
  251. PRECACHE_REGISTER(player);
  252. BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSLocalPlayerExclusive )
  253. SendPropFloat( SENDINFO( m_flStamina ), 14, 0, 0, 1400 ),
  254. SendPropInt( SENDINFO( m_iDirection ), 1, SPROP_UNSIGNED ),
  255. SendPropInt( SENDINFO( m_iShotsFired ), 8, SPROP_UNSIGNED ),
  256. SendPropFloat( SENDINFO( m_flVelocityModifier ), 8, 0, 0, 1 ),
  257. // send a hi-res origin to the local player for use in prediction
  258. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  259. //=============================================================================
  260. // HPE_BEGIN:
  261. // [tj]Set up the send table for per-client domination data
  262. //=============================================================================
  263. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ),
  264. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ),
  265. //=============================================================================
  266. // HPE_END
  267. //=============================================================================
  268. END_SEND_TABLE()
  269. BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSNonLocalPlayerExclusive )
  270. // send a lo-res origin to other players
  271. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  272. END_SEND_TABLE()
  273. IMPLEMENT_SERVERCLASS_ST( CCSPlayer, DT_CSPlayer )
  274. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  275. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  276. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  277. SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
  278. SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
  279. SendPropExclude( "DT_BaseAnimating", "m_nMuzzleFlashParity" ),
  280. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  281. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  282. // cs_playeranimstate and clientside animation takes care of these on the client
  283. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  284. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  285. // We need to send a hi-res origin to the local player to avoid prediction errors sliding along walls
  286. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  287. // Data that only gets sent to the local player.
  288. SendPropDataTable( "cslocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
  289. SendPropDataTable( "csnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSNonLocalPlayerExclusive), SendProxy_SendNonLocalDataTable ),
  290. SendPropInt( SENDINFO( m_iThrowGrenadeCounter ), THROWGRENADE_COUNTER_BITS, SPROP_UNSIGNED ),
  291. SendPropInt( SENDINFO( m_iAddonBits ), NUM_ADDON_BITS, SPROP_UNSIGNED ),
  292. SendPropInt( SENDINFO( m_iPrimaryAddon ), 8, SPROP_UNSIGNED ),
  293. SendPropInt( SENDINFO( m_iSecondaryAddon ), 8, SPROP_UNSIGNED ),
  294. SendPropInt( SENDINFO( m_iPlayerState ), Q_log2( NUM_PLAYER_STATES )+1, SPROP_UNSIGNED ),
  295. SendPropInt( SENDINFO( m_iAccount ), 16, SPROP_UNSIGNED ),
  296. SendPropInt( SENDINFO( m_bInBombZone ), 1, SPROP_UNSIGNED ),
  297. SendPropInt( SENDINFO( m_bInBuyZone ), 1, SPROP_UNSIGNED ),
  298. SendPropInt( SENDINFO( m_iClass ), Q_log2( CS_NUM_CLASSES )+1, SPROP_UNSIGNED ),
  299. SendPropInt( SENDINFO( m_ArmorValue ), 8 ),
  300. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ),
  301. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ),
  302. SendPropBool( SENDINFO( m_bHasDefuser ) ),
  303. SendPropBool( SENDINFO( m_bNightVisionOn ) ), //send as int so we can use a RecvProxy on the client
  304. SendPropBool( SENDINFO( m_bHasNightVision ) ),
  305. //=============================================================================
  306. // HPE_BEGIN:
  307. // [dwenger] Added for fun-fact support
  308. //=============================================================================
  309. //SendPropBool( SENDINFO( m_bPickedUpDefuser ) ),
  310. //SendPropBool( SENDINFO( m_bDefusedWithPickedUpKit) ),
  311. //=============================================================================
  312. // HPE_END
  313. //=============================================================================
  314. SendPropBool( SENDINFO( m_bInHostageRescueZone ) ),
  315. SendPropBool( SENDINFO( m_bIsDefusing ) ),
  316. SendPropBool( SENDINFO( m_bResumeZoom ) ),
  317. SendPropInt( SENDINFO( m_iLastZoom ), 8, SPROP_UNSIGNED ),
  318. #ifdef CS_SHIELD_ENABLED
  319. SendPropBool( SENDINFO( m_bHasShield ) ),
  320. SendPropBool( SENDINFO( m_bShieldDrawn ) ),
  321. #endif
  322. SendPropBool( SENDINFO( m_bHasHelmet ) ),
  323. SendPropFloat (SENDINFO(m_flFlashDuration), 0, SPROP_NOSCALE ),
  324. SendPropFloat( SENDINFO(m_flFlashMaxAlpha), 0, SPROP_NOSCALE ),
  325. SendPropInt( SENDINFO( m_iProgressBarDuration ), 4, SPROP_UNSIGNED ),
  326. SendPropFloat( SENDINFO( m_flProgressBarStartTime ), 0, SPROP_NOSCALE ),
  327. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  328. SendPropInt( SENDINFO( m_cycleLatch ), 4, SPROP_UNSIGNED ),
  329. END_SEND_TABLE()
  330. BEGIN_DATADESC( CCSPlayer )
  331. DEFINE_INPUTFUNC( FIELD_VOID, "OnRescueZoneTouch", RescueZoneTouch ),
  332. DEFINE_THINKFUNC( PushawayThink )
  333. END_DATADESC()
  334. // has to be included after above macros
  335. #include "cs_bot.h"
  336. // memdbgon must be the last include file in a .cpp file!!!
  337. #include "tier0/memdbgon.h"
  338. // -------------------------------------------------------------------------------- //
  339. void cc_CreatePredictionError_f( const CCommand &args )
  340. {
  341. float distance = 32;
  342. if ( args.ArgC() >= 2 )
  343. {
  344. distance = atof(args[1]);
  345. }
  346. CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
  347. pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( distance, 0, 0 ) );
  348. }
  349. ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
  350. // -------------------------------------------------------------------------------- //
  351. // CCSPlayer implementation.
  352. // -------------------------------------------------------------------------------- //
  353. CCSPlayer::CCSPlayer()
  354. {
  355. m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
  356. UseClientSideAnimation();
  357. m_iLastWeaponFireUsercmd = 0;
  358. m_iAddonBits = 0;
  359. m_bEscaped = false;
  360. m_iAccount = 0;
  361. m_bIsVIP = false;
  362. m_iClass = (int)CS_CLASS_NONE;
  363. m_angEyeAngles.Init();
  364. SetViewOffset( VEC_VIEW_SCALED( this ) );
  365. m_pCurStateInfo = NULL; // no state yet
  366. m_iThrowGrenadeCounter = 0;
  367. m_lifeState = LIFE_DEAD; // Start "dead".
  368. m_bInBombZone = false;
  369. m_bInBuyZone = false;
  370. m_bInHostageRescueZone = false;
  371. m_flDeathTime = 0.0f;
  372. m_iHostagesKilled = 0;
  373. iRadioMenu = -1;
  374. m_bTeamChanged = false;
  375. m_iShotsFired = 0;
  376. m_iDirection = 0;
  377. m_receivesMoneyNextRound = true;
  378. m_bIsBeingGivenItem = false;
  379. m_isVIP = false;
  380. m_bJustKilledTeammate = false;
  381. m_bPunishedForTK = false;
  382. m_iTeamKills = 0;
  383. m_flLastMovement = gpGlobals->curtime;
  384. m_iNextTimeCheck = 0;
  385. m_szNewName[0] = 0;
  386. m_szClanTag[0] = 0;
  387. for ( int i=0; i<NAME_CHANGE_HISTORY_SIZE; i++ )
  388. {
  389. m_flNameChangeHistory[i] = -NAME_CHANGE_HISTORY_INTERVAL;
  390. }
  391. m_iIgnoreGlobalChat = 0;
  392. m_bIgnoreRadio = false;
  393. m_pHintMessageQueue = new CHintMessageQueue(this);
  394. m_iDisplayHistoryBits = 0;
  395. m_bShowHints = true;
  396. m_flNextMouseoverUpdate = gpGlobals->curtime;
  397. m_lastDamageHealth = 0;
  398. m_lastDamageArmor = 0;
  399. m_applyDeafnessTime = 0.0f;
  400. m_cycleLatch = 0;
  401. m_cycleLatchTimer.Invalidate();
  402. m_iShouldHaveCash = 0;
  403. m_lastNavArea = NULL;
  404. //=============================================================================
  405. // HPE_BEGIN:
  406. // [menglish] Init achievement variables
  407. // [menglish] Init bullet collision variables
  408. //=============================================================================
  409. m_NumEnemiesKilledThisRound = 0;
  410. m_NumEnemiesAtRoundStart = 0;
  411. m_KillingSpreeStartTime = -1;
  412. m_firstKillBlindStartTime = -1;
  413. m_killsWhileBlind = 0;
  414. m_bSurvivedHeadshotDueToHelmet = false;
  415. m_pGooseChaseDistractingPlayer = NULL;
  416. m_gooseChaseStep = GC_NONE;
  417. m_defuseDefenseStep = DD_NONE;
  418. m_lastRoundResult = Invalid_Round_End_Reason;
  419. m_bMadeFootstepNoise = false;
  420. m_bombPickupTime = -1;
  421. m_bMadePurchseThisRound = false;
  422. m_roundsWonWithoutPurchase = 0;
  423. m_iDeathFlags = 0;
  424. m_lastFlashBangAttacker = NULL;
  425. m_iMVPs = 0;
  426. m_bKilledDefuser = false;
  427. m_bKilledRescuer = false;
  428. m_maxGrenadeKills = 0;
  429. m_grenadeDamageTakenThisRound = 0;
  430. m_vLastHitLocationObjectSpace = Vector(0,0,0);
  431. m_wasNotKilledNaturally = false;
  432. //=============================================================================
  433. // HPE_END
  434. //=============================================================================
  435. }
  436. CCSPlayer::~CCSPlayer()
  437. {
  438. delete m_pHintMessageQueue;
  439. m_pHintMessageQueue = NULL;
  440. // delete the records of damage taken and given
  441. ResetDamageCounters();
  442. m_PlayerAnimState->Release();
  443. }
  444. CCSPlayer *CCSPlayer::CreatePlayer( const char *className, edict_t *ed )
  445. {
  446. CCSPlayer::s_PlayerEdict = ed;
  447. return (CCSPlayer*)CreateEntityByName( className );
  448. }
  449. void CCSPlayer::Precache()
  450. {
  451. Vector mins( -13, -13, -10 );
  452. Vector maxs( 13, 13, 75 );
  453. int i;
  454. for ( i=0; i<CTPlayerModels.Count(); ++i )
  455. {
  456. PrecacheModel( CTPlayerModels[i] );
  457. engine->ForceModelBounds( CTPlayerModels[i], mins, maxs );
  458. }
  459. for ( i=0; i<TerroristPlayerModels.Count(); ++i )
  460. {
  461. PrecacheModel( TerroristPlayerModels[i] );
  462. engine->ForceModelBounds( TerroristPlayerModels[i], mins, maxs );
  463. }
  464. // Sigh - have to force identical VMTs for the player models. I'm just going to hard-code these
  465. // strings here, rather than have char***'s or the CUtlVector<CUtlVector<>> equivalent.
  466. engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban.vmt" );
  467. engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban_glass.vmt" );
  468. engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas.vmt" );
  469. engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas_glass.vmt" );
  470. engine->ForceSimpleMaterial( "materials/models/player/ct_gsg9/ct_gsg9.vmt" );
  471. engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign.vmt" );
  472. engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign_glass.vmt" );
  473. engine->ForceSimpleMaterial( "materials/models/player/t_phoenix/t_phoenix.vmt" );
  474. engine->ForceSimpleMaterial( "materials/models/player/t_guerilla/t_guerilla.vmt" );
  475. engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet.vmt" );
  476. engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet_glass.vmt" );
  477. engine->ForceSimpleMaterial( "materials/models/player/t_arctic/t_arctic.vmt" );
  478. #ifdef CS_SHIELD_ENABLED
  479. PrecacheModel( SHIELD_VIEW_MODEL );
  480. #endif
  481. PrecacheScriptSound( "Player.DeathHeadShot" );
  482. PrecacheScriptSound( "Player.Death" );
  483. PrecacheScriptSound( "Player.DamageHelmet" );
  484. PrecacheScriptSound( "Player.DamageHeadShot" );
  485. PrecacheScriptSound( "Flesh.BulletImpact" );
  486. PrecacheScriptSound( "Player.DamageKevlar" );
  487. PrecacheScriptSound( "Player.PickupWeapon" );
  488. PrecacheScriptSound( "Player.NightVisionOff" );
  489. PrecacheScriptSound( "Player.NightVisionOn" );
  490. PrecacheScriptSound( "Player.FlashlightOn" );
  491. PrecacheScriptSound( "Player.FlashlightOff" );
  492. // CS Bot sounds
  493. PrecacheScriptSound( "Bot.StuckSound" );
  494. PrecacheScriptSound( "Bot.StuckStart" );
  495. PrecacheScriptSound( "Bot.FellOff" );
  496. UTIL_PrecacheOther( "item_kevlar" );
  497. UTIL_PrecacheOther( "item_assaultsuit" );
  498. UTIL_PrecacheOther( "item_defuser" );
  499. PrecacheModel ( "sprites/glow01.vmt" );
  500. PrecacheModel ( "models/items/cs_gift.mdl" );
  501. BaseClass::Precache();
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose: Allow pre-frame adjustments on the player
  505. //-----------------------------------------------------------------------------
  506. ConVar sv_runcmds( "sv_runcmds", "1" );
  507. void CCSPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper )
  508. {
  509. VPROF( "CCSPlayer::PlayerRunCommand" );
  510. if ( !sv_runcmds.GetInt() )
  511. return;
  512. // don't run commands in the future
  513. if ( !IsEngineThreaded() &&
  514. ( ucmd->tick_count > (gpGlobals->tickcount + sv_max_usercmd_future_ticks.GetInt()) ) )
  515. {
  516. DevMsg( "Client cmd out of sync (delta %i).\n", ucmd->tick_count - gpGlobals->tickcount );
  517. return;
  518. }
  519. // If they use a negative bot_mimic value, then don't process their usercmds, but have
  520. // bots process them instead (so they can stay still and have the bot move around).
  521. CUserCmd tempCmd;
  522. if ( -bot_mimic.GetInt() == entindex() )
  523. {
  524. tempCmd = *ucmd;
  525. ucmd = &tempCmd;
  526. ucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0;
  527. ucmd->buttons = 0;
  528. ucmd->impulse = 0;
  529. }
  530. if ( IsBot() && bot_crouch.GetInt() )
  531. ucmd->buttons |= IN_DUCK;
  532. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  533. }
  534. bool CCSPlayer::RunMimicCommand( CUserCmd& cmd )
  535. {
  536. if ( !IsBot() )
  537. return false;
  538. int iMimic = abs( bot_mimic.GetInt() );
  539. if ( iMimic > gpGlobals->maxClients )
  540. return false;
  541. CBasePlayer *pPlayer = UTIL_PlayerByIndex( iMimic );
  542. if ( !pPlayer )
  543. return false;
  544. if ( !pPlayer->GetLastUserCommand() )
  545. return false;
  546. cmd = *pPlayer->GetLastUserCommand();
  547. cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
  548. pl.fixangle = FIXANGLE_NONE;
  549. return true;
  550. }
  551. //-----------------------------------------------------------------------------
  552. // Purpose: Simulates a single frame of movement for a player
  553. //-----------------------------------------------------------------------------
  554. void CCSPlayer::RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
  555. {
  556. CUserCmd cmd;
  557. // Store off the globals.. they're gonna get whacked
  558. float flOldFrametime = gpGlobals->frametime;
  559. float flOldCurtime = gpGlobals->curtime;
  560. float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
  561. this->SetTimeBase( flTimeBase );
  562. CUserCmd lastUserCmd = *GetLastUserCommand();
  563. Q_memset( &cmd, 0, sizeof( cmd ) );
  564. if ( !RunMimicCommand( cmd ) )
  565. {
  566. cmd.forwardmove = forwardmove;
  567. cmd.sidemove = sidemove;
  568. cmd.upmove = upmove;
  569. cmd.buttons = buttons;
  570. cmd.impulse = impulse;
  571. VectorCopy( viewangles, cmd.viewangles );
  572. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  573. }
  574. MoveHelperServer()->SetHost( this );
  575. PlayerRunCommand( &cmd, MoveHelperServer() );
  576. // save off the last good usercmd
  577. if ( -bot_mimic.GetInt() == entindex() )
  578. {
  579. CUserCmd lastCmd = *GetLastUserCommand();
  580. lastCmd.command_number = cmd.command_number;
  581. lastCmd.tick_count = cmd.tick_count;
  582. SetLastUserCommand( lastCmd );
  583. }
  584. else
  585. {
  586. SetLastUserCommand( cmd );
  587. }
  588. // Clear out any fixangle that has been set
  589. pl.fixangle = FIXANGLE_NONE;
  590. // Restore the globals..
  591. gpGlobals->frametime = flOldFrametime;
  592. gpGlobals->curtime = flOldCurtime;
  593. MoveHelperServer()->SetHost( NULL );
  594. }
  595. void CCSPlayer::InitialSpawn( void )
  596. {
  597. BaseClass::InitialSpawn();
  598. // we're going to give the bots money here instead of FinishClientPutInServer()
  599. // because of the bots' timing for purchasing weapons/items.
  600. if ( IsBot() )
  601. {
  602. m_iAccount = CSGameRules()->GetStartMoney();
  603. }
  604. if ( !engine->IsDedicatedServer() && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() )
  605. {
  606. ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
  607. }
  608. State_Enter( STATE_WELCOME );
  609. //=============================================================================
  610. // HPE_BEGIN:
  611. // [tj] We reset the stats at the beginning of the map (including domination tracking)
  612. //=============================================================================
  613. CCS_GameStats.ResetPlayerStats(this);
  614. RemoveNemesisRelationships();
  615. //=============================================================================
  616. // HPE_END
  617. //=============================================================================
  618. }
  619. void CCSPlayer::SetModelFromClass( void )
  620. {
  621. if ( GetTeamNumber() == TEAM_TERRORIST )
  622. {
  623. int index = m_iClass - FIRST_T_CLASS;
  624. if ( index < 0 || index >= TerroristPlayerModels.Count() )
  625. {
  626. index = RandomInt( 0, TerroristPlayerModels.Count() - 1 );
  627. m_iClass = index + FIRST_T_CLASS; // clean up players who selected a higher class than we support yet
  628. }
  629. SetModel( TerroristPlayerModels[index] );
  630. }
  631. else if ( GetTeamNumber() == TEAM_CT )
  632. {
  633. int index = m_iClass - FIRST_CT_CLASS;
  634. if ( index < 0 || index >= CTPlayerModels.Count() )
  635. {
  636. index = RandomInt( 0, CTPlayerModels.Count() - 1 );
  637. m_iClass = index + FIRST_CT_CLASS; // clean up players who selected a higher class than we support yet
  638. }
  639. SetModel( CTPlayerModels[index] );
  640. }
  641. else
  642. {
  643. SetModel( CTPlayerModels[0] );
  644. }
  645. }
  646. void CCSPlayer::Spawn()
  647. {
  648. m_RateLimitLastCommandTimes.Purge();
  649. // Get rid of the progress bar...
  650. SetProgressBarTime( 0 );
  651. CreateViewModel( 1 );
  652. // Set their player model.
  653. SetModelFromClass();
  654. BaseClass::Spawn();
  655. //=============================================================================
  656. // HPE_BEGIN:
  657. // [pfreese] Clear the last known nav area (used to be done by CBasePlayer)
  658. //=============================================================================
  659. m_lastNavArea = NULL;
  660. //=============================================================================
  661. // HPE_END
  662. //=============================================================================
  663. AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.
  664. // Override what CBasePlayer set for the view offset.
  665. SetViewOffset( VEC_VIEW_SCALED( this ) );
  666. //
  667. // Our player movement speed is set once here. This will override the cl_xxxx
  668. // cvars unless they are set to be lower than this.
  669. //
  670. SetMaxSpeed( CS_PLAYER_SPEED_RUN );
  671. SetFOV( this, 0 );
  672. m_bIsDefusing = false;
  673. //=============================================================================
  674. // HPE_BEGIN
  675. // [dwenger] Reset hostage-related variables
  676. //=============================================================================
  677. m_bIsRescuing = false;
  678. m_bInjuredAHostage = false;
  679. m_iNumFollowers = 0;
  680. // [tj] Reset this flag if the player is not in observer mode (as happens when a player spawns late)
  681. if (m_iPlayerState != STATE_OBSERVER_MODE)
  682. {
  683. m_wasNotKilledNaturally = false;
  684. }
  685. //=============================================================================
  686. // HPE_END
  687. //=============================================================================
  688. m_iShotsFired = 0;
  689. m_iDirection = 0;
  690. if ( m_pHintMessageQueue )
  691. {
  692. m_pHintMessageQueue->Reset();
  693. }
  694. m_iDisplayHistoryBits &= ~DHM_ROUND_CLEAR;
  695. // Special-case here. A bunch of things happen in CBasePlayer::Spawn(), and we really want the
  696. // player states to control these things, so give whatever player state we're in a chance
  697. // to reinitialize itself.
  698. State_Transition( m_iPlayerState );
  699. ClearFlashbangScreenFade();
  700. m_flVelocityModifier = 1.0f;
  701. ResetStamina();
  702. m_flLastRadarUpdateTime = 0.0f;
  703. m_iNumSpawns++;
  704. if ( !engine->IsDedicatedServer() && CSGameRules()->m_iTotalRoundsPlayed < 2 && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() )
  705. {
  706. ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
  707. }
  708. m_bTeamChanged = false;
  709. m_iOldTeam = TEAM_UNASSIGNED;
  710. m_iRadioMessages = 60;
  711. m_flRadioTime = gpGlobals->curtime;
  712. if ( m_hRagdoll )
  713. {
  714. UTIL_Remove( m_hRagdoll );
  715. }
  716. m_hRagdoll = NULL;
  717. // did we change our name while we were dead?
  718. if ( m_szNewName[0] != 0 )
  719. {
  720. ChangeName( m_szNewName );
  721. m_szNewName[0] = 0;
  722. }
  723. if ( m_bIsVIP )
  724. {
  725. HintMessage( "#Hint_you_are_the_vip", true, true );
  726. }
  727. m_bIsInAutoBuy = false;
  728. m_bIsInRebuy = false;
  729. m_bAutoReload = false;
  730. SetContextThink( &CCSPlayer::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );
  731. if ( GetActiveWeapon() && !IsObserver() )
  732. {
  733. GetActiveWeapon()->Deploy();
  734. m_flNextAttack = gpGlobals->curtime; // Allow reloads to finish, since we're playing the deploy anim instead. This mimics goldsrc behavior, anyway.
  735. }
  736. m_applyDeafnessTime = 0.0f;
  737. m_cycleLatch = 0;
  738. m_cycleLatchTimer.Start( RandomFloat( 0.0f, CycleLatchInterval ) );
  739. StockPlayerAmmo();
  740. }
  741. void CCSPlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data )
  742. {
  743. if ( CSGameRules()->IsLogoMap() )
  744. return;
  745. if ( CommandLine()->FindParm("-makedevshots") )
  746. return;
  747. BaseClass::ShowViewPortPanel( name, bShow, data );
  748. }
  749. void CCSPlayer::ClearFlashbangScreenFade( void )
  750. {
  751. if( IsBlind() )
  752. {
  753. color32 clr = { 0, 0, 0, 0 };
  754. UTIL_ScreenFade( this, clr, 0.01, 0.0, FFADE_OUT | FFADE_PURGE );
  755. m_flFlashDuration = 0.0f;
  756. m_flFlashMaxAlpha = 255.0f;
  757. }
  758. // clear blind time (after screen fades are canceled)
  759. m_blindUntilTime = 0.0f;
  760. m_blindStartTime = 0.0f;
  761. }
  762. void CCSPlayer::GiveDefaultItems()
  763. {
  764. // Always give the player the knife.
  765. CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  766. if ( pistol )
  767. {
  768. return;
  769. }
  770. m_bUsingDefaultPistol = true;
  771. if ( GetTeamNumber() == TEAM_CT )
  772. {
  773. GiveNamedItem( "weapon_knife" );
  774. GiveNamedItem( "weapon_usp" );
  775. GiveAmmo( 24, BULLET_PLAYER_45ACP );
  776. }
  777. else if ( GetTeamNumber() == TEAM_TERRORIST )
  778. {
  779. GiveNamedItem( "weapon_knife" );
  780. GiveNamedItem( "weapon_glock" );
  781. GiveAmmo( 40, BULLET_PLAYER_9MM );
  782. }
  783. }
  784. void CCSPlayer::SetClanTag( const char *pTag )
  785. {
  786. if ( pTag )
  787. {
  788. Q_strncpy( m_szClanTag, pTag, sizeof( m_szClanTag ) );
  789. }
  790. }
  791. void CCSPlayer::CreateRagdollEntity()
  792. {
  793. // If we already have a ragdoll, don't make another one.
  794. CCSRagdoll *pRagdoll = dynamic_cast< CCSRagdoll* >( m_hRagdoll.Get() );
  795. if ( !pRagdoll )
  796. {
  797. // create a new one
  798. pRagdoll = dynamic_cast< CCSRagdoll* >( CreateEntityByName( "cs_ragdoll" ) );
  799. }
  800. if ( pRagdoll )
  801. {
  802. pRagdoll->m_hPlayer = this;
  803. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  804. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  805. pRagdoll->m_nModelIndex = m_nModelIndex;
  806. pRagdoll->m_nForceBone = m_nForceBone;
  807. pRagdoll->m_vecForce = m_vecTotalBulletForce;
  808. pRagdoll->m_iDeathPose = m_iDeathPose;
  809. pRagdoll->m_iDeathFrame = m_iDeathFrame;
  810. pRagdoll->Init();
  811. }
  812. // ragdolls will be removed on round restart automatically
  813. m_hRagdoll = pRagdoll;
  814. }
  815. int CCSPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  816. {
  817. // set damage type sustained
  818. m_bitsDamageType |= info.GetDamageType();
  819. if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
  820. return 0;
  821. // don't apply damage forces in CS
  822. // fire global game event
  823. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  824. if ( event )
  825. {
  826. event->SetInt("userid", GetUserID() );
  827. event->SetInt("health", MAX(0, m_iHealth) );
  828. event->SetInt("armor", MAX(0, ArmorValue()) );
  829. event->SetInt( "dmg_health", m_lastDamageHealth );
  830. event->SetInt( "dmg_armor", m_lastDamageArmor );
  831. if ( info.GetDamageType() & DMG_BLAST )
  832. {
  833. event->SetInt( "hitgroup", HITGROUP_GENERIC );
  834. }
  835. else
  836. {
  837. event->SetInt( "hitgroup", m_LastHitGroup );
  838. }
  839. CBaseEntity * attacker = info.GetAttacker();
  840. const char *weaponName = "";
  841. if ( attacker->IsPlayer() )
  842. {
  843. CBasePlayer *player = ToBasePlayer( attacker );
  844. event->SetInt("attacker", player->GetUserID() ); // hurt by other player
  845. CBaseEntity *pInflictor = info.GetInflictor();
  846. if ( pInflictor )
  847. {
  848. if ( pInflictor == player )
  849. {
  850. // If the inflictor is the killer, then it must be their current weapon doing the damage
  851. if ( player->GetActiveWeapon() )
  852. {
  853. weaponName = player->GetActiveWeapon()->GetClassname();
  854. }
  855. }
  856. else
  857. {
  858. weaponName = STRING( pInflictor->m_iClassname ); // it's just that easy
  859. }
  860. }
  861. }
  862. else
  863. {
  864. event->SetInt("attacker", 0 ); // hurt by "world"
  865. }
  866. if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
  867. {
  868. weaponName += 7;
  869. }
  870. else if( strncmp( weaponName, "hegrenade", 9 ) == 0 ) //"hegrenade_projectile"
  871. {
  872. //=============================================================================
  873. // HPE_BEGIN:
  874. // [tj] Handle grenade-surviving achievement
  875. //=============================================================================
  876. if (info.GetAttacker()->GetTeamNumber() != GetTeamNumber())
  877. {
  878. m_grenadeDamageTakenThisRound += info.GetDamage();
  879. }
  880. //=============================================================================
  881. // HPE_END
  882. //=============================================================================
  883. weaponName = "hegrenade";
  884. }
  885. else if( strncmp( weaponName, "flashbang", 9 ) == 0 ) //"flashbang_projectile"
  886. {
  887. weaponName = "flashbang";
  888. }
  889. else if( strncmp( weaponName, "smokegrenade", 12 ) == 0 ) //"smokegrenade_projectile"
  890. {
  891. weaponName = "smokegrenade";
  892. }
  893. event->SetString( "weapon", weaponName );
  894. event->SetInt( "priority", 5 );
  895. gameeventmanager->FireEvent( event );
  896. }
  897. return 1;
  898. }
  899. //=============================================================================
  900. // HPE_BEGIN:
  901. // [dwenger] Supports fun-fact
  902. //=============================================================================
  903. // Returns the % of the enemies this player killed in the round
  904. int CCSPlayer::GetPercentageOfEnemyTeamKilled()
  905. {
  906. if ( m_NumEnemiesAtRoundStart > 0 )
  907. {
  908. return (int)( ( (float)m_NumEnemiesKilledThisRound / (float)m_NumEnemiesAtRoundStart ) * 100.0f );
  909. }
  910. return 0;
  911. }
  912. //=============================================================================
  913. // HPE_END
  914. //=============================================================================
  915. void CCSPlayer::Event_Killed( const CTakeDamageInfo &info )
  916. {
  917. //=============================================================================
  918. // HPE_BEGIN:
  919. // [pfreese] Process on-death achievements
  920. //=============================================================================
  921. ProcessPlayerDeathAchievements(ToCSPlayer(info.GetAttacker()), this, info);
  922. //=============================================================================
  923. // HPE_END
  924. //=============================================================================
  925. SetArmorValue( 0 );
  926. //=============================================================================
  927. // HPE_BEGIN:
  928. // [tj] Added a parameter so we know if it was death that caused the drop
  929. // [menglish] Keep track of what the player has dropped for the freeze panel callouts
  930. //=============================================================================
  931. CBaseEntity* pAttacker = info.GetAttacker();
  932. bool friendlyFire = pAttacker && pAttacker->GetTeamNumber() == GetTeamNumber();
  933. //Only count the drop if it was not friendly fire
  934. DropWeapons(true, !friendlyFire);
  935. //=============================================================================
  936. // HPE_END
  937. //=============================================================================
  938. // Just in case the progress bar is on screen, kill it.
  939. SetProgressBarTime( 0 );
  940. m_bIsDefusing = false;
  941. m_bHasNightVision = false;
  942. m_bNightVisionOn = false;
  943. //=============================================================================
  944. // HPE_BEGIN:
  945. // [dwenger] Added for fun-fact support
  946. //=============================================================================
  947. m_bPickedUpDefuser = false;
  948. m_bDefusedWithPickedUpKit = false;
  949. //=============================================================================
  950. // HPE_END
  951. //=============================================================================
  952. m_bHasHelmet = false;
  953. m_flFlashDuration = 0.0f;
  954. FlashlightTurnOff();
  955. // show killer in death cam mode
  956. if( IsValidObserverTarget( info.GetAttacker() ) )
  957. {
  958. SetObserverTarget( info.GetAttacker() );
  959. }
  960. else
  961. {
  962. ResetObserverMode();
  963. }
  964. //update damage info with our accumulated physics force
  965. CTakeDamageInfo subinfo = info;
  966. // HACK[pfreese]: scale impulse up for visual effect
  967. const float kImpulseBonusScale = 2.0f;
  968. subinfo.SetDamageForce( m_vecTotalBulletForce * kImpulseBonusScale);
  969. //Adrian: Select a death pose to extrapolate the ragdoll's velocity.
  970. SelectDeathPose( info );
  971. // See if there's a ragdoll magnet that should influence our force.
  972. CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this );
  973. if( pMagnet )
  974. {
  975. m_vecTotalBulletForce += pMagnet->GetForceVector( this );
  976. }
  977. // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
  978. // because we still want to transmit to the clients in our PVS.
  979. CreateRagdollEntity();
  980. // Special code to drop holiday gifts for the holiday achievement
  981. if ( ( mp_holiday_nogifts.GetBool() == false ) && UTIL_IsHolidayActive( 3 /*kHoliday_Christmas*/ ) )
  982. {
  983. if ( RandomInt( 0, 100 ) < 20 )
  984. {
  985. CHolidayGift::Create( WorldSpaceCenter(), GetAbsAngles(), EyeAngles(), GetAbsVelocity(), this );
  986. }
  987. }
  988. State_Transition( STATE_DEATH_ANIM ); // Transition into the dying state.
  989. BaseClass::Event_Killed( subinfo );
  990. //=============================================================================
  991. // HPE_BEGIN:
  992. // [pfreese] If this kill ended the round, award the MVP to someone on the
  993. // winning team.
  994. // TODO - move this code somewhere else more MVP related
  995. //=============================================================================
  996. bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
  997. bool roundIsWonNow = CSGameRules()->CheckWinConditions();
  998. if ( !roundWasAlreadyWon && roundIsWonNow )
  999. {
  1000. CCSPlayer* pMVP = NULL;
  1001. int maxKills = 0;
  1002. int maxDamage = 0;
  1003. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1004. {
  1005. CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  1006. if ( pPlayer )
  1007. {
  1008. // only consider players on the winning team
  1009. if ( pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
  1010. continue;
  1011. int nKills = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_KILLS];
  1012. int nDamage = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_DAMAGE];
  1013. if ( nKills > maxKills || ( nKills == maxKills && nDamage > maxDamage ) )
  1014. {
  1015. pMVP = pPlayer;
  1016. maxKills = nKills;
  1017. maxDamage = nDamage;
  1018. }
  1019. }
  1020. }
  1021. if ( pMVP )
  1022. {
  1023. pMVP->IncrementNumMVPs( CSMVP_ELIMINATION );
  1024. }
  1025. }
  1026. //=============================================================================
  1027. // HPE_END
  1028. //=============================================================================
  1029. OutputDamageGiven();
  1030. OutputDamageTaken();
  1031. ResetDamageCounters();
  1032. if ( m_bPunishedForTK )
  1033. {
  1034. m_bPunishedForTK = false;
  1035. HintMessage( "#Hint_cannot_play_because_tk", true, true );
  1036. }
  1037. if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
  1038. {
  1039. m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
  1040. HintMessage( "#Spec_Duck", true, true );
  1041. }
  1042. }
  1043. //=============================================================================
  1044. // HPE_BEGIN:
  1045. // [menglish, tj] Update and check any one-off achievements based on the kill
  1046. //=============================================================================
  1047. // Notify that I've killed some other entity. (called from Victim's Event_Killed).
  1048. void CCSPlayer::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )
  1049. {
  1050. BaseClass::Event_KilledOther(pVictim, info);
  1051. }
  1052. //=============================================================================
  1053. // HPE_END
  1054. //=============================================================================
  1055. void CCSPlayer::DeathSound( const CTakeDamageInfo &info )
  1056. {
  1057. if( m_LastHitGroup == HITGROUP_HEAD )
  1058. {
  1059. EmitSound( "Player.DeathHeadShot" );
  1060. }
  1061. else
  1062. {
  1063. EmitSound( "Player.Death" );
  1064. }
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose:
  1068. //-----------------------------------------------------------------------------
  1069. void CCSPlayer::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
  1070. {
  1071. BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );
  1072. if ( sv_turbophysics.GetBool() )
  1073. return;
  1074. // Setup the HL2 specific callback.
  1075. GetPhysicsController()->SetEventHandler( &playerCallback );
  1076. }
  1077. void CCSPlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
  1078. {
  1079. if ( !CanMove() )
  1080. return;
  1081. BaseClass::VPhysicsShadowUpdate( pPhysics );
  1082. }
  1083. bool CCSPlayer::HasShield() const
  1084. {
  1085. #ifdef CS_SHIELD_ENABLED
  1086. return m_bHasShield;
  1087. #else
  1088. return false;
  1089. #endif
  1090. }
  1091. bool CCSPlayer::IsShieldDrawn() const
  1092. {
  1093. #ifdef CS_SHIELD_ENABLED
  1094. return m_bShieldDrawn;
  1095. #else
  1096. return false;
  1097. #endif
  1098. }
  1099. void CCSPlayer::CheatImpulseCommands( int iImpulse )
  1100. {
  1101. switch( iImpulse )
  1102. {
  1103. case 101:
  1104. {
  1105. if( sv_cheats->GetBool() )
  1106. {
  1107. extern int gEvilImpulse101;
  1108. gEvilImpulse101 = true;
  1109. AddAccount( 16000 );
  1110. GiveAmmo( 250, BULLET_PLAYER_50AE );
  1111. GiveAmmo( 250, BULLET_PLAYER_762MM );
  1112. GiveAmmo( 250, BULLET_PLAYER_338MAG );
  1113. GiveAmmo( 250, BULLET_PLAYER_556MM );
  1114. GiveAmmo( 250, BULLET_PLAYER_556MM_BOX );
  1115. GiveAmmo( 250, BULLET_PLAYER_9MM );
  1116. GiveAmmo( 250, BULLET_PLAYER_BUCKSHOT );
  1117. GiveAmmo( 250, BULLET_PLAYER_45ACP );
  1118. GiveAmmo( 250, BULLET_PLAYER_357SIG );
  1119. GiveAmmo( 250, BULLET_PLAYER_57MM );
  1120. gEvilImpulse101 = false;
  1121. }
  1122. }
  1123. break;
  1124. default:
  1125. {
  1126. BaseClass::CheatImpulseCommands( iImpulse );
  1127. }
  1128. }
  1129. }
  1130. void CCSPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  1131. {
  1132. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  1133. int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
  1134. PointCameraSetupVisibility( this, area, pvs, pvssize );
  1135. }
  1136. void CCSPlayer::UpdateAddonBits()
  1137. {
  1138. int iNewBits = 0;
  1139. int nFlashbang = GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_FLASHBANG ) );
  1140. if ( dynamic_cast< CFlashbang* >( GetActiveWeapon() ) )
  1141. {
  1142. --nFlashbang;
  1143. }
  1144. if ( nFlashbang >= 1 )
  1145. iNewBits |= ADDON_FLASHBANG_1;
  1146. if ( nFlashbang >= 2 )
  1147. iNewBits |= ADDON_FLASHBANG_2;
  1148. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_HEGRENADE ) ) &&
  1149. !dynamic_cast< CHEGrenade* >( GetActiveWeapon() ) )
  1150. {
  1151. iNewBits |= ADDON_HE_GRENADE;
  1152. }
  1153. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_SMOKEGRENADE ) ) &&
  1154. !dynamic_cast< CSmokeGrenade* >( GetActiveWeapon() ) )
  1155. {
  1156. iNewBits |= ADDON_SMOKE_GRENADE;
  1157. }
  1158. if ( HasC4() && !dynamic_cast< CC4* >( GetActiveWeapon() ) )
  1159. iNewBits |= ADDON_C4;
  1160. if ( HasDefuser() )
  1161. iNewBits |= ADDON_DEFUSEKIT;
  1162. CWeaponCSBase *weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
  1163. if ( weapon && weapon != GetActiveWeapon() )
  1164. {
  1165. iNewBits |= ADDON_PRIMARY;
  1166. m_iPrimaryAddon = weapon->GetWeaponID();
  1167. }
  1168. else
  1169. {
  1170. m_iPrimaryAddon = WEAPON_NONE;
  1171. }
  1172. weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
  1173. if ( weapon && weapon != GetActiveWeapon() )
  1174. {
  1175. iNewBits |= ADDON_PISTOL;
  1176. if ( weapon->GetWeaponID() == WEAPON_ELITE )
  1177. {
  1178. iNewBits |= ADDON_PISTOL2;
  1179. }
  1180. m_iSecondaryAddon = weapon->GetWeaponID();
  1181. }
  1182. else if ( weapon && weapon->GetWeaponID() == WEAPON_ELITE )
  1183. {
  1184. // The active weapon is weapon_elite. Set ADDON_PISTOL2 without ADDON_PISTOL, so we know
  1185. // to display the empty holster.
  1186. iNewBits |= ADDON_PISTOL2;
  1187. m_iSecondaryAddon = weapon->GetWeaponID();
  1188. }
  1189. else
  1190. {
  1191. m_iSecondaryAddon = WEAPON_NONE;
  1192. }
  1193. m_iAddonBits = iNewBits;
  1194. }
  1195. void CCSPlayer::UpdateRadar()
  1196. {
  1197. // update once a second
  1198. if ( (m_flLastRadarUpdateTime + 1.0) > gpGlobals->curtime )
  1199. return;
  1200. m_flLastRadarUpdateTime = gpGlobals->curtime;
  1201. // update positions of all players outside of my PVS
  1202. CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits;
  1203. engine->Message_DetermineMulticastRecipients( false, EyePosition(), playerbits );
  1204. CSingleUserRecipientFilter user( this );
  1205. UserMessageBegin( user, "UpdateRadar" );
  1206. for ( int i=0; i < MAX_PLAYERS; i++ )
  1207. {
  1208. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i+1 ) );
  1209. if ( !pPlayer )
  1210. continue; // nothing there
  1211. bool bSameTeam = pPlayer->GetTeamNumber() == GetTeamNumber();
  1212. if ( playerbits.Get(i) && bSameTeam == true )
  1213. continue; // this player is in my PVS and not in my team, don't update radar pos
  1214. if ( pPlayer == this )
  1215. continue;
  1216. if ( !pPlayer->IsAlive() || pPlayer->IsObserver() || !pPlayer->IsConnected() )
  1217. continue; // don't update specattors or dead players
  1218. WRITE_BYTE( i+1 ); // player index as entity
  1219. WRITE_SBITLONG( pPlayer->GetAbsOrigin().x/4, COORD_INTEGER_BITS-1 );
  1220. WRITE_SBITLONG( pPlayer->GetAbsOrigin().y/4, COORD_INTEGER_BITS-1 );
  1221. WRITE_SBITLONG( pPlayer->GetAbsOrigin().z/4, COORD_INTEGER_BITS-1 );
  1222. WRITE_SBITLONG( AngleNormalize( pPlayer->GetAbsAngles().y ), 9 );
  1223. }
  1224. WRITE_BYTE( 0 ); // end marker
  1225. MessageEnd();
  1226. }
  1227. void CCSPlayer::UpdateMouseoverHints()
  1228. {
  1229. if ( IsBlind() || IsObserver() )
  1230. return;
  1231. Vector forward, up;
  1232. EyeVectors( &forward, NULL, &up );
  1233. trace_t tr;
  1234. // Search for objects in a sphere (tests for entities that are not solid, yet still useable)
  1235. Vector searchStart = EyePosition();
  1236. Vector searchEnd = searchStart + forward * 2048;
  1237. int useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_VISIBLE_AND_NPCS;
  1238. UTIL_TraceLine( searchStart, searchEnd, useableContents, this, COLLISION_GROUP_NONE, &tr );
  1239. if ( tr.fraction != 1.0f )
  1240. {
  1241. if (tr.DidHitNonWorldEntity() && tr.m_pEnt)
  1242. {
  1243. CBaseEntity *pObject = tr.m_pEnt;
  1244. switch ( pObject->Classify() )
  1245. {
  1246. case CLASS_PLAYER:
  1247. {
  1248. const float grenadeBloat = 1.2f; // Be conservative in estimating what a player can distinguish
  1249. if ( !TheBots->IsLineBlockedBySmoke( EyePosition(), pObject->EyePosition(), grenadeBloat ) )
  1250. {
  1251. if ( g_pGameRules->PlayerRelationship( this, pObject ) == GR_TEAMMATE )
  1252. {
  1253. if ( !(m_iDisplayHistoryBits & DHF_FRIEND_SEEN) )
  1254. {
  1255. m_iDisplayHistoryBits |= DHF_FRIEND_SEEN;
  1256. HintMessage( "#Hint_spotted_a_friend", true );
  1257. }
  1258. }
  1259. else
  1260. {
  1261. if ( !(m_iDisplayHistoryBits & DHF_ENEMY_SEEN) )
  1262. {
  1263. m_iDisplayHistoryBits |= DHF_ENEMY_SEEN;
  1264. HintMessage( "#Hint_spotted_an_enemy", true );
  1265. }
  1266. }
  1267. }
  1268. }
  1269. break;
  1270. case CLASS_PLAYER_ALLY:
  1271. switch ( GetTeamNumber() )
  1272. {
  1273. case TEAM_CT:
  1274. if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) && tr.fraction > 0.1f )
  1275. {
  1276. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  1277. HintMessage( "#Hint_rescue_the_hostages", true );
  1278. }
  1279. else if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_NEAR) && tr.fraction <= 0.1f )
  1280. {
  1281. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  1282. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_NEAR;
  1283. HintMessage( "#Hint_press_use_so_hostage_will_follow", false );
  1284. }
  1285. break;
  1286. case TEAM_TERRORIST:
  1287. if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) )
  1288. {
  1289. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  1290. HintMessage( "#Hint_prevent_hostage_rescue", true );
  1291. }
  1292. break;
  1293. }
  1294. break;
  1295. }
  1296. }
  1297. }
  1298. }
  1299. void CCSPlayer::PostThink()
  1300. {
  1301. BaseClass::PostThink();
  1302. UpdateAddonBits();
  1303. UpdateRadar();
  1304. if ( !(m_iDisplayHistoryBits & DHF_ROUND_STARTED) && CanPlayerBuy(false) )
  1305. {
  1306. HintMessage( "#Hint_press_buy_to_purchase", false );
  1307. m_iDisplayHistoryBits |= DHF_ROUND_STARTED;
  1308. }
  1309. if ( m_flNextMouseoverUpdate < gpGlobals->curtime )
  1310. {
  1311. m_flNextMouseoverUpdate = gpGlobals->curtime + 0.2f;
  1312. if ( m_bShowHints )
  1313. {
  1314. UpdateMouseoverHints();
  1315. }
  1316. }
  1317. if ( GetActiveWeapon() && !(m_iDisplayHistoryBits & DHF_AMMO_EXHAUSTED) )
  1318. {
  1319. CBaseCombatWeapon *pWeapon = GetActiveWeapon();
  1320. if ( !pWeapon->HasAnyAmmo() && !(pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE) )
  1321. {
  1322. m_iDisplayHistoryBits |= DHF_AMMO_EXHAUSTED;
  1323. HintMessage( "#Hint_out_of_ammo", false );
  1324. }
  1325. }
  1326. QAngle angles = GetLocalAngles();
  1327. angles[PITCH] = 0;
  1328. SetLocalAngles( angles );
  1329. // Store the eye angles pitch so the client can compute its animation state correctly.
  1330. m_angEyeAngles = EyeAngles();
  1331. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  1332. // check if we need to apply a deafness DSP effect.
  1333. if ((m_applyDeafnessTime != 0.0f) && (m_applyDeafnessTime <= gpGlobals->curtime))
  1334. {
  1335. ApplyDeafnessEffect();
  1336. }
  1337. if ( IsPlayerUnderwater() && GetWaterLevel() < 3 )
  1338. {
  1339. StopSound( "Player.AmbientUnderWater" );
  1340. SetPlayerUnderwater( false );
  1341. }
  1342. if( IsAlive() && m_cycleLatchTimer.IsElapsed() )
  1343. {
  1344. m_cycleLatchTimer.Start( CycleLatchInterval );
  1345. // Cycle is a float from 0 to 1. We don't need to transmit a whole float for that. Compress it in to a small fixed point
  1346. m_cycleLatch.GetForModify() = 16 * GetCycle();// 4 point fixed
  1347. }
  1348. }
  1349. void CCSPlayer::PushawayThink()
  1350. {
  1351. // Push physics props out of our way.
  1352. PerformObstaclePushaway( this );
  1353. SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. // Purpose: Returns whether or not we can switch to the given weapon.
  1357. // Input : pWeapon -
  1358. //-----------------------------------------------------------------------------
  1359. bool CCSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
  1360. {
  1361. if ( !pWeapon->CanDeploy() )
  1362. return false;
  1363. if ( GetActiveWeapon() )
  1364. {
  1365. if ( !GetActiveWeapon()->CanHolster() )
  1366. return false;
  1367. }
  1368. return true;
  1369. }
  1370. bool CCSPlayer::ShouldDoLargeFlinch( int nHitGroup, CBaseEntity *pAttacker )
  1371. {
  1372. if ( FBitSet( GetFlags(), FL_DUCKING ) )
  1373. return FALSE;
  1374. if ( nHitGroup == HITGROUP_LEFTLEG )
  1375. return FALSE;
  1376. if ( nHitGroup == HITGROUP_RIGHTLEG )
  1377. return FALSE;
  1378. CCSPlayer *pPlayer = ToCSPlayer( pAttacker );
  1379. if ( pPlayer == NULL || !pPlayer->IsPlayer() )
  1380. pPlayer = NULL;
  1381. if ( pPlayer )
  1382. {
  1383. CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();
  1384. if ( pWeapon )
  1385. {
  1386. if ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_RIFLE ||
  1387. pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SHOTGUN ||
  1388. pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE )
  1389. return true;
  1390. }
  1391. else
  1392. return false;
  1393. }
  1394. return false;
  1395. }
  1396. bool CCSPlayer::IsArmored( int nHitGroup )
  1397. {
  1398. bool bApplyArmor = false;
  1399. if ( ArmorValue() > 0 )
  1400. {
  1401. switch ( nHitGroup )
  1402. {
  1403. case HITGROUP_GENERIC:
  1404. case HITGROUP_CHEST:
  1405. case HITGROUP_STOMACH:
  1406. case HITGROUP_LEFTARM:
  1407. case HITGROUP_RIGHTARM:
  1408. bApplyArmor = true;
  1409. break;
  1410. case HITGROUP_HEAD:
  1411. if ( m_bHasHelmet )
  1412. {
  1413. bApplyArmor = true;
  1414. }
  1415. break;
  1416. default:
  1417. break;
  1418. }
  1419. }
  1420. return bApplyArmor;
  1421. }
  1422. void CCSPlayer::Pain( bool bHasArmour )
  1423. {
  1424. switch (m_LastHitGroup)
  1425. {
  1426. case HITGROUP_HEAD:
  1427. if (m_bHasHelmet) // He's wearing a helmet
  1428. {
  1429. EmitSound( "Player.DamageHelmet" );
  1430. }
  1431. else // He's not wearing a helmet
  1432. {
  1433. EmitSound( "Player.DamageHeadShot" );
  1434. }
  1435. break;
  1436. default:
  1437. if ( bHasArmour == false )
  1438. {
  1439. EmitSound( "Flesh.BulletImpact" );
  1440. }
  1441. else
  1442. {
  1443. EmitSound( "Player.DamageKevlar" );
  1444. }
  1445. break;
  1446. }
  1447. }
  1448. int CCSPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  1449. {
  1450. CTakeDamageInfo info = inputInfo;
  1451. CBaseEntity *pInflictor = info.GetInflictor();
  1452. if ( !pInflictor )
  1453. return 0;
  1454. if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
  1455. return 0;
  1456. const float flArmorBonus = 0.5f;
  1457. float flArmorRatio = 0.5f;
  1458. float flDamage = info.GetDamage();
  1459. bool bFriendlyFire = CSGameRules()->IsFriendlyFireOn();
  1460. //=============================================================================
  1461. // HPE_BEGIN:
  1462. // [tj] Added properties for goose chase achievement
  1463. //=============================================================================
  1464. CSGameRules()->PlayerTookDamage(this, inputInfo);
  1465. //Check "Goose Chase" achievement
  1466. CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
  1467. if (m_bIsDefusing && m_gooseChaseStep == GC_NONE && pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber() )
  1468. {
  1469. //Count enemies
  1470. int livingEnemies = 0;
  1471. CTeam *pAttackerTeam = GetGlobalTeam( pAttacker->GetTeamNumber() );
  1472. for ( int iPlayer=0; iPlayer < pAttackerTeam->GetNumPlayers(); iPlayer++ )
  1473. {
  1474. CCSPlayer *pPlayer = ToCSPlayer( pAttackerTeam->GetPlayer( iPlayer ) );
  1475. Assert( pPlayer );
  1476. if ( !pPlayer )
  1477. continue;
  1478. Assert( pPlayer->GetTeamNumber() == pAttackerTeam->GetTeamNumber() );
  1479. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  1480. {
  1481. livingEnemies++;
  1482. }
  1483. }
  1484. //Must be last enemy alive;
  1485. if (livingEnemies == 1)
  1486. {
  1487. m_gooseChaseStep = GC_SHOT_DURING_DEFUSE;
  1488. m_pGooseChaseDistractingPlayer = pAttacker;
  1489. }
  1490. }
  1491. //=============================================================================
  1492. // HPE_END
  1493. //=============================================================================
  1494. // warn about team attacks
  1495. if ( bFriendlyFire && pInflictor->GetTeamNumber() == GetTeamNumber() && pInflictor != this && info.GetAttacker() != this )
  1496. {
  1497. CCSPlayer *pCSAttacker = ToCSPlayer( pInflictor );
  1498. if ( !pCSAttacker )
  1499. pCSAttacker = ToCSPlayer( info.GetAttacker() );
  1500. if ( pCSAttacker )
  1501. {
  1502. if ( !(pCSAttacker->m_iDisplayHistoryBits & DHF_FRIEND_INJURED) )
  1503. {
  1504. pCSAttacker->HintMessage( "#Hint_try_not_to_injure_teammates", false );
  1505. pCSAttacker->m_iDisplayHistoryBits |= DHF_FRIEND_INJURED;
  1506. }
  1507. if ( (pCSAttacker->m_flLastAttackedTeammate + 0.6f) < gpGlobals->curtime )
  1508. {
  1509. pCSAttacker->m_flLastAttackedTeammate = gpGlobals->curtime;
  1510. // tell the rest of this player's team
  1511. Msg( "%s attacked a teammate\n", pCSAttacker->GetPlayerName() );
  1512. for ( int i=1; i<=gpGlobals->maxClients; ++i )
  1513. {
  1514. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1515. if ( pPlayer && pPlayer->GetTeamNumber() == GetTeamNumber() )
  1516. {
  1517. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_teammate_attack", pCSAttacker->GetPlayerName() );
  1518. }
  1519. }
  1520. }
  1521. }
  1522. }
  1523. if ( bFriendlyFire ||
  1524. pInflictor->GetTeamNumber() != GetTeamNumber() ||
  1525. pInflictor == this ||
  1526. info.GetAttacker() == this )
  1527. {
  1528. if ( bFriendlyFire && (info.GetDamageType() & DMG_BLAST) == 0 )
  1529. {
  1530. if ( pInflictor->GetTeamNumber() == GetTeamNumber() )
  1531. {
  1532. flDamage *= 0.35; // bullets hurt teammates less
  1533. }
  1534. }
  1535. if ( ShouldDoLargeFlinch( m_LastHitGroup, info.GetAttacker() ) )
  1536. {
  1537. if ( GetAbsVelocity().Length() < 300 )
  1538. {
  1539. m_flVelocityModifier = 0.65;
  1540. }
  1541. }
  1542. else
  1543. {
  1544. m_flVelocityModifier = 0.5;
  1545. }
  1546. //=============================================================================
  1547. // HPE_BEGIN:
  1548. //=============================================================================
  1549. // [menglish] Store whether or not the knife did this damage as knives do bullet damage,
  1550. // so we need to specifically check the weapon here
  1551. bool bKnifeDamage = false;
  1552. CCSPlayer *pPlayer = ToCSPlayer( info.GetAttacker() );
  1553. if ( pPlayer )
  1554. {
  1555. // [paquin. forest] if this is blast damage, and we haven't opted out with a cvar,
  1556. // we need to get the armor ratio out of the inflictor
  1557. if ( (info.GetDamageType() & DMG_BLAST) && !sv_legacy_grenade_damage.GetBool() )
  1558. {
  1559. // [paquin] if we know this is a grenade, use it's armor ratio, otherwise
  1560. // use the he grenade armor ratio
  1561. CBaseCSGrenadeProjectile *pGrenade = dynamic_cast< CBaseCSGrenadeProjectile * >( pInflictor );
  1562. CCSWeaponInfo* pWeaponInfo;
  1563. if ( pGrenade && pGrenade->m_pWeaponInfo )
  1564. {
  1565. pWeaponInfo = pGrenade->m_pWeaponInfo;
  1566. }
  1567. else
  1568. {
  1569. pWeaponInfo = GetWeaponInfo( WEAPON_HEGRENADE );
  1570. }
  1571. if ( pWeaponInfo )
  1572. {
  1573. flArmorRatio *= pWeaponInfo->m_flArmorRatio;
  1574. }
  1575. }
  1576. else
  1577. {
  1578. CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();
  1579. if ( pWeapon )
  1580. {
  1581. flArmorRatio *= pWeapon->GetCSWpnData().m_flArmorRatio;
  1582. //Knives do bullet damage, so we need to specifically check the weapon here
  1583. bKnifeDamage = pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE;
  1584. if ( info.GetDamageType() & DMG_BULLET && !bKnifeDamage && pPlayer->GetTeam() != GetTeam() )
  1585. {
  1586. CCS_GameStats.Event_ShotHit( pPlayer, info );
  1587. }
  1588. }
  1589. }
  1590. }
  1591. //=============================================================================
  1592. // HPE_END
  1593. //=============================================================================
  1594. // keep track of amount of damage last sustained
  1595. m_lastDamageAmount = flDamage;
  1596. // Deal with Armour
  1597. if ( ArmorValue() && !( info.GetDamageType() & (DMG_FALL | DMG_DROWN)) && IsArmored( m_LastHitGroup ) )
  1598. {
  1599. float fDamageToHealth = flDamage * flArmorRatio;
  1600. float fDamageToArmor = (flDamage - fDamageToHealth) * flArmorBonus;
  1601. int armorValue = ArmorValue();
  1602. // Does this use more armor than we have?
  1603. if (fDamageToArmor > armorValue )
  1604. {
  1605. fDamageToHealth = flDamage - armorValue / flArmorBonus;
  1606. fDamageToArmor = armorValue;
  1607. armorValue = 0;
  1608. }
  1609. else
  1610. {
  1611. if ( fDamageToArmor < 0 )
  1612. fDamageToArmor = 1;
  1613. armorValue -= fDamageToArmor;
  1614. }
  1615. m_lastDamageArmor = (int)fDamageToArmor;
  1616. SetArmorValue(armorValue);
  1617. //=============================================================================
  1618. // HPE_BEGIN:
  1619. // [tj] Handle headshot-surviving achievement
  1620. //=============================================================================
  1621. if (m_LastHitGroup == HITGROUP_HEAD && flDamage > m_iHealth && fDamageToHealth < m_iHealth)
  1622. {
  1623. m_bSurvivedHeadshotDueToHelmet = true;
  1624. }
  1625. //=============================================================================
  1626. // HPE_END
  1627. //=============================================================================
  1628. flDamage = fDamageToHealth;
  1629. info.SetDamage( flDamage );
  1630. if ( ArmorValue() <= 0.0)
  1631. m_bHasHelmet = false;
  1632. if( !(info.GetDamageType() & DMG_FALL) )
  1633. Pain( true /*has armor*/ );
  1634. }
  1635. else
  1636. {
  1637. m_lastDamageArmor = 0;
  1638. if( !(info.GetDamageType() & DMG_FALL) )
  1639. Pain( false /*no armor*/ );
  1640. }
  1641. // round damage to integer
  1642. m_lastDamageHealth = (int)flDamage;
  1643. info.SetDamage( m_lastDamageHealth );
  1644. if ( info.GetDamage() <= 0 )
  1645. return 0;
  1646. CSingleUserRecipientFilter user( this );
  1647. user.MakeReliable();
  1648. UserMessageBegin( user, "Damage" );
  1649. WRITE_BYTE( (int)info.GetDamage() );
  1650. WRITE_VEC3COORD( info.GetInflictor()->WorldSpaceCenter() );
  1651. //=============================================================================
  1652. // HPE_BEGIN:
  1653. // [menglish] Send the info about where the player was hit
  1654. //=============================================================================
  1655. if ( !( info.GetDamageType() & DMG_BULLET ) || bKnifeDamage )
  1656. {
  1657. WRITE_LONG( -1 );
  1658. }
  1659. else
  1660. {
  1661. WRITE_LONG( m_LastHitBox );
  1662. }
  1663. WRITE_VEC3COORD( m_vLastHitLocationObjectSpace );
  1664. //=============================================================================
  1665. // HPE_END
  1666. //=============================================================================
  1667. MessageEnd();
  1668. // Do special explosion damage effect
  1669. if ( info.GetDamageType() & DMG_BLAST )
  1670. {
  1671. OnDamagedByExplosion( info );
  1672. }
  1673. //=============================================================================
  1674. // HPE_BEGIN:
  1675. // [menglish] Achievement award for kill stealing i.e. killing an enemy who was very damaged from other players
  1676. // [Forrest] Moved this check before RecordDamageTaken so that the damage currently being dealt by this player
  1677. // won't disqualify them from getting the achievement.
  1678. //=============================================================================
  1679. if(m_iHealth - info.GetDamage() <= 0 && m_iHealth <= AchievementConsts::KillLowDamage_MaxHealthLeft)
  1680. {
  1681. bool onlyDamage = true;
  1682. CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
  1683. if(pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber())
  1684. {
  1685. //Verify that the killer has not done damage to this player beforehand
  1686. FOR_EACH_LL( m_DamageTakenList, i )
  1687. {
  1688. if( Q_strncmp( pAttacker->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
  1689. {
  1690. onlyDamage = false;
  1691. break;
  1692. }
  1693. }
  1694. if(onlyDamage)
  1695. {
  1696. pAttacker->AwardAchievement(CSKillLowDamage);
  1697. }
  1698. }
  1699. }
  1700. //=============================================================================
  1701. // HPE_END
  1702. //=============================================================================
  1703. #if REPORT_PLAYER_DAMAGE
  1704. // damage output spew
  1705. char dmgtype[64];
  1706. CTakeDamageInfo::DebugGetDamageTypeString( info.GetDamageType(), dmgtype, sizeof(dmgtype) );
  1707. if ( info.GetDamageType() & DMG_HEADSHOT )
  1708. Q_strncat(dmgtype, "HEADSHOT", sizeof(dmgtype));
  1709. char outputString[256];
  1710. Q_snprintf( outputString, sizeof(outputString), "%f: Player %s incoming %f damage from %s, type %s; applied %d health and %d armor\n",
  1711. gpGlobals->curtime, GetPlayerName(),
  1712. inputInfo.GetDamage(), info.GetInflictor()->GetDebugName(), dmgtype,
  1713. m_lastDamageHealth, m_lastDamageArmor);
  1714. Msg(outputString);
  1715. #endif
  1716. if ( pPlayer )
  1717. {
  1718. // Record for the shooter
  1719. pPlayer->RecordDamageGiven( GetPlayerName(), info.GetDamage() );
  1720. // And for the victim
  1721. RecordDamageTaken( pPlayer->GetPlayerName(), info.GetDamage() );
  1722. }
  1723. else
  1724. {
  1725. RecordDamageTaken( "world", info.GetDamage() );
  1726. }
  1727. m_vecTotalBulletForce += info.GetDamageForce();
  1728. gamestats->Event_PlayerDamage( this, info );
  1729. return CBaseCombatCharacter::OnTakeDamage( info );
  1730. }
  1731. else
  1732. {
  1733. return 0;
  1734. }
  1735. }
  1736. //MIKETODO: this probably should let the shield model catch the trace attacks.
  1737. bool CCSPlayer::IsHittingShield( const Vector &vecDirection, trace_t *ptr )
  1738. {
  1739. if ( HasShield() == false )
  1740. return false;
  1741. if ( IsShieldDrawn() == false )
  1742. return false;
  1743. float flDot;
  1744. Vector vForward;
  1745. Vector2D vec2LOS = vecDirection.AsVector2D();
  1746. AngleVectors( GetLocalAngles(), &vForward );
  1747. Vector2DNormalize( vForward.AsVector2D() );
  1748. Vector2DNormalize( vec2LOS );
  1749. flDot = DotProduct2D ( vec2LOS , vForward.AsVector2D() );
  1750. if ( flDot < -0.87f )
  1751. return true;
  1752. return false;
  1753. }
  1754. void CCSPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1755. {
  1756. bool bShouldBleed = true;
  1757. bool bShouldSpark = false;
  1758. bool bHitShield = IsHittingShield( vecDir, ptr );
  1759. CBasePlayer *pAttacker = (CBasePlayer*)ToBasePlayer( info.GetAttacker() );
  1760. // show blood for firendly fire only if FF is on
  1761. if ( pAttacker && ( GetTeamNumber() == pAttacker->GetTeamNumber() ) )
  1762. bShouldBleed = CSGameRules()->IsFriendlyFireOn();
  1763. if ( m_takedamage != DAMAGE_YES )
  1764. return;
  1765. m_LastHitGroup = ptr->hitgroup;
  1766. //=============================================================================
  1767. // HPE_BEGIN:
  1768. // [menglish] Used when calculating the position this player was hit at in the bone space
  1769. //=============================================================================
  1770. m_LastHitBox = ptr->hitbox;
  1771. //=============================================================================
  1772. // HPE_END
  1773. //=============================================================================
  1774. m_nForceBone = ptr->physicsbone; //Save this bone for ragdoll
  1775. float flDamage = info.GetDamage();
  1776. bool bHeadShot = false;
  1777. if ( bHitShield )
  1778. {
  1779. flDamage = 0;
  1780. bShouldBleed = false;
  1781. bShouldSpark = true;
  1782. }
  1783. else if( info.GetDamageType() & DMG_BLAST )
  1784. {
  1785. if ( ArmorValue() > 0 )
  1786. bShouldBleed = false;
  1787. if ( bShouldBleed == true )
  1788. {
  1789. // punch view if we have no armor
  1790. QAngle punchAngle = GetPunchAngle();
  1791. punchAngle.x = flDamage * -0.1;
  1792. if ( punchAngle.x < -4 )
  1793. punchAngle.x = -4;
  1794. SetPunchAngle( punchAngle );
  1795. }
  1796. }
  1797. else
  1798. {
  1799. //=============================================================================
  1800. // HPE_BEGIN:
  1801. // [menglish] Calculate the position this player was hit at in the bone space
  1802. //=============================================================================
  1803. matrix3x4_t boneTransformToWorld, boneTransformToObject;
  1804. GetBoneTransform(GetHitboxBone(ptr->hitbox), boneTransformToWorld);
  1805. MatrixInvert(boneTransformToWorld, boneTransformToObject);
  1806. VectorTransform(ptr->endpos, boneTransformToObject, m_vLastHitLocationObjectSpace);
  1807. //=============================================================================
  1808. // HPE_END
  1809. //=============================================================================
  1810. switch ( ptr->hitgroup )
  1811. {
  1812. case HITGROUP_GENERIC:
  1813. break;
  1814. case HITGROUP_HEAD:
  1815. if ( m_bHasHelmet )
  1816. {
  1817. // bShouldBleed = false;
  1818. bShouldSpark = true;
  1819. }
  1820. flDamage *= 4;
  1821. if ( !m_bHasHelmet )
  1822. {
  1823. QAngle punchAngle = GetPunchAngle();
  1824. punchAngle.x = flDamage * -0.5;
  1825. if ( punchAngle.x < -12 )
  1826. punchAngle.x = -12;
  1827. punchAngle.z = flDamage * random->RandomFloat(-1,1);
  1828. if ( punchAngle.z < -9 )
  1829. punchAngle.z = -9;
  1830. else if ( punchAngle.z > 9 )
  1831. punchAngle.z = 9;
  1832. SetPunchAngle( punchAngle );
  1833. }
  1834. bHeadShot = true;
  1835. break;
  1836. case HITGROUP_CHEST:
  1837. flDamage *= 1.0;
  1838. if ( ArmorValue() <= 0 )
  1839. {
  1840. QAngle punchAngle = GetPunchAngle();
  1841. punchAngle.x = flDamage * -0.1;
  1842. if ( punchAngle.x < -4 )
  1843. punchAngle.x = -4;
  1844. SetPunchAngle( punchAngle );
  1845. }
  1846. break;
  1847. case HITGROUP_STOMACH:
  1848. flDamage *= 1.25;
  1849. if ( ArmorValue() <= 0 )
  1850. {
  1851. QAngle punchAngle = GetPunchAngle();
  1852. punchAngle.x = flDamage * -0.1;
  1853. if ( punchAngle.x < -4 )
  1854. punchAngle.x = -4;
  1855. SetPunchAngle( punchAngle );
  1856. }
  1857. break;
  1858. case HITGROUP_LEFTARM:
  1859. case HITGROUP_RIGHTARM:
  1860. flDamage *= 1.0;
  1861. break;
  1862. case HITGROUP_LEFTLEG:
  1863. case HITGROUP_RIGHTLEG:
  1864. flDamage *= 0.75;
  1865. break;
  1866. default:
  1867. break;
  1868. }
  1869. }
  1870. // Since this code only runs on the server, make sure it shows the tempents it creates.
  1871. CDisablePredictionFiltering disabler;
  1872. if ( bShouldBleed )
  1873. {
  1874. // This does smaller splotches on the guy and splats blood on the world.
  1875. TraceBleed( flDamage, vecDir, ptr, info.GetDamageType() );
  1876. CEffectData data;
  1877. data.m_vOrigin = ptr->endpos;
  1878. data.m_vNormal = vecDir * -1;
  1879. data.m_nEntIndex = ptr->m_pEnt ? ptr->m_pEnt->entindex() : 0;
  1880. data.m_flMagnitude = flDamage;
  1881. // reduce blood effect if target has armor
  1882. if ( ArmorValue() > 0 )
  1883. data.m_flMagnitude *= 0.5f;
  1884. // reduce blood effect if target is hit in the helmet
  1885. if ( ptr->hitgroup == HITGROUP_HEAD && bShouldSpark )
  1886. data.m_flMagnitude *= 0.5;
  1887. DispatchEffect( "csblood", data );
  1888. }
  1889. if ( ( ptr->hitgroup == HITGROUP_HEAD || bHitShield ) && bShouldSpark ) // they hit a helmet
  1890. {
  1891. // show metal spark effect
  1892. g_pEffects->Sparks( ptr->endpos, 1, 1, &ptr->plane.normal );
  1893. }
  1894. if ( !bHitShield )
  1895. {
  1896. CTakeDamageInfo subInfo = info;
  1897. subInfo.SetDamage( flDamage );
  1898. if( bHeadShot )
  1899. subInfo.AddDamageType( DMG_HEADSHOT );
  1900. AddMultiDamage( subInfo, this );
  1901. }
  1902. }
  1903. void CCSPlayer::Reset()
  1904. {
  1905. ResetFragCount();
  1906. ResetDeathCount();
  1907. m_iAccount = 0;
  1908. AddAccount( -16000, false );
  1909. //remove any weapons they bought before the round started
  1910. RemoveAllItems( true );
  1911. //RemoveShield();
  1912. AddAccount( CSGameRules()->GetStartMoney(), true );
  1913. }
  1914. //-----------------------------------------------------------------------------
  1915. // Purpose: Displays a hint message to the player
  1916. // Input : *pMessage -
  1917. // bDisplayIfDead -
  1918. // bOverrideClientSettings -
  1919. //-----------------------------------------------------------------------------
  1920. void CCSPlayer::HintMessage( const char *pMessage, bool bDisplayIfDead, bool bOverrideClientSettings )
  1921. {
  1922. if ( ( !bDisplayIfDead && !IsAlive() ) || !IsNetClient() || !m_pHintMessageQueue )
  1923. return;
  1924. if ( bOverrideClientSettings || m_bShowHints )
  1925. m_pHintMessageQueue->AddMessage( pMessage );
  1926. }
  1927. void CCSPlayer::AddAccount( int amount, bool bTrackChange, bool bItemBought, const char *pItemName )
  1928. {
  1929. m_iAccount += amount;
  1930. //=============================================================================
  1931. // HPE_BEGIN:
  1932. // [menglish] Description of reason for change
  1933. //=============================================================================
  1934. if(amount > 0)
  1935. {
  1936. CCS_GameStats.Event_MoneyEarned( this, amount );
  1937. }
  1938. else if( amount < 0 && bItemBought)
  1939. {
  1940. CCS_GameStats.Event_MoneySpent( this, ABS(amount), pItemName );
  1941. }
  1942. //=============================================================================
  1943. // HPE_END
  1944. //=============================================================================
  1945. if ( m_iAccount < 0 )
  1946. m_iAccount = 0;
  1947. else if ( m_iAccount > 16000 )
  1948. m_iAccount = 16000;
  1949. }
  1950. void CCSPlayer::MarkAsNotReceivingMoneyNextRound()
  1951. {
  1952. m_receivesMoneyNextRound = false;
  1953. }
  1954. bool CCSPlayer::DoesPlayerGetRoundStartMoney()
  1955. {
  1956. return m_receivesMoneyNextRound;
  1957. }
  1958. CCSPlayer* CCSPlayer::Instance( int iEnt )
  1959. {
  1960. return dynamic_cast< CCSPlayer* >( CBaseEntity::Instance( INDEXENT( iEnt ) ) );
  1961. }
  1962. void CCSPlayer::DropC4()
  1963. {
  1964. }
  1965. bool CCSPlayer::HasDefuser()
  1966. {
  1967. return m_bHasDefuser;
  1968. }
  1969. void CCSPlayer::RemoveDefuser()
  1970. {
  1971. m_bHasDefuser = false;
  1972. }
  1973. void CCSPlayer::GiveDefuser(bool bPickedUp /* = false */)
  1974. {
  1975. if ( !m_bHasDefuser )
  1976. {
  1977. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  1978. if( event )
  1979. {
  1980. event->SetInt( "userid", GetUserID() );
  1981. event->SetString( "item", "defuser" );
  1982. gameeventmanager->FireEvent( event );
  1983. }
  1984. }
  1985. m_bHasDefuser = true;
  1986. //=============================================================================
  1987. // HPE_BEGIN:
  1988. // [dwenger] Added for fun-fact support
  1989. //=============================================================================
  1990. m_bPickedUpDefuser = bPickedUp;
  1991. //=============================================================================
  1992. // HPE_END
  1993. //=============================================================================
  1994. }
  1995. // player blinded by a flashbang
  1996. void CCSPlayer::Blind( float holdTime, float fadeTime, float startingAlpha )
  1997. {
  1998. // Don't flash a spectator.
  1999. color32 clr = {255, 255, 255, 255};
  2000. clr.a = startingAlpha;
  2001. // estimate when we can see again
  2002. float oldBlindUntilTime = m_blindUntilTime;
  2003. float oldBlindStartTime = m_blindStartTime;
  2004. m_blindUntilTime = MAX( m_blindUntilTime, gpGlobals->curtime + holdTime + 0.5f * fadeTime );
  2005. m_blindStartTime = gpGlobals->curtime;
  2006. // Spectators get a lessened flash.
  2007. if ( (GetObserverMode() != OBS_MODE_NONE) && (GetObserverMode() != OBS_MODE_IN_EYE) )
  2008. {
  2009. if ( !mp_fadetoblack.GetBool() )
  2010. {
  2011. clr.a = 150;
  2012. fadeTime = MIN(fadeTime, 0.5f); // make sure the spectator flashbang time is 1/2 second or less.
  2013. holdTime = MIN(holdTime, fadeTime * 0.5f); // adjust the hold time to match the fade time.
  2014. UTIL_ScreenFade( this, clr, fadeTime, holdTime, FFADE_IN );
  2015. }
  2016. }
  2017. else
  2018. {
  2019. fadeTime /= 1.4;
  2020. if ( gpGlobals->curtime > oldBlindUntilTime )
  2021. {
  2022. // The previous flashbang is wearing off, or completely gone
  2023. m_flFlashDuration = fadeTime;
  2024. m_flFlashMaxAlpha = startingAlpha;
  2025. }
  2026. else
  2027. {
  2028. // The previous flashbang is still going strong - only extend the duration
  2029. float remainingDuration = oldBlindStartTime + m_flFlashDuration - gpGlobals->curtime;
  2030. m_flFlashDuration = MAX( remainingDuration, fadeTime );
  2031. m_flFlashMaxAlpha = MAX( m_flFlashMaxAlpha, startingAlpha );
  2032. }
  2033. // allow bots to react
  2034. IGameEvent * event = gameeventmanager->CreateEvent( "player_blind" );
  2035. if ( event )
  2036. {
  2037. event->SetInt( "userid", GetUserID() );
  2038. gameeventmanager->FireEvent( event );
  2039. }
  2040. }
  2041. }
  2042. void CCSPlayer::Deafen( float flDistance )
  2043. {
  2044. // Spectators don't get deafened
  2045. if ( (GetObserverMode() == OBS_MODE_NONE) || (GetObserverMode() == OBS_MODE_IN_EYE) )
  2046. {
  2047. // dsp presets are defined in hl2/scripts/dsp_presets.txt
  2048. int effect;
  2049. if( flDistance < 600 )
  2050. {
  2051. effect = 134;
  2052. }
  2053. else if( flDistance < 800 )
  2054. {
  2055. effect = 135;
  2056. }
  2057. else if( flDistance < 1000 )
  2058. {
  2059. effect = 136;
  2060. }
  2061. else
  2062. {
  2063. // too far for us to get an effect
  2064. return;
  2065. }
  2066. CSingleUserRecipientFilter user( this );
  2067. enginesound->SetPlayerDSP( user, effect, false );
  2068. //TODO: bots can't hear sound for a while?
  2069. }
  2070. }
  2071. void CCSPlayer::GiveShield( void )
  2072. {
  2073. #ifdef CS_SHIELD_ENABLED
  2074. m_bHasShield = true;
  2075. m_bShieldDrawn = false;
  2076. if ( HasSecondaryWeapon() )
  2077. {
  2078. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2079. pWeapon->SetModel( pWeapon->GetViewModel() );
  2080. pWeapon->Deploy();
  2081. }
  2082. CBaseViewModel *pVM = GetViewModel( 1 );
  2083. if ( pVM )
  2084. {
  2085. ShowViewModel( true );
  2086. pVM->RemoveEffects( EF_NODRAW );
  2087. pVM->SetWeaponModel( SHIELD_VIEW_MODEL, GetActiveWeapon() );
  2088. pVM->SendViewModelMatchingSequence( 1 );
  2089. }
  2090. #endif
  2091. }
  2092. void CCSPlayer::RemoveShield( void )
  2093. {
  2094. #ifdef CS_SHIELD_ENABLED
  2095. m_bHasShield = false;
  2096. CBaseViewModel *pVM = GetViewModel( 1 );
  2097. if ( pVM )
  2098. {
  2099. pVM->AddEffects( EF_NODRAW );
  2100. }
  2101. #endif
  2102. }
  2103. void CCSPlayer::RemoveAllItems( bool removeSuit )
  2104. {
  2105. if( HasDefuser() )
  2106. {
  2107. RemoveDefuser();
  2108. }
  2109. if ( HasShield() )
  2110. {
  2111. RemoveShield();
  2112. }
  2113. m_bHasNightVision = false;
  2114. m_bNightVisionOn = false;
  2115. //=============================================================================
  2116. // HPE_BEGIN:
  2117. // [dwenger] Added for fun-fact support
  2118. //=============================================================================
  2119. m_bPickedUpDefuser = false;
  2120. m_bDefusedWithPickedUpKit = false;
  2121. //=============================================================================
  2122. // HPE_END
  2123. //=============================================================================
  2124. if ( removeSuit )
  2125. {
  2126. m_bHasHelmet = false;
  2127. SetArmorValue( 0 );
  2128. }
  2129. BaseClass::RemoveAllItems( removeSuit );
  2130. }
  2131. void CCSPlayer::ObserverRoundRespawn()
  2132. {
  2133. ClearFlashbangScreenFade();
  2134. // did we change our name last round?
  2135. if ( m_szNewName[0] != 0 )
  2136. {
  2137. // ... and force the name change now. After this happens, the gamerules will get
  2138. // a ClientSettingsChanged callback from the above ClientCommand, but the name
  2139. // matches what we're setting here, so it will do nothing.
  2140. ChangeName( m_szNewName );
  2141. m_szNewName[0] = 0;
  2142. }
  2143. }
  2144. void CCSPlayer::RoundRespawn()
  2145. {
  2146. //MIKETODO: menus
  2147. //if ( m_iMenu != Menu_ChooseAppearance )
  2148. {
  2149. // Put them back into the game.
  2150. StopObserverMode();
  2151. State_Transition( STATE_ACTIVE );
  2152. respawn( this, false );
  2153. m_nButtons = 0;
  2154. SetNextThink( TICK_NEVER_THINK );
  2155. }
  2156. m_receivesMoneyNextRound = true; // reset this variable so they can receive their cash next round.
  2157. //If they didn't die, this will print out their damage info
  2158. OutputDamageGiven();
  2159. OutputDamageTaken();
  2160. ResetDamageCounters();
  2161. }
  2162. void CCSPlayer::CheckTKPunishment( void )
  2163. {
  2164. // teamkill punishment..
  2165. if ( (m_bJustKilledTeammate == true) && mp_tkpunish.GetInt() )
  2166. {
  2167. m_bJustKilledTeammate = false;
  2168. m_bPunishedForTK = true;
  2169. CommitSuicide();
  2170. }
  2171. }
  2172. CWeaponCSBase* CCSPlayer::GetActiveCSWeapon() const
  2173. {
  2174. return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );
  2175. }
  2176. void CCSPlayer::PreThink()
  2177. {
  2178. BaseClass::PreThink();
  2179. if ( m_bAutoReload )
  2180. {
  2181. m_bAutoReload = false;
  2182. m_nButtons |= IN_RELOAD;
  2183. }
  2184. if ( m_afButtonLast != m_nButtons )
  2185. m_flLastMovement = gpGlobals->curtime;
  2186. if ( g_fGameOver )
  2187. return;
  2188. State_PreThink();
  2189. if ( m_pHintMessageQueue )
  2190. m_pHintMessageQueue->Update();
  2191. //Reset bullet force accumulator, only lasts one frame
  2192. m_vecTotalBulletForce = vec3_origin;
  2193. if ( mp_autokick.GetBool() && !IsBot() && !IsHLTV() && !IsAutoKickDisabled() )
  2194. {
  2195. if ( m_flLastMovement + CSGameRules()->GetRoundLength()*2 < gpGlobals->curtime )
  2196. {
  2197. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_idle_kick", GetPlayerName() );
  2198. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", GetUserID() ) );
  2199. m_flLastMovement = gpGlobals->curtime;
  2200. }
  2201. }
  2202. #ifndef _XBOX
  2203. // CS would like their players to continue to update their LastArea since it is displayed in the hud voice chat UI
  2204. // But we won't do the population tracking while dead.
  2205. CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin(), 1000 );
  2206. if (area && area != m_lastNavArea)
  2207. {
  2208. m_lastNavArea = area;
  2209. if ( area->GetPlace() != UNDEFINED_PLACE )
  2210. {
  2211. const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() );
  2212. if ( placeName && *placeName )
  2213. {
  2214. Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH );
  2215. }
  2216. }
  2217. }
  2218. #endif
  2219. }
  2220. void CCSPlayer::MoveToNextIntroCamera()
  2221. {
  2222. m_pIntroCamera = gEntList.FindEntityByClassname( m_pIntroCamera, "point_viewcontrol" );
  2223. // if m_pIntroCamera is NULL we just were at end of list, start searching from start again
  2224. if(!m_pIntroCamera)
  2225. m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "point_viewcontrol");
  2226. // find the target
  2227. CBaseEntity *Target = NULL;
  2228. if( m_pIntroCamera )
  2229. {
  2230. Target = gEntList.FindEntityByName( NULL, STRING(m_pIntroCamera->m_target) );
  2231. }
  2232. // if we still couldn't find a camera, goto T spawn
  2233. if(!m_pIntroCamera)
  2234. m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "info_player_terrorist");
  2235. SetViewOffset( vec3_origin ); // no view offset
  2236. UTIL_SetSize( this, vec3_origin, vec3_origin ); // no bbox
  2237. if( !Target ) //if there are no cameras(or the camera has no target, find a spawn point and black out the screen
  2238. {
  2239. if ( m_pIntroCamera.IsValid() )
  2240. SetAbsOrigin( m_pIntroCamera->GetAbsOrigin() + VEC_VIEW );
  2241. SetAbsAngles( QAngle( 0, 0, 0 ) );
  2242. m_pIntroCamera = NULL; // never update again
  2243. return;
  2244. }
  2245. Vector vCamera = Target->GetAbsOrigin() - m_pIntroCamera->GetAbsOrigin();
  2246. Vector vIntroCamera = m_pIntroCamera->GetAbsOrigin();
  2247. VectorNormalize( vCamera );
  2248. QAngle CamAngles;
  2249. VectorAngles( vCamera, CamAngles );
  2250. SetAbsOrigin( vIntroCamera );
  2251. SetAbsAngles( CamAngles );
  2252. SnapEyeAngles( CamAngles );
  2253. m_fIntroCamTime = gpGlobals->curtime + 6;
  2254. }
  2255. class NotVIP
  2256. {
  2257. public:
  2258. bool operator()( CBasePlayer *player )
  2259. {
  2260. CCSPlayer *csPlayer = static_cast< CCSPlayer * >(player);
  2261. csPlayer->MakeVIP( false );
  2262. return true;
  2263. }
  2264. };
  2265. // Expose the VIP selection to plugins, since we don't have an official VIP mode. This
  2266. // allows plugins to access the (limited) VIP functionality already present (scoreboard
  2267. // identification and radar color).
  2268. CON_COMMAND( cs_make_vip, "Marks a player as the VIP" )
  2269. {
  2270. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2271. return;
  2272. if ( args.ArgC() != 2 )
  2273. {
  2274. return;
  2275. }
  2276. CCSPlayer *player = static_cast< CCSPlayer * >(UTIL_PlayerByIndex( atoi( args[1] ) ));
  2277. if ( !player )
  2278. {
  2279. // Invalid value clears out VIP
  2280. NotVIP notVIP;
  2281. ForEachPlayer( notVIP );
  2282. return;
  2283. }
  2284. player->MakeVIP( true );
  2285. }
  2286. void CCSPlayer::MakeVIP( bool isVIP )
  2287. {
  2288. if ( isVIP )
  2289. {
  2290. NotVIP notVIP;
  2291. ForEachPlayer( notVIP );
  2292. }
  2293. m_isVIP = isVIP;
  2294. }
  2295. bool CCSPlayer::IsVIP() const
  2296. {
  2297. return m_isVIP;
  2298. }
  2299. void CCSPlayer::DropShield( void )
  2300. {
  2301. #ifdef CS_SHIELD_ENABLED
  2302. //Drop an item_defuser
  2303. Vector vForward, vRight;
  2304. AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
  2305. RemoveShield();
  2306. CBaseAnimating *pShield = (CBaseAnimating *)CBaseEntity::Create( "item_shield", WorldSpaceCenter(), GetLocalAngles() );
  2307. pShield->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );
  2308. CBaseCombatWeapon *pActive = GetActiveWeapon();
  2309. if ( pActive )
  2310. {
  2311. pActive->Deploy();
  2312. }
  2313. #endif
  2314. }
  2315. void CCSPlayer::SetShieldDrawnState( bool bState )
  2316. {
  2317. #ifdef CS_SHIELD_ENABLED
  2318. m_bShieldDrawn = bState;
  2319. #endif
  2320. }
  2321. bool CCSPlayer::CSWeaponDrop( CBaseCombatWeapon *pWeapon, bool bDropShield, bool bThrowForward )
  2322. {
  2323. bool bSuccess = false;
  2324. if ( HasShield() && bDropShield == true )
  2325. {
  2326. DropShield();
  2327. return true;
  2328. }
  2329. if ( pWeapon )
  2330. {
  2331. Vector vForward;
  2332. AngleVectors( EyeAngles(), &vForward, NULL, NULL );
  2333. //GetVectors( &vForward, NULL, NULL );
  2334. Vector vTossPos = WorldSpaceCenter();
  2335. if( bThrowForward )
  2336. vTossPos = vTossPos + vForward * 64;
  2337. Weapon_Drop( pWeapon, &vTossPos, NULL );
  2338. pWeapon->SetSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER | FSOLID_USE_TRIGGER_BOUNDS );
  2339. pWeapon->SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );
  2340. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
  2341. if( pCSWeapon )
  2342. {
  2343. pCSWeapon->SetWeaponModelIndex( pCSWeapon->GetCSWpnData().szWorldModel );
  2344. //Find out the index of the ammo type
  2345. int iAmmoIndex = pCSWeapon->GetPrimaryAmmoType();
  2346. //If it has an ammo type, find out how much the player has
  2347. if( iAmmoIndex != -1 )
  2348. {
  2349. // Check to make sure we don't have other weapons using this ammo type
  2350. bool bAmmoTypeInUse = false;
  2351. if ( IsAlive() && GetHealth() > 0 )
  2352. {
  2353. for ( int i=0; i<MAX_WEAPONS; ++i )
  2354. {
  2355. CBaseCombatWeapon *pOtherWeapon = GetWeapon(i);
  2356. if ( pOtherWeapon && pOtherWeapon != pWeapon && pOtherWeapon->GetPrimaryAmmoType() == iAmmoIndex )
  2357. {
  2358. bAmmoTypeInUse = true;
  2359. break;
  2360. }
  2361. }
  2362. }
  2363. if ( !bAmmoTypeInUse )
  2364. {
  2365. int iAmmoToDrop = GetAmmoCount( iAmmoIndex );
  2366. //Add this much to the dropped weapon
  2367. pCSWeapon->SetExtraAmmoCount( iAmmoToDrop );
  2368. //Remove all ammo of this type from the player
  2369. SetAmmoCount( 0, iAmmoIndex );
  2370. }
  2371. }
  2372. }
  2373. //=========================================
  2374. // Teleport the weapon to the player's hand
  2375. //=========================================
  2376. int iBIndex = -1;
  2377. int iWeaponBoneIndex = -1;
  2378. MDLCACHE_CRITICAL_SECTION();
  2379. CStudioHdr *hdr = pWeapon->GetModelPtr();
  2380. // If I have a hand, set the weapon position to my hand bone position.
  2381. if ( hdr && hdr->numbones() > 0 )
  2382. {
  2383. // Assume bone zero is the root
  2384. for ( iWeaponBoneIndex = 0; iWeaponBoneIndex < hdr->numbones(); ++iWeaponBoneIndex )
  2385. {
  2386. iBIndex = LookupBone( hdr->pBone( iWeaponBoneIndex )->pszName() );
  2387. // Found one!
  2388. if ( iBIndex != -1 )
  2389. {
  2390. break;
  2391. }
  2392. }
  2393. if ( iWeaponBoneIndex == hdr->numbones() )
  2394. return true;
  2395. if ( iBIndex == -1 )
  2396. {
  2397. iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
  2398. }
  2399. }
  2400. else
  2401. {
  2402. iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
  2403. }
  2404. if ( iBIndex != -1)
  2405. {
  2406. Vector origin;
  2407. QAngle angles;
  2408. matrix3x4_t transform;
  2409. // Get the transform for the weapon bonetoworldspace in the NPC
  2410. GetBoneTransform( iBIndex, transform );
  2411. // find offset of root bone from origin in local space
  2412. // Make sure we're detached from hierarchy before doing this!!!
  2413. pWeapon->StopFollowingEntity();
  2414. pWeapon->SetAbsOrigin( Vector( 0, 0, 0 ) );
  2415. pWeapon->SetAbsAngles( QAngle( 0, 0, 0 ) );
  2416. pWeapon->InvalidateBoneCache();
  2417. matrix3x4_t rootLocal;
  2418. pWeapon->GetBoneTransform( iWeaponBoneIndex, rootLocal );
  2419. // invert it
  2420. matrix3x4_t rootInvLocal;
  2421. MatrixInvert( rootLocal, rootInvLocal );
  2422. matrix3x4_t weaponMatrix;
  2423. ConcatTransforms( transform, rootInvLocal, weaponMatrix );
  2424. MatrixAngles( weaponMatrix, angles, origin );
  2425. pWeapon->Teleport( &origin, &angles, NULL );
  2426. //Have to teleport the physics object as well
  2427. IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject();
  2428. if( pWeaponPhys )
  2429. {
  2430. Vector vPos;
  2431. QAngle vAngles;
  2432. pWeaponPhys->GetPosition( &vPos, &vAngles );
  2433. pWeaponPhys->SetPosition( vPos, angles, true );
  2434. AngularImpulse angImp(0,0,0);
  2435. Vector vecAdd = GetAbsVelocity();
  2436. pWeaponPhys->AddVelocity( &vecAdd, &angImp );
  2437. }
  2438. }
  2439. bSuccess = true;
  2440. }
  2441. return bSuccess;
  2442. }
  2443. bool CCSPlayer::DropRifle( bool fromDeath )
  2444. {
  2445. bool bSuccess = false;
  2446. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  2447. if ( pWeapon )
  2448. {
  2449. bSuccess = CSWeaponDrop( pWeapon, false );
  2450. }
  2451. //=============================================================================
  2452. // HPE_BEGIN:
  2453. // [menglish] Add the dropped weapon to the dropped equipment list
  2454. //=============================================================================
  2455. if( fromDeath && bSuccess )
  2456. {
  2457. m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon);
  2458. }
  2459. //=============================================================================
  2460. // HPE_END
  2461. //=============================================================================
  2462. return bSuccess;
  2463. }
  2464. bool CCSPlayer::DropPistol( bool fromDeath )
  2465. {
  2466. bool bSuccess = false;
  2467. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2468. if ( pWeapon )
  2469. {
  2470. bSuccess = CSWeaponDrop( pWeapon, false );
  2471. m_bUsingDefaultPistol = false;
  2472. }
  2473. //=============================================================================
  2474. // HPE_BEGIN:
  2475. // [menglish] Add the dropped weapon to the dropped equipment list
  2476. //=============================================================================
  2477. if( fromDeath && bSuccess )
  2478. {
  2479. m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon);
  2480. }
  2481. //=============================================================================
  2482. // HPE_END
  2483. //=============================================================================
  2484. return bSuccess;
  2485. }
  2486. bool CCSPlayer::HasPrimaryWeapon( void )
  2487. {
  2488. bool bSuccess = false;
  2489. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  2490. if ( pWeapon )
  2491. {
  2492. bSuccess = true;
  2493. }
  2494. return bSuccess;
  2495. }
  2496. bool CCSPlayer::HasSecondaryWeapon( void )
  2497. {
  2498. bool bSuccess = false;
  2499. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2500. if ( pWeapon )
  2501. {
  2502. bSuccess = true;
  2503. }
  2504. return bSuccess;
  2505. }
  2506. bool CCSPlayer::IsInBuyZone()
  2507. {
  2508. return m_bInBuyZone && !IsVIP();
  2509. }
  2510. bool CCSPlayer::CanPlayerBuy( bool display )
  2511. {
  2512. // is the player in a buy zone?
  2513. if ( !IsInBuyZone() )
  2514. {
  2515. return false;
  2516. }
  2517. CCSGameRules* mp = CSGameRules();
  2518. // is the player alive?
  2519. if ( m_lifeState != LIFE_ALIVE )
  2520. {
  2521. return false;
  2522. }
  2523. int buyTime = (int)(mp_buytime.GetFloat() * 60);
  2524. if ( mp->IsBuyTimeElapsed() )
  2525. {
  2526. if ( display == true )
  2527. {
  2528. char strBuyTime[16];
  2529. Q_snprintf( strBuyTime, sizeof( strBuyTime ), "%d", buyTime );
  2530. ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy", strBuyTime );
  2531. }
  2532. return false;
  2533. }
  2534. if ( m_bIsVIP )
  2535. {
  2536. if ( display == true )
  2537. ClientPrint( this, HUD_PRINTCENTER, "#VIP_cant_buy" );
  2538. return false;
  2539. }
  2540. if ( mp->m_bCTCantBuy && ( GetTeamNumber() == TEAM_CT ) )
  2541. {
  2542. if ( display == true )
  2543. ClientPrint( this, HUD_PRINTCENTER, "#CT_cant_buy" );
  2544. return false;
  2545. }
  2546. if ( mp->m_bTCantBuy && ( GetTeamNumber() == TEAM_TERRORIST ) )
  2547. {
  2548. if ( display == true )
  2549. ClientPrint( this, HUD_PRINTCENTER, "#Terrorist_cant_buy" );
  2550. return false;
  2551. }
  2552. return true;
  2553. }
  2554. BuyResult_e CCSPlayer::AttemptToBuyVest( void )
  2555. {
  2556. int iKevlarPrice = KEVLAR_PRICE;
  2557. if ( CSGameRules()->IsBlackMarket() )
  2558. {
  2559. iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
  2560. }
  2561. if ( ArmorValue() >= 100 )
  2562. {
  2563. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2564. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar" );
  2565. return BUY_ALREADY_HAVE;
  2566. }
  2567. else if ( m_iAccount < iKevlarPrice )
  2568. {
  2569. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2570. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2571. return BUY_CANT_AFFORD;
  2572. }
  2573. else
  2574. {
  2575. if ( m_bHasHelmet )
  2576. {
  2577. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2578. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
  2579. }
  2580. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  2581. if( event )
  2582. {
  2583. event->SetInt( "userid", GetUserID() );
  2584. event->SetString( "item", "vest" );
  2585. gameeventmanager->FireEvent( event );
  2586. }
  2587. GiveNamedItem( "item_kevlar" );
  2588. AddAccount( -iKevlarPrice, true, true, "item_kevlar" );
  2589. BlackMarketAddWeapon( "item_kevlar", this );
  2590. return BUY_BOUGHT;
  2591. }
  2592. }
  2593. BuyResult_e CCSPlayer::AttemptToBuyAssaultSuit( void )
  2594. {
  2595. // WARNING: This price logic also exists in C_CSPlayer::GetCurrentAssaultSuitPrice
  2596. // and must be kept in sync if changes are made.
  2597. int fullArmor = ArmorValue() >= 100 ? 1 : 0;
  2598. int price = 0, enoughMoney = 0;
  2599. int iHelmetPrice = HELMET_PRICE;
  2600. int iKevlarPrice = KEVLAR_PRICE;
  2601. int iAssaultSuitPrice = ASSAULTSUIT_PRICE;
  2602. if ( CSGameRules()->IsBlackMarket() )
  2603. {
  2604. iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
  2605. iAssaultSuitPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT );
  2606. iHelmetPrice = iAssaultSuitPrice - iKevlarPrice;
  2607. }
  2608. if ( fullArmor && m_bHasHelmet )
  2609. {
  2610. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2611. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Helmet" );
  2612. return BUY_ALREADY_HAVE;
  2613. }
  2614. else if ( fullArmor && !m_bHasHelmet && m_iAccount >= iHelmetPrice )
  2615. {
  2616. enoughMoney = 1;
  2617. price = iHelmetPrice;
  2618. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2619. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Bought_Helmet" );
  2620. }
  2621. else if ( !fullArmor && m_bHasHelmet && m_iAccount >= iKevlarPrice )
  2622. {
  2623. enoughMoney = 1;
  2624. price = iKevlarPrice;
  2625. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2626. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
  2627. }
  2628. else if ( m_iAccount >= iAssaultSuitPrice )
  2629. {
  2630. enoughMoney = 1;
  2631. price = iAssaultSuitPrice;
  2632. }
  2633. // process the result
  2634. if ( !enoughMoney )
  2635. {
  2636. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2637. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2638. return BUY_CANT_AFFORD;
  2639. }
  2640. else
  2641. {
  2642. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  2643. if( event )
  2644. {
  2645. event->SetInt( "userid", GetUserID() );
  2646. event->SetString( "item", "vesthelm" );
  2647. gameeventmanager->FireEvent( event );
  2648. }
  2649. GiveNamedItem( "item_assaultsuit" );
  2650. AddAccount( -price, true, true, "item_assaultsuit" );
  2651. BlackMarketAddWeapon( "item_assaultsuit", this );
  2652. return BUY_BOUGHT;
  2653. }
  2654. }
  2655. BuyResult_e CCSPlayer::AttemptToBuyShield( void )
  2656. {
  2657. #ifdef CS_SHIELD_ENABLED
  2658. if ( HasShield() ) // prevent this guy from buying more than 1 Defuse Kit
  2659. {
  2660. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2661. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
  2662. return BUY_ALREADY_HAVE;
  2663. }
  2664. else if ( m_iAccount < SHIELD_PRICE )
  2665. {
  2666. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2667. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2668. return BUY_CANT_AFFORD;
  2669. }
  2670. else
  2671. {
  2672. if ( HasSecondaryWeapon() )
  2673. {
  2674. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2675. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
  2676. if ( pCSWeapon && pCSWeapon->GetCSWpnData().m_bCanUseWithShield == false )
  2677. return;
  2678. }
  2679. if ( HasPrimaryWeapon() )
  2680. DropRifle();
  2681. GiveShield();
  2682. CPASAttenuationFilter filter( this, "Player.PickupWeapon" );
  2683. EmitSound( filter, entindex(), "Player.PickupWeapon" );
  2684. m_bAnythingBought = true;
  2685. AddAccount( -SHIELD_PRICE, true, true, "item_shield" );
  2686. return BUY_BOUGHT;
  2687. }
  2688. #else
  2689. ClientPrint( this, HUD_PRINTCENTER, "Tactical shield disabled" );
  2690. return BUY_NOT_ALLOWED;
  2691. #endif
  2692. }
  2693. BuyResult_e CCSPlayer::AttemptToBuyDefuser( void )
  2694. {
  2695. CCSGameRules *MPRules = CSGameRules();
  2696. if( ( GetTeamNumber() == TEAM_CT ) && MPRules->IsBombDefuseMap() )
  2697. {
  2698. if ( HasDefuser() ) // prevent this guy from buying more than 1 Defuse Kit
  2699. {
  2700. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2701. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
  2702. return BUY_ALREADY_HAVE;
  2703. }
  2704. else if ( m_iAccount < DEFUSEKIT_PRICE )
  2705. {
  2706. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2707. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2708. return BUY_CANT_AFFORD;
  2709. }
  2710. else
  2711. {
  2712. GiveDefuser();
  2713. CPASAttenuationFilter filter( this, "Player.PickupWeapon" );
  2714. EmitSound( filter, entindex(), "Player.PickupWeapon" );
  2715. AddAccount( -DEFUSEKIT_PRICE, true, true, "item_defuser" );
  2716. return BUY_BOUGHT;
  2717. }
  2718. }
  2719. return BUY_NOT_ALLOWED;
  2720. }
  2721. BuyResult_e CCSPlayer::AttemptToBuyNightVision( void )
  2722. {
  2723. int iNVGPrice = NVG_PRICE;
  2724. if ( CSGameRules()->IsBlackMarket() )
  2725. {
  2726. iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG );
  2727. }
  2728. if ( m_bHasNightVision == TRUE )
  2729. {
  2730. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2731. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
  2732. return BUY_ALREADY_HAVE;
  2733. }
  2734. else if ( m_iAccount < iNVGPrice )
  2735. {
  2736. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2737. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2738. return BUY_CANT_AFFORD;
  2739. }
  2740. else
  2741. {
  2742. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  2743. if( event )
  2744. {
  2745. event->SetInt( "userid", GetUserID() );
  2746. event->SetString( "item", "nvgs" );
  2747. gameeventmanager->FireEvent( event );
  2748. }
  2749. GiveNamedItem( "item_nvgs" );
  2750. AddAccount( -iNVGPrice, true, true );
  2751. BlackMarketAddWeapon( "nightvision", this );
  2752. if ( !(m_iDisplayHistoryBits & DHF_NIGHTVISION) )
  2753. {
  2754. HintMessage( "#Hint_use_nightvision", false );
  2755. m_iDisplayHistoryBits |= DHF_NIGHTVISION;
  2756. }
  2757. return BUY_BOUGHT;
  2758. }
  2759. }
  2760. // Handles the special "buy" alias commands we're creating to accommodate the buy
  2761. // scripts players use (now that we've rearranged the buy menus and broken the scripts)
  2762. //=============================================================================
  2763. // HPE_BEGIN:
  2764. //[tj] This is essentially a shim so I can easily check the return
  2765. // value without adding new code to all the return points.
  2766. //=============================================================================
  2767. BuyResult_e CCSPlayer::HandleCommand_Buy( const char *item )
  2768. {
  2769. BuyResult_e result = HandleCommand_Buy_Internal(item);
  2770. if (result == BUY_BOUGHT)
  2771. {
  2772. m_bMadePurchseThisRound = true;
  2773. CCS_GameStats.IncrementStat(this, CSSTAT_ITEMS_PURCHASED, 1);
  2774. }
  2775. return result;
  2776. }
  2777. BuyResult_e CCSPlayer::HandleCommand_Buy_Internal( const char* wpnName )
  2778. //=============================================================================
  2779. // HPE_END
  2780. //=============================================================================
  2781. {
  2782. BuyResult_e result = CanPlayerBuy( false ) ? BUY_PLAYER_CANT_BUY : BUY_INVALID_ITEM; // set some defaults
  2783. // translate the new weapon names to the old ones that are actually being used.
  2784. wpnName = GetTranslatedWeaponAlias(wpnName);
  2785. CCSWeaponInfo *pWeaponInfo = GetWeaponInfo( AliasToWeaponID( wpnName ) );
  2786. if ( pWeaponInfo == NULL )
  2787. {
  2788. if ( Q_stricmp( wpnName, "primammo" ) == 0 )
  2789. {
  2790. result = AttemptToBuyAmmo( 0 );
  2791. }
  2792. else if ( Q_stricmp( wpnName, "secammo" ) == 0 )
  2793. {
  2794. result = AttemptToBuyAmmo( 1 );
  2795. }
  2796. else if ( Q_stristr( wpnName, "defuser" ) )
  2797. {
  2798. if( CanPlayerBuy( true ) )
  2799. {
  2800. result = AttemptToBuyDefuser();
  2801. }
  2802. }
  2803. }
  2804. else
  2805. {
  2806. if( !CanPlayerBuy( true ) )
  2807. {
  2808. return BUY_PLAYER_CANT_BUY;
  2809. }
  2810. BuyResult_e equipResult = BUY_INVALID_ITEM;
  2811. if ( Q_stristr( wpnName, "kevlar" ) )
  2812. {
  2813. equipResult = AttemptToBuyVest();
  2814. }
  2815. else if ( Q_stristr( wpnName, "assaultsuit" ) )
  2816. {
  2817. equipResult = AttemptToBuyAssaultSuit();
  2818. }
  2819. else if ( Q_stristr( wpnName, "shield" ) )
  2820. {
  2821. equipResult = AttemptToBuyShield();
  2822. }
  2823. else if ( Q_stristr( wpnName, "nightvision" ) )
  2824. {
  2825. equipResult = AttemptToBuyNightVision();
  2826. }
  2827. if ( equipResult != BUY_INVALID_ITEM )
  2828. {
  2829. if ( equipResult == BUY_BOUGHT )
  2830. {
  2831. BuildRebuyStruct();
  2832. }
  2833. return equipResult; // intentional early return here
  2834. }
  2835. bool bPurchase = false;
  2836. // MIKETODO: assasination maps have a specific set of weapons that can be used in them.
  2837. if ( pWeaponInfo->m_iTeam != TEAM_UNASSIGNED && GetTeamNumber() != pWeaponInfo->m_iTeam )
  2838. {
  2839. result = BUY_NOT_ALLOWED;
  2840. if ( pWeaponInfo->m_WrongTeamMsg[0] != 0 )
  2841. {
  2842. ClientPrint( this, HUD_PRINTCENTER, "#Alias_Not_Avail", pWeaponInfo->m_WrongTeamMsg );
  2843. }
  2844. }
  2845. else if ( pWeaponInfo->GetWeaponPrice() <= 0 )
  2846. {
  2847. // ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy_this_item", pWeaponInfo->m_WrongTeamMsg );
  2848. }
  2849. else if( pWeaponInfo->m_WeaponType == WEAPONTYPE_GRENADE )
  2850. {
  2851. // make sure the player can afford this item.
  2852. if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() )
  2853. {
  2854. bPurchase = true;
  2855. const char *szWeaponName = NULL;
  2856. int ammoMax = 0;
  2857. if ( Q_strstr( pWeaponInfo->szClassName, "flashbang" ) )
  2858. {
  2859. szWeaponName = "weapon_flashbang";
  2860. ammoMax = ammo_flashbang_max.GetInt();
  2861. }
  2862. else if ( Q_strstr( pWeaponInfo->szClassName, "hegrenade" ) )
  2863. {
  2864. szWeaponName = "weapon_hegrenade";
  2865. ammoMax = ammo_hegrenade_max.GetInt();
  2866. }
  2867. else if ( Q_strstr( pWeaponInfo->szClassName, "smokegrenade" ) )
  2868. {
  2869. szWeaponName = "weapon_smokegrenade";
  2870. ammoMax = ammo_smokegrenade_max.GetInt();
  2871. }
  2872. if ( szWeaponName != NULL )
  2873. {
  2874. CBaseCombatWeapon* pGrenadeWeapon = Weapon_OwnsThisType( szWeaponName );
  2875. {
  2876. if ( pGrenadeWeapon != NULL )
  2877. {
  2878. int nAmmoType = pGrenadeWeapon->GetPrimaryAmmoType();
  2879. if( nAmmoType != -1 )
  2880. {
  2881. if( GetAmmoCount(nAmmoType) >= ammoMax )
  2882. {
  2883. result = BUY_ALREADY_HAVE;
  2884. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2885. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Carry_Anymore" );
  2886. bPurchase = false;
  2887. }
  2888. }
  2889. }
  2890. }
  2891. }
  2892. }
  2893. }
  2894. else if ( !Weapon_OwnsThisType( pWeaponInfo->szClassName ) ) //don't buy duplicate weapons
  2895. {
  2896. // do they have enough money?
  2897. if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() )
  2898. {
  2899. if ( m_lifeState != LIFE_DEAD )
  2900. {
  2901. if ( pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
  2902. {
  2903. DropPistol();
  2904. }
  2905. else if ( pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE )
  2906. {
  2907. DropRifle();
  2908. }
  2909. }
  2910. bPurchase = true;
  2911. }
  2912. else
  2913. {
  2914. result = BUY_CANT_AFFORD;
  2915. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2916. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2917. }
  2918. }
  2919. else
  2920. {
  2921. result = BUY_ALREADY_HAVE;
  2922. }
  2923. if ( HasShield() )
  2924. {
  2925. if ( pWeaponInfo->m_bCanUseWithShield == false )
  2926. {
  2927. result = BUY_NOT_ALLOWED;
  2928. bPurchase = false;
  2929. }
  2930. }
  2931. if( bPurchase )
  2932. {
  2933. result = BUY_BOUGHT;
  2934. if ( bPurchase && pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
  2935. m_bUsingDefaultPistol = false;
  2936. GiveNamedItem( pWeaponInfo->szClassName );
  2937. AddAccount( -pWeaponInfo->GetWeaponPrice(), true, true, pWeaponInfo->szClassName );
  2938. BlackMarketAddWeapon( wpnName, this );
  2939. }
  2940. }
  2941. if ( result == BUY_BOUGHT )
  2942. {
  2943. BuildRebuyStruct();
  2944. }
  2945. return result;
  2946. }
  2947. BuyResult_e CCSPlayer::BuyGunAmmo( CBaseCombatWeapon *pWeapon, bool bBlinkMoney )
  2948. {
  2949. if ( !CanPlayerBuy( false ) )
  2950. {
  2951. return BUY_PLAYER_CANT_BUY;
  2952. }
  2953. // Ensure that the weapon uses ammo
  2954. int nAmmo = pWeapon->GetPrimaryAmmoType();
  2955. if ( nAmmo == -1 )
  2956. {
  2957. return BUY_ALREADY_HAVE;
  2958. }
  2959. // Can only buy if the player does not already have full ammo
  2960. if ( GetAmmoCount( nAmmo ) >= GetAmmoDef()->MaxCarry( nAmmo ) )
  2961. {
  2962. return BUY_ALREADY_HAVE;
  2963. }
  2964. // Purchase the ammo if the player has enough money
  2965. if ( m_iAccount >= GetCSAmmoDef()->GetCost( nAmmo ) )
  2966. {
  2967. GiveAmmo( GetCSAmmoDef()->GetBuySize( nAmmo ), nAmmo, true );
  2968. AddAccount( -GetCSAmmoDef()->GetCost( nAmmo ), true, true, GetCSAmmoDef()->GetAmmoOfIndex( nAmmo )->pName );
  2969. return BUY_BOUGHT;
  2970. }
  2971. if ( bBlinkMoney )
  2972. {
  2973. // Not enough money.. let the player know
  2974. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  2975. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  2976. }
  2977. return BUY_CANT_AFFORD;
  2978. }
  2979. BuyResult_e CCSPlayer::BuyAmmo( int nSlot, bool bBlinkMoney )
  2980. {
  2981. if ( !CanPlayerBuy( false ) )
  2982. {
  2983. return BUY_PLAYER_CANT_BUY;
  2984. }
  2985. if ( nSlot < 0 || nSlot > 1 )
  2986. {
  2987. return BUY_INVALID_ITEM;
  2988. }
  2989. // Buy one ammo clip for all weapons in the given slot
  2990. //
  2991. // nSlot == 1 : Primary weapons
  2992. // nSlot == 2 : Secondary weapons
  2993. CBaseCombatWeapon *pSlot = Weapon_GetSlot( nSlot );
  2994. if ( !pSlot )
  2995. return BUY_INVALID_ITEM;
  2996. //MIKETODO: shield.
  2997. //if ( player->HasShield() && player->m_rgpPlayerItems[2] )
  2998. // pItem = player->m_rgpPlayerItems[2];
  2999. return BuyGunAmmo( pSlot, bBlinkMoney );
  3000. }
  3001. BuyResult_e CCSPlayer::AttemptToBuyAmmo( int iAmmoType )
  3002. {
  3003. Assert( iAmmoType == 0 || iAmmoType == 1 );
  3004. BuyResult_e result = BuyAmmo( iAmmoType, true );
  3005. if ( result == BUY_BOUGHT )
  3006. {
  3007. while ( BuyAmmo( iAmmoType, false ) == BUY_BOUGHT )
  3008. {
  3009. // empty loop - keep buying
  3010. }
  3011. return BUY_BOUGHT;
  3012. }
  3013. return result;
  3014. }
  3015. BuyResult_e CCSPlayer::AttemptToBuyAmmoSingle( int iAmmoType )
  3016. {
  3017. Assert( iAmmoType == 0 || iAmmoType == 1 );
  3018. BuyResult_e result = BuyAmmo( iAmmoType, true );
  3019. if ( result == BUY_BOUGHT )
  3020. {
  3021. BuildRebuyStruct();
  3022. }
  3023. return result;
  3024. }
  3025. const char *RadioEventName[ RADIO_NUM_EVENTS+1 ] =
  3026. {
  3027. "RADIO_INVALID",
  3028. "EVENT_START_RADIO_1",
  3029. "EVENT_RADIO_COVER_ME",
  3030. "EVENT_RADIO_YOU_TAKE_THE_POINT",
  3031. "EVENT_RADIO_HOLD_THIS_POSITION",
  3032. "EVENT_RADIO_REGROUP_TEAM",
  3033. "EVENT_RADIO_FOLLOW_ME",
  3034. "EVENT_RADIO_TAKING_FIRE",
  3035. "EVENT_START_RADIO_2",
  3036. "EVENT_RADIO_GO_GO_GO",
  3037. "EVENT_RADIO_TEAM_FALL_BACK",
  3038. "EVENT_RADIO_STICK_TOGETHER_TEAM",
  3039. "EVENT_RADIO_GET_IN_POSITION_AND_WAIT",
  3040. "EVENT_RADIO_STORM_THE_FRONT",
  3041. "EVENT_RADIO_REPORT_IN_TEAM",
  3042. "EVENT_START_RADIO_3",
  3043. "EVENT_RADIO_AFFIRMATIVE",
  3044. "EVENT_RADIO_ENEMY_SPOTTED",
  3045. "EVENT_RADIO_NEED_BACKUP",
  3046. "EVENT_RADIO_SECTOR_CLEAR",
  3047. "EVENT_RADIO_IN_POSITION",
  3048. "EVENT_RADIO_REPORTING_IN",
  3049. "EVENT_RADIO_GET_OUT_OF_THERE",
  3050. "EVENT_RADIO_NEGATIVE",
  3051. "EVENT_RADIO_ENEMY_DOWN",
  3052. "EVENT_RADIO_END",
  3053. NULL // must be NULL-terminated
  3054. };
  3055. /**
  3056. * Convert name to RadioType
  3057. */
  3058. RadioType NameToRadioEvent( const char *name )
  3059. {
  3060. for( int i=0; RadioEventName[i]; ++i )
  3061. if (!stricmp( RadioEventName[i], name ))
  3062. return static_cast<RadioType>( i );
  3063. return RADIO_INVALID;
  3064. }
  3065. void CCSPlayer::HandleMenu_Radio1( int slot )
  3066. {
  3067. if( m_iRadioMessages < 0 )
  3068. return;
  3069. if( m_flRadioTime > gpGlobals->curtime )
  3070. return;
  3071. m_iRadioMessages--;
  3072. m_flRadioTime = gpGlobals->curtime + 1.5;
  3073. switch ( slot )
  3074. {
  3075. case 1 :
  3076. Radio( "Radio.CoverMe", "#Cstrike_TitlesTXT_Cover_me" );
  3077. break;
  3078. case 2 :
  3079. Radio( "Radio.YouTakeThePoint", "#Cstrike_TitlesTXT_You_take_the_point" );
  3080. break;
  3081. case 3 :
  3082. Radio( "Radio.HoldPosition", "#Cstrike_TitlesTXT_Hold_this_position" );
  3083. break;
  3084. case 4 :
  3085. Radio( "Radio.Regroup", "#Cstrike_TitlesTXT_Regroup_team" );
  3086. break;
  3087. case 5 :
  3088. Radio( "Radio.FollowMe", "#Cstrike_TitlesTXT_Follow_me" );
  3089. break;
  3090. case 6 :
  3091. Radio( "Radio.TakingFire", "#Cstrike_TitlesTXT_Taking_fire" );
  3092. break;
  3093. }
  3094. // tell bots about radio message
  3095. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  3096. if ( event )
  3097. {
  3098. event->SetInt("userid", GetUserID() );
  3099. event->SetInt("slot", RADIO_START_1 + slot );
  3100. gameeventmanager->FireEvent( event );
  3101. }
  3102. }
  3103. void CCSPlayer::HandleMenu_Radio2( int slot )
  3104. {
  3105. if( m_iRadioMessages < 0 )
  3106. return;
  3107. if( m_flRadioTime > gpGlobals->curtime )
  3108. return;
  3109. m_iRadioMessages--;
  3110. m_flRadioTime = gpGlobals->curtime + 1.5;
  3111. switch ( slot )
  3112. {
  3113. case 1 :
  3114. Radio( "Radio.GoGoGo", "#Cstrike_TitlesTXT_Go_go_go" );
  3115. break;
  3116. case 2 :
  3117. Radio( "Radio.TeamFallBack", "#Cstrike_TitlesTXT_Team_fall_back" );
  3118. break;
  3119. case 3 :
  3120. Radio( "Radio.StickTogether", "#Cstrike_TitlesTXT_Stick_together_team" );
  3121. break;
  3122. case 4 :
  3123. Radio( "Radio.GetInPosition", "#Cstrike_TitlesTXT_Get_in_position_and_wait" );
  3124. break;
  3125. case 5 :
  3126. Radio( "Radio.StormFront", "#Cstrike_TitlesTXT_Storm_the_front" );
  3127. break;
  3128. case 6 :
  3129. Radio( "Radio.ReportInTeam", "#Cstrike_TitlesTXT_Report_in_team" );
  3130. break;
  3131. }
  3132. // tell bots about radio message
  3133. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  3134. if ( event )
  3135. {
  3136. event->SetInt("userid", GetUserID() );
  3137. event->SetInt("slot", RADIO_START_2 + slot );
  3138. gameeventmanager->FireEvent( event );
  3139. }
  3140. }
  3141. void CCSPlayer::HandleMenu_Radio3( int slot )
  3142. {
  3143. if( m_iRadioMessages < 0 )
  3144. return;
  3145. if( m_flRadioTime > gpGlobals->curtime )
  3146. return;
  3147. m_iRadioMessages--;
  3148. m_flRadioTime = gpGlobals->curtime + 1.5;
  3149. switch ( slot )
  3150. {
  3151. case 1 :
  3152. if ( random->RandomInt( 0,1 ) )
  3153. Radio( "Radio.Affirmitive", "#Cstrike_TitlesTXT_Affirmative" );
  3154. else
  3155. Radio( "Radio.Roger", "#Cstrike_TitlesTXT_Roger_that" );
  3156. break;
  3157. case 2 :
  3158. Radio( "Radio.EnemySpotted", "#Cstrike_TitlesTXT_Enemy_spotted" );
  3159. break;
  3160. case 3 :
  3161. Radio( "Radio.NeedBackup", "#Cstrike_TitlesTXT_Need_backup" );
  3162. break;
  3163. case 4 :
  3164. Radio( "Radio.SectorClear", "#Cstrike_TitlesTXT_Sector_clear" );
  3165. break;
  3166. case 5 :
  3167. Radio( "Radio.InPosition", "#Cstrike_TitlesTXT_In_position" );
  3168. break;
  3169. case 6 :
  3170. Radio( "Radio.ReportingIn", "#Cstrike_TitlesTXT_Reporting_in" );
  3171. break;
  3172. case 7 :
  3173. Radio( "Radio.GetOutOfThere", "#Cstrike_TitlesTXT_Get_out_of_there" );
  3174. break;
  3175. case 8 :
  3176. Radio( "Radio.Negative", "#Cstrike_TitlesTXT_Negative" );
  3177. break;
  3178. case 9 :
  3179. Radio( "Radio.EnemyDown", "#Cstrike_TitlesTXT_Enemy_down" );
  3180. break;
  3181. }
  3182. // tell bots about radio message
  3183. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  3184. if ( event )
  3185. {
  3186. event->SetInt("userid", GetUserID() );
  3187. event->SetInt("slot", RADIO_START_3 + slot );
  3188. gameeventmanager->FireEvent( event );
  3189. }
  3190. }
  3191. void UTIL_CSRadioMessage( IRecipientFilter& filter, int iClient, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL )
  3192. {
  3193. UserMessageBegin( filter, "RadioText" );
  3194. WRITE_BYTE( msg_dest );
  3195. WRITE_BYTE( iClient );
  3196. WRITE_STRING( msg_name );
  3197. if ( param1 )
  3198. WRITE_STRING( param1 );
  3199. else
  3200. WRITE_STRING( "" );
  3201. if ( param2 )
  3202. WRITE_STRING( param2 );
  3203. else
  3204. WRITE_STRING( "" );
  3205. if ( param3 )
  3206. WRITE_STRING( param3 );
  3207. else
  3208. WRITE_STRING( "" );
  3209. if ( param4 )
  3210. WRITE_STRING( param4 );
  3211. else
  3212. WRITE_STRING( "" );
  3213. MessageEnd();
  3214. }
  3215. void CCSPlayer::ConstructRadioFilter( CRecipientFilter& filter )
  3216. {
  3217. filter.MakeReliable();
  3218. int localTeam = GetTeamNumber();
  3219. int i;
  3220. for ( i = 1; i <= gpGlobals->maxClients; ++i )
  3221. {
  3222. CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) );
  3223. if ( !player )
  3224. continue;
  3225. // Skip players ignoring the radio
  3226. if ( player->m_bIgnoreRadio )
  3227. continue;
  3228. if( player->GetTeamNumber() == TEAM_SPECTATOR )
  3229. {
  3230. // add spectators
  3231. if( player->m_iObserverMode == OBS_MODE_IN_EYE || player->m_iObserverMode == OBS_MODE_CHASE )
  3232. {
  3233. filter.AddRecipient( player );
  3234. }
  3235. }
  3236. else if( player->GetTeamNumber() == localTeam )
  3237. {
  3238. // add teammates
  3239. filter.AddRecipient( player );
  3240. }
  3241. }
  3242. }
  3243. void CCSPlayer::Radio( const char *pszRadioSound, const char *pszRadioText )
  3244. {
  3245. if( !IsAlive() )
  3246. return;
  3247. if ( IsObserver() )
  3248. return;
  3249. CRecipientFilter filter;
  3250. ConstructRadioFilter( filter );
  3251. if( pszRadioText )
  3252. {
  3253. const char *pszLocationText = CSGameRules()->GetChatLocation( true, this );
  3254. if ( pszLocationText && *pszLocationText )
  3255. {
  3256. UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio_location", GetPlayerName(), pszLocationText, pszRadioText );
  3257. }
  3258. else
  3259. {
  3260. UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio", GetPlayerName(), pszRadioText );
  3261. }
  3262. }
  3263. UserMessageBegin ( filter, "SendAudio" );
  3264. WRITE_STRING( pszRadioSound );
  3265. MessageEnd();
  3266. //icon over the head for teammates
  3267. TE_RadioIcon( filter, 0.0, this );
  3268. }
  3269. //-----------------------------------------------------------------------------
  3270. // Purpose: Outputs currently connected players to the console
  3271. //-----------------------------------------------------------------------------
  3272. void CCSPlayer::ListPlayers()
  3273. {
  3274. char buf[64];
  3275. for ( int i=1; i <= gpGlobals->maxClients; i++ )
  3276. {
  3277. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
  3278. if ( pPlayer && !pPlayer->IsDormant() )
  3279. {
  3280. if ( pPlayer->IsBot() )
  3281. {
  3282. Q_snprintf( buf, sizeof(buf), "B %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
  3283. }
  3284. else
  3285. {
  3286. Q_snprintf( buf, sizeof(buf), " %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
  3287. }
  3288. ClientPrint( this, HUD_PRINTCONSOLE, buf );
  3289. }
  3290. }
  3291. ClientPrint( this, HUD_PRINTCONSOLE, "\n" );
  3292. }
  3293. //-----------------------------------------------------------------------------
  3294. // Purpose:
  3295. // Input : &info -
  3296. //-----------------------------------------------------------------------------
  3297. void CCSPlayer::OnDamagedByExplosion( const CTakeDamageInfo &info )
  3298. {
  3299. float lastDamage = info.GetDamage();
  3300. //Adrian - This is hacky since we might have been damaged by something else
  3301. //but since the round is ending, who cares.
  3302. if ( CSGameRules()->m_bTargetBombed == true )
  3303. return;
  3304. float distanceFromPlayer = 9999.0f;
  3305. CBaseEntity *inflictor = info.GetInflictor();
  3306. if ( inflictor )
  3307. {
  3308. Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin();
  3309. distanceFromPlayer = delta.Length();
  3310. }
  3311. bool shock = lastDamage >= 30.0f;
  3312. if ( !shock )
  3313. return;
  3314. m_applyDeafnessTime = gpGlobals->curtime + 0.3;
  3315. m_currentDeafnessFilter = 0;
  3316. }
  3317. void CCSPlayer::ApplyDeafnessEffect()
  3318. {
  3319. // what's happening here is that the low-pass filter and the oscillator frequency effects need
  3320. // to fade in and out slowly. So we have several filters that we switch between to achieve this
  3321. // effect. The first 3rd of the total effect will be the "fade in" of the effect. Which means going
  3322. // from filter to filter from the first to the last. Then we keep on the "last" filter for another
  3323. // third of the total effect time. Then the last third of the time we go back from the last filter
  3324. // to the first. Clear as mud?
  3325. // glossary:
  3326. // filter: an individual filter as defined in dsp_presets.txt
  3327. // section: one of the sections for the total effect, fade in, full, fade out are the possible sections
  3328. // effect: the total effect of combining all the sections, the whole of what the player hears from start to finish.
  3329. const int firstGrenadeFilterIndex = 137;
  3330. const int lastGrenadeFilterIndex = 139;
  3331. const float grenadeEffectLengthInSecs = 4.5f; // time of the total effect
  3332. const float fadeInSectionTime = 0.1f;
  3333. const float fadeOutSectionTime = 1.5f;
  3334. const float timeForEachFilterInFadeIn = fadeInSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex);
  3335. const float timeForEachFilterInFadeOut = fadeOutSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex);
  3336. float timeIntoEffect = gpGlobals->curtime - m_applyDeafnessTime;
  3337. if (timeIntoEffect >= grenadeEffectLengthInSecs)
  3338. {
  3339. // the effect is done, so reset the deafness variables.
  3340. m_applyDeafnessTime = 0.0f;
  3341. m_currentDeafnessFilter = 0;
  3342. return;
  3343. }
  3344. int section = 0;
  3345. if (timeIntoEffect < fadeInSectionTime)
  3346. {
  3347. section = 0;
  3348. }
  3349. else if (timeIntoEffect < (grenadeEffectLengthInSecs - fadeOutSectionTime))
  3350. {
  3351. section = 1;
  3352. }
  3353. else
  3354. {
  3355. section = 2;
  3356. }
  3357. int filterToUse = 0;
  3358. if (section == 0)
  3359. {
  3360. // fade into the effect.
  3361. int filterIndex = (int)(timeIntoEffect / timeForEachFilterInFadeIn);
  3362. filterToUse = filterIndex += firstGrenadeFilterIndex;
  3363. }
  3364. else if (section == 1)
  3365. {
  3366. // in full effect.
  3367. filterToUse = lastGrenadeFilterIndex;
  3368. }
  3369. else if (section == 2)
  3370. {
  3371. // fade out of the effect
  3372. float timeIntoSection = timeIntoEffect - (grenadeEffectLengthInSecs - fadeOutSectionTime);
  3373. int filterIndex = (int)(timeIntoSection / timeForEachFilterInFadeOut);
  3374. filterToUse = lastGrenadeFilterIndex - filterIndex - 1;
  3375. }
  3376. if (filterToUse != m_currentDeafnessFilter)
  3377. {
  3378. m_currentDeafnessFilter = filterToUse;
  3379. CSingleUserRecipientFilter user( this );
  3380. enginesound->SetPlayerDSP( user, m_currentDeafnessFilter, false );
  3381. }
  3382. }
  3383. void CCSPlayer::NoteWeaponFired()
  3384. {
  3385. Assert( m_pCurrentCommand );
  3386. if( m_pCurrentCommand )
  3387. {
  3388. m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
  3389. }
  3390. }
  3391. bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
  3392. {
  3393. // No need to lag compensate at all if we're not attacking in this command and
  3394. // we haven't attacked recently.
  3395. if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
  3396. {
  3397. if ( ( pCmd->buttons & IN_ATTACK2 ) == 0 )
  3398. return false;
  3399. CWeaponCSBase *weapon = GetActiveCSWeapon();
  3400. if ( !weapon )
  3401. return false;
  3402. if ( weapon->GetWeaponID() != WEAPON_KNIFE )
  3403. return false; // IN_ATTACK2 with WEAPON_KNIFE should do lag compensation
  3404. }
  3405. return BaseClass::WantsLagCompensationOnEntity( pPlayer, pCmd, pEntityTransmitBits );
  3406. }
  3407. // Handles the special "radio" alias commands we're creating to accommodate the scripts players use
  3408. // ** Returns true if we've handled the command **
  3409. bool HandleRadioAliasCommands( CCSPlayer *pPlayer, const char *pszCommand )
  3410. {
  3411. bool bRetVal = false;
  3412. // don't execute them if we are not alive or are an observer
  3413. if( !pPlayer->IsAlive() || pPlayer->IsObserver() )
  3414. return false;
  3415. // Radio1 commands
  3416. if ( FStrEq( pszCommand, "coverme" ) )
  3417. {
  3418. bRetVal = true;
  3419. pPlayer->HandleMenu_Radio1( 1 );
  3420. }
  3421. else if ( FStrEq( pszCommand, "takepoint" ) )
  3422. {
  3423. bRetVal = true;
  3424. pPlayer->HandleMenu_Radio1( 2 );
  3425. }
  3426. else if ( FStrEq( pszCommand, "holdpos" ) )
  3427. {
  3428. bRetVal = true;
  3429. pPlayer->HandleMenu_Radio1( 3 );
  3430. }
  3431. else if ( FStrEq( pszCommand, "regroup" ) )
  3432. {
  3433. bRetVal = true;
  3434. pPlayer->HandleMenu_Radio1( 4 );
  3435. }
  3436. else if ( FStrEq( pszCommand, "followme" ) )
  3437. {
  3438. bRetVal = true;
  3439. pPlayer->HandleMenu_Radio1( 5 );
  3440. }
  3441. else if ( FStrEq( pszCommand, "takingfire" ) )
  3442. {
  3443. bRetVal = true;
  3444. pPlayer->HandleMenu_Radio1( 6 );
  3445. }
  3446. // Radio2 commands
  3447. else if ( FStrEq( pszCommand, "go" ) )
  3448. {
  3449. bRetVal = true;
  3450. pPlayer->HandleMenu_Radio2( 1 );
  3451. }
  3452. else if ( FStrEq( pszCommand, "fallback" ) )
  3453. {
  3454. bRetVal = true;
  3455. pPlayer->HandleMenu_Radio2( 2 );
  3456. }
  3457. else if ( FStrEq( pszCommand, "sticktog" ) )
  3458. {
  3459. bRetVal = true;
  3460. pPlayer->HandleMenu_Radio2( 3 );
  3461. }
  3462. else if ( FStrEq( pszCommand, "getinpos" ) )
  3463. {
  3464. bRetVal = true;
  3465. pPlayer->HandleMenu_Radio2( 4 );
  3466. }
  3467. else if ( FStrEq( pszCommand, "stormfront" ) )
  3468. {
  3469. bRetVal = true;
  3470. pPlayer->HandleMenu_Radio2( 5 );
  3471. }
  3472. else if ( FStrEq( pszCommand, "report" ) )
  3473. {
  3474. bRetVal = true;
  3475. pPlayer->HandleMenu_Radio2( 6 );
  3476. }
  3477. // Radio3 commands
  3478. else if ( FStrEq( pszCommand, "roger" ) )
  3479. {
  3480. bRetVal = true;
  3481. pPlayer->HandleMenu_Radio3( 1 );
  3482. }
  3483. else if ( FStrEq( pszCommand, "enemyspot" ) )
  3484. {
  3485. bRetVal = true;
  3486. pPlayer->HandleMenu_Radio3( 2 );
  3487. }
  3488. else if ( FStrEq( pszCommand, "needbackup" ) )
  3489. {
  3490. bRetVal = true;
  3491. pPlayer->HandleMenu_Radio3( 3 );
  3492. }
  3493. else if ( FStrEq( pszCommand, "sectorclear" ) )
  3494. {
  3495. bRetVal = true;
  3496. pPlayer->HandleMenu_Radio3( 4 );
  3497. }
  3498. else if ( FStrEq( pszCommand, "inposition" ) )
  3499. {
  3500. bRetVal = true;
  3501. pPlayer->HandleMenu_Radio3( 5 );
  3502. }
  3503. else if ( FStrEq( pszCommand, "reportingin" ) )
  3504. {
  3505. bRetVal = true;
  3506. pPlayer->HandleMenu_Radio3( 6 );
  3507. }
  3508. else if ( FStrEq( pszCommand, "getout" ) )
  3509. {
  3510. bRetVal = true;
  3511. pPlayer->HandleMenu_Radio3( 7 );
  3512. }
  3513. else if ( FStrEq( pszCommand, "negative" ) )
  3514. {
  3515. bRetVal = true;
  3516. pPlayer->HandleMenu_Radio3( 8 );
  3517. }
  3518. else if ( FStrEq( pszCommand, "enemydown" ) )
  3519. {
  3520. bRetVal = true;
  3521. pPlayer->HandleMenu_Radio3( 9 );
  3522. }
  3523. return bRetVal;
  3524. }
  3525. bool CCSPlayer::ShouldRunRateLimitedCommand( const CCommand &args )
  3526. {
  3527. const char *pcmd = args[0];
  3528. int i = m_RateLimitLastCommandTimes.Find( pcmd );
  3529. if ( i == m_RateLimitLastCommandTimes.InvalidIndex() )
  3530. {
  3531. m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime );
  3532. return true;
  3533. }
  3534. else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < CS_COMMAND_MAX_RATE )
  3535. {
  3536. // Too fast.
  3537. return false;
  3538. }
  3539. else
  3540. {
  3541. m_RateLimitLastCommandTimes[i] = gpGlobals->curtime;
  3542. return true;
  3543. }
  3544. }
  3545. bool CCSPlayer::ClientCommand( const CCommand &args )
  3546. {
  3547. const char *pcmd = args[0];
  3548. // Bots mimic our client commands.
  3549. /*
  3550. if ( bot_mimic.GetInt() && !( GetFlags() & FL_FAKECLIENT ) )
  3551. {
  3552. for ( int i=1; i <= gpGlobals->maxClients; i++ )
  3553. {
  3554. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
  3555. if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
  3556. {
  3557. pPlayer->ClientCommand( pcmd );
  3558. }
  3559. }
  3560. }
  3561. */
  3562. #if defined ( DEBUG )
  3563. if ( FStrEq( pcmd, "bot_cmd" ) )
  3564. {
  3565. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( atoi( args[1] ) ) );
  3566. if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
  3567. {
  3568. CCommand botArgs( args.ArgC() - 2, &args.ArgV()[2] );
  3569. pPlayer->ClientCommand( botArgs );
  3570. pPlayer->RemoveEffects( EF_NODRAW );
  3571. }
  3572. return true;
  3573. }
  3574. if ( FStrEq( pcmd, "blind" ) )
  3575. {
  3576. if ( ShouldRunRateLimitedCommand( args ) )
  3577. {
  3578. if ( args.ArgC() == 3 )
  3579. {
  3580. Blind( atof( args[1] ), atof( args[2] ) );
  3581. }
  3582. else
  3583. {
  3584. ClientPrint( this, HUD_PRINTCONSOLE, "usage: blind holdtime fadetime\n" );
  3585. }
  3586. }
  3587. return true;
  3588. }
  3589. if ( FStrEq( pcmd, "deafen" ) )
  3590. {
  3591. Deafen( 0.0f );
  3592. return true;
  3593. }
  3594. if ( FStrEq( pcmd, "he_deafen" ) )
  3595. {
  3596. m_applyDeafnessTime = gpGlobals->curtime + 0.3;
  3597. m_currentDeafnessFilter = 0;
  3598. return true;
  3599. }
  3600. if ( FStrEq( pcmd, "hint_reset" ) )
  3601. {
  3602. m_iDisplayHistoryBits = 0;
  3603. return true;
  3604. }
  3605. if ( FStrEq( pcmd, "punch" ) )
  3606. {
  3607. float flDamage = 100;
  3608. QAngle punchAngle = GetPunchAngle();
  3609. punchAngle.x = flDamage * random->RandomFloat ( -0.15, 0.15 );
  3610. punchAngle.y = flDamage * random->RandomFloat ( -0.15, 0.15 );
  3611. punchAngle.z = flDamage * random->RandomFloat ( -0.15, 0.15 );
  3612. clamp( punchAngle.x, -4, punchAngle.x );
  3613. clamp( punchAngle.y, -5, 5 );
  3614. clamp( punchAngle.z, -5, 5 );
  3615. // +y == down
  3616. // +x == left
  3617. // +z == roll clockwise
  3618. if ( args.ArgC() == 4 )
  3619. {
  3620. punchAngle.x = atof(args[1]);
  3621. punchAngle.y = atof(args[2]);
  3622. punchAngle.z = atof(args[3]);
  3623. }
  3624. SetPunchAngle( punchAngle );
  3625. return true;
  3626. }
  3627. #endif //DEBUG
  3628. if ( FStrEq( pcmd, "jointeam" ) )
  3629. {
  3630. if ( args.ArgC() < 2 )
  3631. {
  3632. Warning( "Player sent bad jointeam syntax\n" );
  3633. }
  3634. if ( ShouldRunRateLimitedCommand( args ) )
  3635. {
  3636. int iTeam = atoi( args[1] );
  3637. HandleCommand_JoinTeam( iTeam );
  3638. }
  3639. return true;
  3640. }
  3641. else if ( FStrEq( pcmd, "spectate" ) )
  3642. {
  3643. if ( ShouldRunRateLimitedCommand( args ) )
  3644. {
  3645. // instantly join spectators
  3646. HandleCommand_JoinTeam( TEAM_SPECTATOR );
  3647. }
  3648. return true;
  3649. }
  3650. else if ( FStrEq( pcmd, "joingame" ) )
  3651. {
  3652. // player just closed MOTD dialog
  3653. if ( m_iPlayerState == STATE_WELCOME )
  3654. {
  3655. State_Transition( STATE_PICKINGTEAM );
  3656. }
  3657. return true;
  3658. }
  3659. else if ( FStrEq( pcmd, "joinclass" ) )
  3660. {
  3661. if ( args.ArgC() < 2 )
  3662. {
  3663. Warning( "Player sent bad joinclass syntax\n" );
  3664. }
  3665. if ( ShouldRunRateLimitedCommand( args ) )
  3666. {
  3667. int iClass = atoi( args[1] );
  3668. HandleCommand_JoinClass( iClass );
  3669. }
  3670. return true;
  3671. }
  3672. else if ( FStrEq( pcmd, "drop" ) )
  3673. {
  3674. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );
  3675. if( pWeapon )
  3676. {
  3677. //=============================================================================
  3678. // HPE_BEGIN:
  3679. // [dwenger] Determine value of dropped item.
  3680. //=============================================================================
  3681. if ( !pWeapon->IsAPriorOwner( this ) )
  3682. {
  3683. pWeapon->AddToPriorOwnerList( this );
  3684. CCS_GameStats.IncrementStat(this, CSTAT_ITEMS_DROPPED_VALUE, pWeapon->GetCSWpnData().GetWeaponPrice());
  3685. }
  3686. //=============================================================================
  3687. // HPE_END
  3688. //=============================================================================
  3689. CSWeaponType type = pWeapon->GetCSWpnData().m_WeaponType;
  3690. if( type != WEAPONTYPE_KNIFE && type != WEAPONTYPE_GRENADE )
  3691. {
  3692. if (CSGameRules()->GetCanDonateWeapon() && !pWeapon->GetDonated())
  3693. {
  3694. pWeapon->SetDonated(true);
  3695. pWeapon->SetDonor(this);
  3696. }
  3697. CSWeaponDrop( pWeapon, true, true );
  3698. }
  3699. }
  3700. return true;
  3701. }
  3702. else if ( FStrEq( pcmd, "buy" ) )
  3703. {
  3704. BuyResult_e result = BUY_INVALID_ITEM;
  3705. if ( args.ArgC() == 2 )
  3706. {
  3707. result = HandleCommand_Buy( args[1] );
  3708. }
  3709. if ( result == BUY_INVALID_ITEM )
  3710. {
  3711. // Print out a message on the console
  3712. int msg_dest = HUD_PRINTCONSOLE;
  3713. ClientPrint( this, msg_dest, "usage: buy <item>\n" );
  3714. ClientPrint( this, msg_dest, " primammo\n" );
  3715. ClientPrint( this, msg_dest, " secammo\n" );
  3716. ClientPrint( this, msg_dest, " vest\n" );
  3717. ClientPrint( this, msg_dest, " vesthelm\n" );
  3718. ClientPrint( this, msg_dest, " defuser\n" );
  3719. //ClientPrint( this, msg_dest, " shield\n" );
  3720. ClientPrint( this, msg_dest, " nvgs\n" );
  3721. ClientPrint( this, msg_dest, " flashbang\n" );
  3722. ClientPrint( this, msg_dest, " hegrenade\n" );
  3723. ClientPrint( this, msg_dest, " smokegrenade\n" );
  3724. ClientPrint( this, msg_dest, " galil\n" );
  3725. ClientPrint( this, msg_dest, " ak47\n" );
  3726. ClientPrint( this, msg_dest, " scout\n" );
  3727. ClientPrint( this, msg_dest, " sg552\n" );
  3728. ClientPrint( this, msg_dest, " awp\n" );
  3729. ClientPrint( this, msg_dest, " g3sg1\n" );
  3730. ClientPrint( this, msg_dest, " famas\n" );
  3731. ClientPrint( this, msg_dest, " m4a1\n" );
  3732. ClientPrint( this, msg_dest, " aug\n" );
  3733. ClientPrint( this, msg_dest, " sg550\n" );
  3734. ClientPrint( this, msg_dest, " glock\n" );
  3735. ClientPrint( this, msg_dest, " usp\n" );
  3736. ClientPrint( this, msg_dest, " p228\n" );
  3737. ClientPrint( this, msg_dest, " deagle\n" );
  3738. ClientPrint( this, msg_dest, " elite\n" );
  3739. ClientPrint( this, msg_dest, " fiveseven\n" );
  3740. ClientPrint( this, msg_dest, " m3\n" );
  3741. ClientPrint( this, msg_dest, " xm1014\n" );
  3742. ClientPrint( this, msg_dest, " mac10\n" );
  3743. ClientPrint( this, msg_dest, " tmp\n" );
  3744. ClientPrint( this, msg_dest, " mp5navy\n" );
  3745. ClientPrint( this, msg_dest, " ump45\n" );
  3746. ClientPrint( this, msg_dest, " p90\n" );
  3747. ClientPrint( this, msg_dest, " m249\n" );
  3748. }
  3749. return true;
  3750. }
  3751. else if ( FStrEq( pcmd, "buyammo1" ) )
  3752. {
  3753. AttemptToBuyAmmoSingle(0);
  3754. return true;
  3755. }
  3756. else if ( FStrEq( pcmd, "buyammo2" ) )
  3757. {
  3758. AttemptToBuyAmmoSingle(1);
  3759. return true;
  3760. }
  3761. else if ( FStrEq( pcmd, "nightvision" ) )
  3762. {
  3763. if ( ShouldRunRateLimitedCommand( args ) )
  3764. {
  3765. if( m_bHasNightVision )
  3766. {
  3767. if( m_bNightVisionOn )
  3768. {
  3769. CPASAttenuationFilter filter( this );
  3770. EmitSound( filter, entindex(), "Player.NightVisionOff" );
  3771. }
  3772. else
  3773. {
  3774. CPASAttenuationFilter filter( this );
  3775. EmitSound( filter, entindex(), "Player.NightVisionOn" );
  3776. }
  3777. m_bNightVisionOn = !m_bNightVisionOn;
  3778. }
  3779. }
  3780. return true;
  3781. }
  3782. else if ( FStrEq( pcmd, "menuselect" ) )
  3783. {
  3784. return true;
  3785. }
  3786. else if ( HandleRadioAliasCommands( this, pcmd ) )
  3787. {
  3788. return true;
  3789. }
  3790. else if ( FStrEq( pcmd, "listplayers" ) )
  3791. {
  3792. ListPlayers();
  3793. return true;
  3794. }
  3795. else if ( FStrEq( pcmd, "ignorerad" ) )
  3796. {
  3797. m_bIgnoreRadio = !m_bIgnoreRadio;
  3798. if ( m_bIgnoreRadio )
  3799. {
  3800. ClientPrint( this, HUD_PRINTTALK, "#Ignore_Radio" );
  3801. }
  3802. else
  3803. {
  3804. ClientPrint( this, HUD_PRINTTALK, "#Accept_Radio" );
  3805. }
  3806. return true;
  3807. }
  3808. else if ( FStrEq( pcmd, "become_vip" ) )
  3809. {
  3810. //MIKETODO: VIP mode
  3811. /*
  3812. if ( ( CSGameRules()->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
  3813. {
  3814. mp->AddToVIPQueue( this );
  3815. }
  3816. */
  3817. return true;
  3818. }
  3819. return BaseClass::ClientCommand( args );
  3820. }
  3821. // returns true if the selection has been handled and the player's menu
  3822. // can be closed...false if the menu should be displayed again
  3823. bool CCSPlayer::HandleCommand_JoinTeam( int team )
  3824. {
  3825. CCSGameRules *mp = CSGameRules();
  3826. if ( !GetGlobalTeam( team ) )
  3827. {
  3828. DevWarning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team );
  3829. return false;
  3830. }
  3831. // If this player is a VIP, don't allow him to switch teams/appearances unless the following conditions are met :
  3832. // a) There is another TEAM_CT player who is in the queue to be a VIP
  3833. // b) This player is dead
  3834. //MIKETODO: handle this when doing VIP mode
  3835. /*
  3836. if ( m_bIsVIP == true )
  3837. {
  3838. if ( !IsDead() )
  3839. {
  3840. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
  3841. MenuReset();
  3842. return true;
  3843. }
  3844. else if ( mp->IsVIPQueueEmpty() == true )
  3845. {
  3846. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
  3847. MenuReset();
  3848. return true;
  3849. }
  3850. }
  3851. //MIKETODO: VIP mode
  3852. case 3:
  3853. if ( ( mp->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
  3854. {
  3855. mp->AddToVIPQueue( player );
  3856. MenuReset();
  3857. return true;
  3858. }
  3859. else
  3860. {
  3861. return false;
  3862. }
  3863. break;
  3864. */
  3865. // If we already died and changed teams once, deny
  3866. if( m_bTeamChanged && team != m_iOldTeam && team != TEAM_SPECTATOR )
  3867. {
  3868. ClientPrint( this, HUD_PRINTCENTER, "#Only_1_Team_Change" );
  3869. return true;
  3870. }
  3871. // check if we're limited in our team selection
  3872. if ( team == TEAM_UNASSIGNED && !IsBot() )
  3873. {
  3874. team = mp->GetHumanTeam(); // returns TEAM_UNASSIGNED if we're unrestricted
  3875. }
  3876. if ( team == TEAM_UNASSIGNED )
  3877. {
  3878. // Attempt to auto-select a team, may set team to T, CT or SPEC
  3879. team = mp->SelectDefaultTeam( !IsBot() );
  3880. if ( team == TEAM_UNASSIGNED )
  3881. {
  3882. // still team unassigned, try to kick a bot if possible
  3883. // kick a bot to allow human to join
  3884. if (cv_bot_auto_vacate.GetBool() && !IsBot())
  3885. {
  3886. team = (random->RandomInt( 0, 1 ) == 0) ? TEAM_TERRORIST : TEAM_CT;
  3887. if (UTIL_KickBotFromTeam( team ) == false)
  3888. {
  3889. // no bots on that team, try the other
  3890. team = (team == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
  3891. if (UTIL_KickBotFromTeam( team ) == false)
  3892. {
  3893. // couldn't kick any bots, fail
  3894. team = TEAM_UNASSIGNED;
  3895. }
  3896. }
  3897. }
  3898. if (team == TEAM_UNASSIGNED)
  3899. {
  3900. ClientPrint( this, HUD_PRINTCENTER, "#All_Teams_Full" );
  3901. ShowViewPortPanel( PANEL_TEAM );
  3902. return false;
  3903. }
  3904. }
  3905. }
  3906. if ( team == GetTeamNumber() )
  3907. {
  3908. // Let people change class (skin) by re-joining the same team
  3909. if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 )
  3910. {
  3911. ShowViewPortPanel( PANEL_CLASS_TER );
  3912. }
  3913. else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 )
  3914. {
  3915. ShowViewPortPanel( PANEL_CLASS_CT );
  3916. }
  3917. return true; // we wouldn't change the team
  3918. }
  3919. if ( mp->TeamFull( team ) )
  3920. {
  3921. // attempt to kick a bot to make room for this player
  3922. bool madeRoom = false;
  3923. if (cv_bot_auto_vacate.GetBool() && !IsBot())
  3924. {
  3925. if (UTIL_KickBotFromTeam( team ))
  3926. madeRoom = true;
  3927. }
  3928. if (!madeRoom)
  3929. {
  3930. if ( team == TEAM_TERRORIST )
  3931. {
  3932. ClientPrint( this, HUD_PRINTCENTER, "#Terrorists_Full" );
  3933. }
  3934. else if ( team == TEAM_CT )
  3935. {
  3936. ClientPrint( this, HUD_PRINTCENTER, "#CTs_Full" );
  3937. }
  3938. ShowViewPortPanel( PANEL_TEAM );
  3939. return false;
  3940. }
  3941. }
  3942. // check if humans are restricted to a single team (Tour of Duty, etc)
  3943. if ( !IsBot() && team != TEAM_SPECTATOR)
  3944. {
  3945. int humanTeam = mp->GetHumanTeam();
  3946. if ( humanTeam != TEAM_UNASSIGNED && humanTeam != team )
  3947. {
  3948. if ( humanTeam == TEAM_TERRORIST )
  3949. {
  3950. ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_T" );
  3951. }
  3952. else if ( humanTeam == TEAM_CT )
  3953. {
  3954. ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_CT" );
  3955. }
  3956. ShowViewPortPanel( PANEL_TEAM );
  3957. return false;
  3958. }
  3959. }
  3960. if ( team == TEAM_SPECTATOR )
  3961. {
  3962. // Prevent this is the cvar is set
  3963. if ( !mp_allowspectators.GetInt() && !IsHLTV() )
  3964. {
  3965. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
  3966. return false;
  3967. }
  3968. if ( GetTeamNumber() != TEAM_UNASSIGNED && State_Get() == STATE_ACTIVE )
  3969. {
  3970. m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work
  3971. CommitSuicide();
  3972. // add 1 to frags to balance out the 1 subtracted for killing yourself
  3973. IncrementFragCount( 1 );
  3974. }
  3975. ChangeTeam( TEAM_SPECTATOR );
  3976. m_iClass = (int)CS_CLASS_NONE;
  3977. if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
  3978. {
  3979. m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
  3980. HintMessage( "#Spec_Duck", true, true );
  3981. }
  3982. // do we have fadetoblack on? (need to fade their screen back in)
  3983. if ( mp_fadetoblack.GetBool() )
  3984. {
  3985. color32_s clr = { 0,0,0,255 };
  3986. UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
  3987. }
  3988. return true;
  3989. }
  3990. // If the code gets this far, the team is not TEAM_UNASSIGNED
  3991. if (mp->TeamStacked( team, GetTeamNumber() ))//players are allowed to change to their own team so they can just change their model
  3992. {
  3993. // attempt to kick a bot to make room for this player
  3994. bool madeRoom = false;
  3995. if (cv_bot_auto_vacate.GetBool() && !IsBot())
  3996. {
  3997. if (UTIL_KickBotFromTeam( team ))
  3998. madeRoom = true;
  3999. }
  4000. if (!madeRoom)
  4001. {
  4002. // The specified team is full
  4003. ClientPrint(
  4004. this,
  4005. HUD_PRINTCENTER,
  4006. ( team == TEAM_TERRORIST ) ? "#Too_Many_Terrorists" : "#Too_Many_CTs" );
  4007. ShowViewPortPanel( PANEL_TEAM );
  4008. return false;
  4009. }
  4010. }
  4011. // Show the appropriate Choose Appearance menu
  4012. // This must come before ClientKill() for CheckWinConditions() to function properly
  4013. // Switch their actual team...
  4014. ChangeTeam( team );
  4015. return true;
  4016. }
  4017. bool CCSPlayer::HandleCommand_JoinClass( int iClass )
  4018. {
  4019. if( iClass == CS_CLASS_NONE )
  4020. {
  4021. // User choosed random class
  4022. switch ( GetTeamNumber() )
  4023. {
  4024. case TEAM_TERRORIST : iClass = RandomInt(FIRST_T_CLASS, LAST_T_CLASS);
  4025. break;
  4026. case TEAM_CT : iClass = RandomInt(FIRST_CT_CLASS, LAST_CT_CLASS);
  4027. break;
  4028. default : iClass = CS_CLASS_NONE;
  4029. break;
  4030. }
  4031. }
  4032. // clamp to valid classes
  4033. switch ( GetTeamNumber() )
  4034. {
  4035. case TEAM_TERRORIST:
  4036. iClass = clamp( iClass, FIRST_T_CLASS, LAST_T_CLASS );
  4037. break;
  4038. case TEAM_CT:
  4039. iClass = clamp( iClass, FIRST_CT_CLASS, LAST_CT_CLASS );
  4040. break;
  4041. default:
  4042. iClass = CS_CLASS_NONE;
  4043. }
  4044. // Reset the player's state
  4045. if ( State_Get() == STATE_ACTIVE )
  4046. {
  4047. CSGameRules()->CheckWinConditions();
  4048. }
  4049. if ( !IsBot() && State_Get() == STATE_ACTIVE ) // Bots are responsible about only switching classes when they join.
  4050. {
  4051. // Kill player if switching classes while alive.
  4052. // This mimics goldsrc CS 1.6, and prevents a player from hiding, and switching classes to
  4053. // make the opposing team think there are more enemies than there really are.
  4054. CommitSuicide();
  4055. }
  4056. m_iClass = iClass;
  4057. if (State_Get() == STATE_PICKINGCLASS)
  4058. {
  4059. // SetModelFromClass();
  4060. GetIntoGame();
  4061. }
  4062. return true;
  4063. }
  4064. /*
  4065. void CheckStartMoney( void )
  4066. {
  4067. if ( mp_startmoney.GetInt() > 16000 )
  4068. {
  4069. mp_startmoney.SetInt( 16000 );
  4070. }
  4071. else if ( mp_startmoney.GetInt() < 800 )
  4072. {
  4073. mp_startmoney.SetInt( 800 );
  4074. }
  4075. }
  4076. */
  4077. void CCSPlayer::GetIntoGame()
  4078. {
  4079. // Set their model and if they're allowed to spawn right now, put them into the world.
  4080. //SetPlayerModel( iClass );
  4081. SetFOV( this, 0 );
  4082. m_flLastMovement = gpGlobals->curtime;
  4083. CCSGameRules *MPRules = CSGameRules();
  4084. /* //MIKETODO: Escape gameplay ?
  4085. if ( ( MPRules->m_bMapHasEscapeZone == true ) && ( m_iTeam == TEAM_CT ) )
  4086. {
  4087. m_iAccount = 0;
  4088. CheckStartMoney();
  4089. AddAccount( (int)startmoney.value, true );
  4090. }
  4091. */
  4092. //****************New Code by SupraFiend************
  4093. if ( !MPRules->FPlayerCanRespawn( this ) )
  4094. {
  4095. // This player is joining in the middle of a round or is an observer. Put them directly into observer mode.
  4096. //pev->deadflag = DEAD_RESPAWNABLE;
  4097. //pev->classname = MAKE_STRING("player");
  4098. //pev->flags &= ( FL_PROXY | FL_FAKECLIENT ); // clear flags, but keep proxy and bot flags that might already be set
  4099. //pev->flags |= FL_CLIENT | FL_SPECTATOR;
  4100. //SetThink(PlayerDeathThink);
  4101. if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
  4102. {
  4103. m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
  4104. HintMessage( "#Spec_Duck", true, true );
  4105. }
  4106. State_Transition( STATE_OBSERVER_MODE );
  4107. m_wasNotKilledNaturally = true;
  4108. MPRules->CheckWinConditions();
  4109. }
  4110. else// else spawn them right in
  4111. {
  4112. State_Transition( STATE_ACTIVE );
  4113. Spawn();
  4114. MPRules->CheckWinConditions();
  4115. //=============================================================================
  4116. // HPE_BEGIN:
  4117. // [menglish] Have the rules update anything related to a player spawning in late
  4118. //=============================================================================
  4119. MPRules->SpawningLatePlayer(this);
  4120. //=============================================================================
  4121. // HPE_END
  4122. //=============================================================================
  4123. if( MPRules->m_flRestartRoundTime == 0.0f )
  4124. {
  4125. //Bomb target, no bomber and no bomb lying around.
  4126. if( MPRules->IsBombDefuseMap() && !MPRules->IsThereABomber() && !MPRules->IsThereABomb() )
  4127. MPRules->GiveC4(); //Checks for terrorists.
  4128. }
  4129. // If a new terrorist is entering the fray, then up the # of potential escapers.
  4130. if ( GetTeamNumber() == TEAM_TERRORIST )
  4131. MPRules->m_iNumEscapers++;
  4132. //=============================================================================
  4133. // HPE_BEGIN:
  4134. // [menglish] Reset Round Based Achievement Variables
  4135. //=============================================================================
  4136. ResetRoundBasedAchievementVariables();
  4137. //=============================================================================
  4138. // HPE_END
  4139. //=============================================================================
  4140. }
  4141. }
  4142. int CCSPlayer::PlayerClass() const
  4143. {
  4144. return m_iClass;
  4145. }
  4146. bool CCSPlayer::SelectSpawnSpot( const char *pEntClassName, CBaseEntity* &pSpot )
  4147. {
  4148. // Find the next spawn spot.
  4149. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4150. if ( pSpot == NULL ) // skip over the null point
  4151. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4152. CBaseEntity *pFirstSpot = pSpot;
  4153. do
  4154. {
  4155. if ( pSpot )
  4156. {
  4157. // check if pSpot is valid
  4158. if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
  4159. {
  4160. if ( pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
  4161. {
  4162. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4163. continue;
  4164. }
  4165. // if so, go to pSpot
  4166. return true;
  4167. }
  4168. }
  4169. // increment pSpot
  4170. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4171. } while ( pSpot != pFirstSpot ); // loop if we're not back to the start
  4172. DevMsg("CCSPlayer::SelectSpawnSpot: couldn't find valid spawn point.\n");
  4173. return true;
  4174. }
  4175. CBaseEntity* CCSPlayer::EntSelectSpawnPoint()
  4176. {
  4177. CBaseEntity *pSpot;
  4178. /* MIKETODO: VIP
  4179. // VIP spawn point *************
  4180. if ( ( g_pGameRules->IsDeathmatch() ) && ( ((CBasePlayer*)pPlayer)->m_bIsVIP == TRUE) )
  4181. {
  4182. //ALERT (at_console,"Looking for a VIP spawn point\n");
  4183. // Randomize the start spot
  4184. //for ( int i = RANDOM_LONG(1,5); i > 0; i-- )
  4185. pSpot = UTIL_FindEntityByClassname( NULL, "info_vip_start" );
  4186. if ( !FNullEnt( pSpot ) ) // skip over the null point
  4187. goto ReturnSpot;
  4188. else
  4189. goto CTSpawn;
  4190. }
  4191. //
  4192. // the counter-terrorist spawns at "info_player_start"
  4193. else
  4194. */
  4195. pSpot = NULL;
  4196. if ( CSGameRules()->IsLogoMap() )
  4197. {
  4198. // This is a logo map. Don't allow movement or logos or menus.
  4199. SelectSpawnSpot( "info_player_logo", pSpot );
  4200. LockPlayerInPlace();
  4201. goto ReturnSpot;
  4202. }
  4203. else
  4204. {
  4205. if ( GetTeamNumber() == TEAM_CT )
  4206. {
  4207. pSpot = g_pLastCTSpawn;
  4208. if ( SelectSpawnSpot( "info_player_counterterrorist", pSpot ))
  4209. {
  4210. g_pLastCTSpawn = pSpot;
  4211. goto ReturnSpot;
  4212. }
  4213. }
  4214. /*********************************************************/
  4215. // The terrorist spawn points
  4216. else if ( GetTeamNumber() == TEAM_TERRORIST )
  4217. {
  4218. pSpot = g_pLastTerroristSpawn;
  4219. if ( SelectSpawnSpot( "info_player_terrorist", pSpot ) )
  4220. {
  4221. g_pLastTerroristSpawn = pSpot;
  4222. goto ReturnSpot;
  4223. }
  4224. }
  4225. }
  4226. // If startspot is set, (re)spawn there.
  4227. if ( !gpGlobals->startspot || !strlen(STRING(gpGlobals->startspot)))
  4228. {
  4229. pSpot = gEntList.FindEntityByClassname(NULL, "info_player_terrorist");
  4230. if ( pSpot )
  4231. goto ReturnSpot;
  4232. }
  4233. else
  4234. {
  4235. pSpot = gEntList.FindEntityByTarget( NULL, STRING(gpGlobals->startspot) );
  4236. if ( pSpot )
  4237. goto ReturnSpot;
  4238. }
  4239. ReturnSpot:
  4240. if ( !pSpot )
  4241. {
  4242. if( CSGameRules()->IsLogoMap() )
  4243. Warning( "PutClientInServer: no info_player_logo on level\n" );
  4244. else
  4245. Warning( "PutClientInServer: no info_player_start on level\n" );
  4246. return CBaseEntity::Instance( INDEXENT(0) );
  4247. }
  4248. return pSpot;
  4249. }
  4250. void CCSPlayer::SetProgressBarTime( int barTime )
  4251. {
  4252. m_iProgressBarDuration = barTime;
  4253. m_flProgressBarStartTime = this->m_flSimulationTime;
  4254. }
  4255. void CCSPlayer::PlayerDeathThink()
  4256. {
  4257. }
  4258. void CCSPlayer::State_Transition( CSPlayerState newState )
  4259. {
  4260. State_Leave();
  4261. State_Enter( newState );
  4262. }
  4263. void CCSPlayer::State_Enter( CSPlayerState newState )
  4264. {
  4265. m_iPlayerState = newState;
  4266. m_pCurStateInfo = State_LookupInfo( newState );
  4267. if ( cs_ShowStateTransitions.GetInt() == -1 || cs_ShowStateTransitions.GetInt() == entindex() )
  4268. {
  4269. if ( m_pCurStateInfo )
  4270. Msg( "ShowStateTransitions: entering '%s'\n", m_pCurStateInfo->m_pStateName );
  4271. else
  4272. Msg( "ShowStateTransitions: entering #%d\n", newState );
  4273. }
  4274. // Initialize the new state.
  4275. if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
  4276. (this->*m_pCurStateInfo->pfnEnterState)();
  4277. }
  4278. void CCSPlayer::State_Leave()
  4279. {
  4280. if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
  4281. {
  4282. (this->*m_pCurStateInfo->pfnLeaveState)();
  4283. }
  4284. }
  4285. void CCSPlayer::State_PreThink()
  4286. {
  4287. if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink )
  4288. {
  4289. (this->*m_pCurStateInfo->pfnPreThink)();
  4290. }
  4291. }
  4292. CCSPlayerStateInfo* CCSPlayer::State_LookupInfo( CSPlayerState state )
  4293. {
  4294. // This table MUST match the
  4295. static CCSPlayerStateInfo playerStateInfos[] =
  4296. {
  4297. { STATE_ACTIVE, "STATE_ACTIVE", &CCSPlayer::State_Enter_ACTIVE, NULL, &CCSPlayer::State_PreThink_ACTIVE },
  4298. { STATE_WELCOME, "STATE_WELCOME", &CCSPlayer::State_Enter_WELCOME, NULL, &CCSPlayer::State_PreThink_WELCOME },
  4299. { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CCSPlayer::State_Enter_PICKINGTEAM, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE },
  4300. { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CCSPlayer::State_Enter_PICKINGCLASS, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE },
  4301. { STATE_DEATH_ANIM, "STATE_DEATH_ANIM", &CCSPlayer::State_Enter_DEATH_ANIM, NULL, &CCSPlayer::State_PreThink_DEATH_ANIM },
  4302. { STATE_DEATH_WAIT_FOR_KEY, "STATE_DEATH_WAIT_FOR_KEY", &CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY, NULL, &CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY },
  4303. { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CCSPlayer::State_Enter_OBSERVER_MODE, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE }
  4304. };
  4305. for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
  4306. {
  4307. if ( playerStateInfos[i].m_iPlayerState == state )
  4308. return &playerStateInfos[i];
  4309. }
  4310. return NULL;
  4311. }
  4312. void CCSPlayer::PhysObjectSleep()
  4313. {
  4314. IPhysicsObject *pObj = VPhysicsGetObject();
  4315. if ( pObj )
  4316. pObj->Sleep();
  4317. }
  4318. void CCSPlayer::PhysObjectWake()
  4319. {
  4320. IPhysicsObject *pObj = VPhysicsGetObject();
  4321. if ( pObj )
  4322. pObj->Wake();
  4323. }
  4324. void CCSPlayer::State_Enter_WELCOME()
  4325. {
  4326. StartObserverMode( OBS_MODE_ROAMING );
  4327. // Important to set MOVETYPE_NONE or our physics object will fall while we're sitting at one of the intro cameras.
  4328. SetMoveType( MOVETYPE_NONE );
  4329. AddSolidFlags( FSOLID_NOT_SOLID );
  4330. PhysObjectSleep();
  4331. const ConVar *hostname = cvar->FindVar( "hostname" );
  4332. const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY";
  4333. // Show info panel (if it's not a simple demo map).
  4334. if ( !CSGameRules()->IsLogoMap() )
  4335. {
  4336. if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the MOTD when making reslists
  4337. {
  4338. engine->ClientCommand( edict(), "jointeam 3\n" );
  4339. }
  4340. else
  4341. {
  4342. KeyValues *data = new KeyValues("data");
  4343. data->SetString( "title", title ); // info panel title
  4344. data->SetString( "type", "1" ); // show userdata from stringtable entry
  4345. data->SetString( "msg", "motd" ); // use this stringtable entry
  4346. data->SetInt( "cmd", TEXTWINDOW_CMD_JOINGAME ); // exec this command if panel closed
  4347. data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );
  4348. ShowViewPortPanel( PANEL_INFO, true, data );
  4349. data->deleteThis();
  4350. }
  4351. }
  4352. }
  4353. void CCSPlayer::State_PreThink_WELCOME()
  4354. {
  4355. // Verify some state.
  4356. Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  4357. Assert( GetAbsVelocity().Length() == 0 );
  4358. // Update whatever intro camera it's at.
  4359. if( m_pIntroCamera && (gpGlobals->curtime >= m_fIntroCamTime) )
  4360. {
  4361. MoveToNextIntroCamera();
  4362. }
  4363. }
  4364. void CCSPlayer::State_Enter_PICKINGTEAM()
  4365. {
  4366. ShowViewPortPanel( "team" ); // show the team menu
  4367. }
  4368. void CCSPlayer::State_Enter_DEATH_ANIM()
  4369. {
  4370. if ( HasWeapons() )
  4371. {
  4372. // we drop the guns here because weapons that have an area effect and can kill their user
  4373. // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
  4374. // player class sometimes is freed. It's safer to manipulate the weapons once we know
  4375. // we aren't calling into any of their code anymore through the player pointer.
  4376. PackDeadPlayerItems();
  4377. }
  4378. // Used for a timer.
  4379. m_flDeathTime = gpGlobals->curtime;
  4380. m_bAbortFreezeCam = false;
  4381. StartObserverMode( OBS_MODE_DEATHCAM ); // go to observer mode
  4382. RemoveEffects( EF_NODRAW ); // still draw player body
  4383. if ( mp_fadetoblack.GetBool() )
  4384. {
  4385. color32_s clr = {0,0,0,255};
  4386. UTIL_ScreenFade( this, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
  4387. //Don't perform any freezecam stuff if we are fading to black
  4388. State_Transition( STATE_DEATH_WAIT_FOR_KEY );
  4389. }
  4390. }
  4391. //=============================================================================
  4392. // HPE_BEGIN:
  4393. // [menglish, pfreese] Added freeze cam logic
  4394. //=============================================================================
  4395. void CCSPlayer::State_PreThink_DEATH_ANIM()
  4396. {
  4397. // If the anim is done playing, go to the next state (waiting for a keypress to
  4398. // either respawn the guy or put him into observer mode).
  4399. if ( GetFlags() & FL_ONGROUND )
  4400. {
  4401. float flForward = GetAbsVelocity().Length() - 20;
  4402. if (flForward <= 0)
  4403. {
  4404. SetAbsVelocity( vec3_origin );
  4405. }
  4406. else
  4407. {
  4408. Vector vAbsVel = GetAbsVelocity();
  4409. VectorNormalize( vAbsVel );
  4410. vAbsVel *= flForward;
  4411. SetAbsVelocity( vAbsVel );
  4412. }
  4413. }
  4414. float fDeathEnd = m_flDeathTime + CS_DEATH_ANIMATION_TIME;
  4415. float fFreezeEnd = fDeathEnd + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();
  4416. // transition to Freezecam mode once the death animation is complete
  4417. if ( gpGlobals->curtime >= fDeathEnd )
  4418. {
  4419. if ( GetObserverTarget() && GetObserverTarget() != this &&
  4420. !m_bAbortFreezeCam && gpGlobals->curtime < fFreezeEnd && GetObserverMode() != OBS_MODE_FREEZECAM)
  4421. {
  4422. StartObserverMode( OBS_MODE_FREEZECAM );
  4423. }
  4424. else if(GetObserverMode() == OBS_MODE_FREEZECAM)
  4425. {
  4426. if ( m_bAbortFreezeCam && !mp_fadetoblack.GetBool() )
  4427. {
  4428. State_Transition( STATE_OBSERVER_MODE );
  4429. }
  4430. }
  4431. }
  4432. // Don't transfer to observer state until the freeze cam is done
  4433. if ( gpGlobals->curtime < fFreezeEnd )
  4434. return;
  4435. State_Transition( STATE_OBSERVER_MODE );
  4436. }
  4437. //=============================================================================
  4438. // HPE_END
  4439. //=============================================================================
  4440. void CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY()
  4441. {
  4442. // Remember when we died, so we can automatically put them into observer mode
  4443. // if they don't hit a key soon enough.
  4444. m_lifeState = LIFE_DEAD;
  4445. StopAnimation();
  4446. // Don't do this. The ragdoll system expects to be able to read from this player on
  4447. // the next update and will read it at the new origin if this is set.
  4448. // Since it is more complicated to redesign the ragdoll system to not need that data
  4449. // it is easier to cause a less obvious bug than popping ragdolls
  4450. //AddEffects( EF_NOINTERP );
  4451. }
  4452. void CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY()
  4453. {
  4454. // once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore
  4455. // this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn
  4456. if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
  4457. SetMoveType( MOVETYPE_NONE );
  4458. // if the player has been dead for one second longer than allowed by forcerespawn,
  4459. // forcerespawn isn't on. Send the player off to an intermission camera until they
  4460. // choose to respawn.
  4461. bool fAnyButtonDown = (m_nButtons & ~IN_SCORE) != 0;
  4462. if ( mp_fadetoblack.GetBool() )
  4463. fAnyButtonDown = false;
  4464. // after a certain amount of time switch to observer mode even if they don't press a key.
  4465. if (gpGlobals->curtime >= (m_flDeathTime + DEATH_ANIMATION_TIME + 3.0))
  4466. {
  4467. fAnyButtonDown = true;
  4468. }
  4469. if ( fAnyButtonDown )
  4470. {
  4471. if ( GetObserverTarget() )
  4472. {
  4473. StartReplayMode( 8, 8, GetObserverTarget()->entindex() );
  4474. }
  4475. State_Transition( STATE_OBSERVER_MODE );
  4476. }
  4477. }
  4478. void CCSPlayer::State_Enter_OBSERVER_MODE()
  4479. {
  4480. // do we have fadetoblack on? (need to fade their screen back in)
  4481. if ( mp_fadetoblack.GetBool() && mp_forcecamera.GetInt() != OBS_ALLOW_NONE)
  4482. {
  4483. color32_s clr = { 0,0,0,255 };
  4484. UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
  4485. }
  4486. int observerMode = m_iObserverLastMode;
  4487. if ( IsNetClient() )
  4488. {
  4489. const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" );
  4490. if ( pIdealMode )
  4491. {
  4492. int nIdealMode = atoi( pIdealMode );
  4493. if ( nIdealMode < OBS_MODE_IN_EYE )
  4494. {
  4495. nIdealMode = OBS_MODE_IN_EYE;
  4496. }
  4497. else if ( nIdealMode > OBS_MODE_ROAMING )
  4498. {
  4499. nIdealMode = OBS_MODE_ROAMING;
  4500. }
  4501. observerMode = nIdealMode;
  4502. }
  4503. }
  4504. StartObserverMode( observerMode );
  4505. PhysObjectSleep();
  4506. }
  4507. void CCSPlayer::State_PreThink_OBSERVER_MODE()
  4508. {
  4509. // Make sure nobody has changed any of our state.
  4510. // Assert( GetMoveType() == MOVETYPE_FLY );
  4511. Assert( m_takedamage == DAMAGE_NO );
  4512. Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  4513. // Assert( IsEffectActive( EF_NODRAW ) );
  4514. // Must be dead.
  4515. Assert( m_lifeState == LIFE_DEAD );
  4516. Assert( pl.deadflag );
  4517. }
  4518. void CCSPlayer::State_Enter_PICKINGCLASS()
  4519. {
  4520. if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the menu when making reslists
  4521. {
  4522. engine->ClientCommand( edict(), "joinclass 0\n" );
  4523. return;
  4524. }
  4525. // go to spec mode, if dying keep deathcam
  4526. if ( GetObserverMode() == OBS_MODE_DEATHCAM )
  4527. {
  4528. StartObserverMode( OBS_MODE_DEATHCAM );
  4529. }
  4530. else
  4531. {
  4532. StartObserverMode( OBS_MODE_FIXED );
  4533. }
  4534. m_iClass = (int)CS_CLASS_NONE;
  4535. PhysObjectSleep();
  4536. // show the class menu:
  4537. if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 )
  4538. {
  4539. ShowViewPortPanel( PANEL_CLASS_TER );
  4540. }
  4541. else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 )
  4542. {
  4543. ShowViewPortPanel( PANEL_CLASS_CT );
  4544. }
  4545. else
  4546. {
  4547. HandleCommand_JoinClass( 0 );
  4548. }
  4549. }
  4550. void CCSPlayer::State_Enter_ACTIVE()
  4551. {
  4552. SetMoveType( MOVETYPE_WALK );
  4553. RemoveSolidFlags( FSOLID_NOT_SOLID );
  4554. m_Local.m_iHideHUD = 0;
  4555. PhysObjectWake();
  4556. }
  4557. void CCSPlayer::State_PreThink_ACTIVE()
  4558. {
  4559. // We only allow noclip here only because noclip is useful for debugging.
  4560. // It would be nice if the noclip command set some flag so we could tell that they
  4561. // did it intentionally.
  4562. if ( IsEFlagSet( EFL_NOCLIP_ACTIVE ) )
  4563. {
  4564. // Assert( GetMoveType() == MOVETYPE_NOCLIP );
  4565. }
  4566. else
  4567. {
  4568. // Assert( GetMoveType() == MOVETYPE_WALK );
  4569. }
  4570. Assert( !IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  4571. }
  4572. void CCSPlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
  4573. {
  4574. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
  4575. if ( pCSWeapon )
  4576. {
  4577. // For rifles, pistols, or the knife, drop our old weapon in this slot.
  4578. if ( pCSWeapon->GetSlot() == WEAPON_SLOT_RIFLE ||
  4579. pCSWeapon->GetSlot() == WEAPON_SLOT_PISTOL ||
  4580. pCSWeapon->GetSlot() == WEAPON_SLOT_KNIFE )
  4581. {
  4582. CBaseCombatWeapon *pDropWeapon = Weapon_GetSlot( pCSWeapon->GetSlot() );
  4583. if ( pDropWeapon )
  4584. {
  4585. CSWeaponDrop( pDropWeapon, false, true );
  4586. }
  4587. }
  4588. else if( pCSWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE )
  4589. {
  4590. //if we already have this weapon, just add the ammo and destroy it
  4591. if( Weapon_OwnsThisType( pCSWeapon->GetClassname() ) )
  4592. {
  4593. Weapon_EquipAmmoOnly( pWeapon );
  4594. UTIL_Remove( pCSWeapon );
  4595. return;
  4596. }
  4597. }
  4598. pCSWeapon->SetSolidFlags( FSOLID_NOT_SOLID );
  4599. pCSWeapon->SetOwnerEntity( this );
  4600. }
  4601. BaseClass::Weapon_Equip( pWeapon );
  4602. }
  4603. bool CCSPlayer::Weapon_CanUse( CBaseCombatWeapon *pBaseWeapon )
  4604. {
  4605. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  4606. if ( pWeapon )
  4607. {
  4608. // Don't give weapon_c4 to non-terrorists
  4609. if( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_C4 && GetTeamNumber() != TEAM_TERRORIST )
  4610. {
  4611. return false;
  4612. }
  4613. }
  4614. return true;
  4615. }
  4616. bool CCSPlayer::BumpWeapon( CBaseCombatWeapon *pBaseWeapon )
  4617. {
  4618. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  4619. if ( !pWeapon )
  4620. {
  4621. Assert( !pWeapon );
  4622. pBaseWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
  4623. pBaseWeapon->AddEffects( EF_NODRAW );
  4624. Weapon_Equip( pBaseWeapon );
  4625. return true;
  4626. }
  4627. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  4628. // Can I have this weapon type?
  4629. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  4630. {
  4631. extern int gEvilImpulse101;
  4632. if ( gEvilImpulse101 )
  4633. {
  4634. UTIL_Remove( pWeapon );
  4635. }
  4636. return false;
  4637. }
  4638. // Even if we already have a grenade in this slot, we can pickup another one if we don't already
  4639. // own this type of grenade.
  4640. bool bPickupGrenade = ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE );
  4641. /*
  4642. // ----------------------------------------
  4643. // If I already have it just take the ammo
  4644. // ----------------------------------------
  4645. if ( !bPickupGrenade && Weapon_SlotOccupied( pWeapon ) )
  4646. {
  4647. Weapon_EquipAmmoOnly( pWeapon );
  4648. // Only remove me if I have no ammo left
  4649. // Can't just check HasAnyAmmo because if I don't use clips, I want to be removed,
  4650. if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() )
  4651. return false;
  4652. UTIL_Remove( pWeapon );
  4653. return false;
  4654. }
  4655. */
  4656. if ( HasShield() && pWeapon->GetCSWpnData().m_bCanUseWithShield == false )
  4657. return false;
  4658. // Check ammo counts for grenades, and don't try to pick up more grenades than we can carry
  4659. if ( bPickupGrenade )
  4660. {
  4661. CBaseCombatWeapon *pOwnedGrenade = Weapon_OwnsThisType( pWeapon->GetClassname() );
  4662. if( pOwnedGrenade )
  4663. {
  4664. int numGrenades = 0;
  4665. int maxGrenades = 0;
  4666. int ammoIndex = pOwnedGrenade->GetPrimaryAmmoType();
  4667. if( ammoIndex != -1 )
  4668. {
  4669. numGrenades = GetAmmoCount( ammoIndex );
  4670. }
  4671. maxGrenades = GetAmmoDef()->MaxCarry(ammoIndex);
  4672. if( numGrenades >= maxGrenades )
  4673. {
  4674. return false;
  4675. }
  4676. }
  4677. }
  4678. if( bPickupGrenade || !Weapon_SlotOccupied( pWeapon ) )
  4679. {
  4680. pWeapon->CheckRespawn();
  4681. pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
  4682. pWeapon->AddEffects( EF_NODRAW );
  4683. CCSPlayer* pDonor = pWeapon->GetDonor();
  4684. if ( pDonor && pDonor != this && pWeapon->GetCSWpnData().GetWeaponPrice() > m_iAccount )
  4685. {
  4686. CCS_GameStats.Event_PlayerDonatedWeapon( pDonor );
  4687. }
  4688. pWeapon->SetDonor(NULL);
  4689. Weapon_Equip( pWeapon );
  4690. int iExtraAmmo = pWeapon->GetExtraAmmoCount();
  4691. if( iExtraAmmo && !bPickupGrenade )
  4692. {
  4693. //Find out the index of the ammo
  4694. int iAmmoIndex = pWeapon->GetPrimaryAmmoType();
  4695. if( iAmmoIndex != -1 )
  4696. {
  4697. //Remove the extra ammo from the weapon
  4698. pWeapon->SetExtraAmmoCount(0);
  4699. //Give it to the player
  4700. SetAmmoCount( iExtraAmmo, iAmmoIndex );
  4701. }
  4702. }
  4703. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  4704. if( event )
  4705. {
  4706. const char *weaponName = pWeapon->GetClassname();
  4707. if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
  4708. {
  4709. weaponName += 7;
  4710. }
  4711. event->SetInt( "userid", GetUserID() );
  4712. event->SetString( "item", weaponName );
  4713. gameeventmanager->FireEvent( event );
  4714. }
  4715. return true;
  4716. }
  4717. return false;
  4718. }
  4719. void CCSPlayer::ResetStamina( void )
  4720. {
  4721. m_flStamina = 0.0f;
  4722. }
  4723. void CCSPlayer::RescueZoneTouch( inputdata_t &inputdata )
  4724. {
  4725. m_bInHostageRescueZone = true;
  4726. if ( GetTeamNumber() == TEAM_CT && !(m_iDisplayHistoryBits & DHF_IN_RESCUE_ZONE) )
  4727. {
  4728. HintMessage( "#Hint_hostage_rescue_zone", false );
  4729. m_iDisplayHistoryBits |= DHF_IN_RESCUE_ZONE;
  4730. }
  4731. }
  4732. //------------------------------------------------------------------------------------------
  4733. CON_COMMAND( timeleft, "prints the time remaining in the match" )
  4734. {
  4735. CCSPlayer *pPlayer = ToCSPlayer( UTIL_GetCommandClient() );
  4736. if ( pPlayer && pPlayer->m_iNextTimeCheck >= gpGlobals->curtime )
  4737. {
  4738. return; // rate limiting
  4739. }
  4740. int iTimeRemaining = (int)CSGameRules()->GetMapRemainingTime();
  4741. if ( iTimeRemaining < 0 )
  4742. {
  4743. if ( pPlayer )
  4744. {
  4745. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_no_timelimit" );
  4746. }
  4747. else
  4748. {
  4749. Msg( "* No Time Limit *\n" );
  4750. }
  4751. }
  4752. else if ( iTimeRemaining == 0 )
  4753. {
  4754. if ( pPlayer )
  4755. {
  4756. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_last_round" );
  4757. }
  4758. else
  4759. {
  4760. Msg( "* Last Round *\n" );
  4761. }
  4762. }
  4763. else
  4764. {
  4765. int iMinutes, iSeconds;
  4766. iMinutes = iTimeRemaining / 60;
  4767. iSeconds = iTimeRemaining % 60;
  4768. char minutes[8];
  4769. char seconds[8];
  4770. Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes );
  4771. Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds );
  4772. if ( pPlayer )
  4773. {
  4774. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_timelimit", minutes, seconds );
  4775. }
  4776. else
  4777. {
  4778. Msg( "Time Remaining: %s:%s\n", minutes, seconds );
  4779. }
  4780. }
  4781. if ( pPlayer )
  4782. {
  4783. pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
  4784. }
  4785. }
  4786. //------------------------------------------------------------------------------------------
  4787. /**
  4788. * Emit given sound that only we can hear
  4789. */
  4790. void CCSPlayer::EmitPrivateSound( const char *soundName )
  4791. {
  4792. CSoundParameters params;
  4793. if (!GetParametersForSound( soundName, params, NULL ))
  4794. return;
  4795. CSingleUserRecipientFilter filter( this );
  4796. EmitSound( filter, entindex(), soundName );
  4797. }
  4798. //=====================
  4799. //Autobuy
  4800. //=====================
  4801. static void AutoBuy( void )
  4802. {
  4803. CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() );
  4804. if ( player )
  4805. player->AutoBuy();
  4806. }
  4807. static ConCommand autobuy( "autobuy", AutoBuy, "Attempt to purchase items with the order listed in cl_autobuy" );
  4808. //==============================================
  4809. //AutoBuy - do the work of deciding what to buy
  4810. //==============================================
  4811. void CCSPlayer::AutoBuy()
  4812. {
  4813. if ( !IsInBuyZone() )
  4814. {
  4815. EmitPrivateSound( "BuyPreset.CantBuy" );
  4816. return;
  4817. }
  4818. const char *autobuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_autobuy" );
  4819. if ( !autobuyString || !*autobuyString )
  4820. {
  4821. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  4822. return;
  4823. }
  4824. bool boughtPrimary = false, boughtSecondary = false;
  4825. m_bIsInAutoBuy = true;
  4826. ParseAutoBuyString(autobuyString, boughtPrimary, boughtSecondary);
  4827. m_bIsInAutoBuy = false;
  4828. m_bAutoReload = true;
  4829. //TODO ?: stripped out all the attempts to buy career weapons.
  4830. // as we're not porting cs:cz, these were skipped
  4831. }
  4832. void CCSPlayer::ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary)
  4833. {
  4834. char command[32];
  4835. int nBuffSize = sizeof(command) - 1; // -1 to leave space for the NULL at the end of the string
  4836. const char *c = string;
  4837. if (c == NULL)
  4838. {
  4839. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  4840. return;
  4841. }
  4842. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  4843. // loop through the string of commands, trying each one in turn.
  4844. while (*c != 0)
  4845. {
  4846. int i = 0;
  4847. // copy the next word into the command buffer.
  4848. while ((*c != 0) && (*c != ' ') && (i < nBuffSize))
  4849. {
  4850. command[i] = *(c);
  4851. ++c;
  4852. ++i;
  4853. }
  4854. if (*c == ' ')
  4855. {
  4856. ++c; // skip the space.
  4857. }
  4858. command[i] = 0; // terminate the string.
  4859. // clear out any spaces.
  4860. i = 0;
  4861. while (command[i] != 0)
  4862. {
  4863. if (command[i] == ' ')
  4864. {
  4865. command[i] = 0;
  4866. break;
  4867. }
  4868. ++i;
  4869. }
  4870. // make sure we actually have a command.
  4871. if (strlen(command) == 0)
  4872. {
  4873. continue;
  4874. }
  4875. AutoBuyInfoStruct * commandInfo = GetAutoBuyCommandInfo(command);
  4876. if (ShouldExecuteAutoBuyCommand(commandInfo, boughtPrimary, boughtSecondary))
  4877. {
  4878. BuyResult_e result = HandleCommand_Buy( command );
  4879. overallResult = CombineBuyResults( overallResult, result );
  4880. // check to see if we actually bought a primary or secondary weapon this time.
  4881. PostAutoBuyCommandProcessing(commandInfo, boughtPrimary, boughtSecondary);
  4882. }
  4883. }
  4884. if ( overallResult == BUY_CANT_AFFORD )
  4885. {
  4886. EmitPrivateSound( "BuyPreset.CantBuy" );
  4887. }
  4888. else if ( overallResult == BUY_ALREADY_HAVE )
  4889. {
  4890. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  4891. }
  4892. else if ( overallResult == BUY_BOUGHT )
  4893. {
  4894. g_iAutoBuyPurchases++;
  4895. }
  4896. }
  4897. BuyResult_e CCSPlayer::CombineBuyResults( BuyResult_e prevResult, BuyResult_e newResult )
  4898. {
  4899. if ( newResult == BUY_BOUGHT )
  4900. {
  4901. prevResult = BUY_BOUGHT;
  4902. }
  4903. else if ( prevResult != BUY_BOUGHT &&
  4904. (newResult == BUY_CANT_AFFORD || newResult == BUY_INVALID_ITEM || newResult == BUY_PLAYER_CANT_BUY ) )
  4905. {
  4906. prevResult = BUY_CANT_AFFORD;
  4907. }
  4908. return prevResult;
  4909. }
  4910. //==============================================
  4911. //PostAutoBuyCommandProcessing
  4912. //==============================================
  4913. void CCSPlayer::PostAutoBuyCommandProcessing(const AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary)
  4914. {
  4915. if (commandInfo == NULL)
  4916. {
  4917. return;
  4918. }
  4919. CBaseCombatWeapon *pPrimary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  4920. CBaseCombatWeapon *pSecondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  4921. if ((pPrimary != NULL) && (stricmp(pPrimary->GetClassname(), commandInfo->m_classname) == 0))
  4922. {
  4923. // I just bought the gun I was trying to buy.
  4924. boughtPrimary = true;
  4925. }
  4926. else if ((pPrimary == NULL) && ((commandInfo->m_class & AUTOBUYCLASS_SHIELD) == AUTOBUYCLASS_SHIELD) && HasShield())
  4927. {
  4928. // the shield is a primary weapon even though it isn't a "real" weapon.
  4929. boughtPrimary = true;
  4930. }
  4931. else if ((pSecondary != NULL) && (stricmp(pSecondary->GetClassname(), commandInfo->m_classname) == 0))
  4932. {
  4933. // I just bought the pistol I was trying to buy.
  4934. boughtSecondary = true;
  4935. }
  4936. }
  4937. bool CCSPlayer::ShouldExecuteAutoBuyCommand(const AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary)
  4938. {
  4939. if (commandInfo == NULL)
  4940. {
  4941. return false;
  4942. }
  4943. if ((boughtPrimary) && ((commandInfo->m_class & AUTOBUYCLASS_PRIMARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
  4944. {
  4945. // this is a primary weapon and we already have one.
  4946. return false;
  4947. }
  4948. if ((boughtSecondary) && ((commandInfo->m_class & AUTOBUYCLASS_SECONDARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
  4949. {
  4950. // this is a secondary weapon and we already have one.
  4951. return false;
  4952. }
  4953. if( commandInfo->m_class & AUTOBUYCLASS_ARMOR && ArmorValue() >= 100 )
  4954. {
  4955. return false;
  4956. }
  4957. return true;
  4958. }
  4959. AutoBuyInfoStruct *CCSPlayer::GetAutoBuyCommandInfo(const char *command)
  4960. {
  4961. int i = 0;
  4962. AutoBuyInfoStruct *ret = NULL;
  4963. AutoBuyInfoStruct *temp = &(g_autoBuyInfo[i]);
  4964. // loop through all the commands till we find the one that matches.
  4965. while ((ret == NULL) && (temp->m_class != (AutoBuyClassType)0))
  4966. {
  4967. temp = &(g_autoBuyInfo[i]);
  4968. ++i;
  4969. if (stricmp(temp->m_command, command) == 0)
  4970. {
  4971. ret = temp;
  4972. }
  4973. }
  4974. return ret;
  4975. }
  4976. //==============================================
  4977. //PostAutoBuyCommandProcessing
  4978. //- reorders the tokens in autobuyString based on the order of tokens in the priorityString.
  4979. //==============================================
  4980. void CCSPlayer::PrioritizeAutoBuyString(char *autobuyString, const char *priorityString)
  4981. {
  4982. char newString[256];
  4983. int newStringPos = 0;
  4984. char priorityToken[32];
  4985. if ((priorityString == NULL) || (autobuyString == NULL))
  4986. {
  4987. return;
  4988. }
  4989. const char *priorityChar = priorityString;
  4990. while (*priorityChar != 0)
  4991. {
  4992. int i = 0;
  4993. // get the next token from the priority string.
  4994. while ((*priorityChar != 0) && (*priorityChar != ' '))
  4995. {
  4996. priorityToken[i] = *priorityChar;
  4997. ++i;
  4998. ++priorityChar;
  4999. }
  5000. priorityToken[i] = 0;
  5001. // skip spaces
  5002. while (*priorityChar == ' ')
  5003. {
  5004. ++priorityChar;
  5005. }
  5006. if (strlen(priorityToken) == 0)
  5007. {
  5008. continue;
  5009. }
  5010. // see if the priority token is in the autobuy string.
  5011. // if it is, copy that token to the new string and blank out
  5012. // that token in the autobuy string.
  5013. char *autoBuyPosition = strstr(autobuyString, priorityToken);
  5014. if (autoBuyPosition != NULL)
  5015. {
  5016. while ((*autoBuyPosition != 0) && (*autoBuyPosition != ' '))
  5017. {
  5018. newString[newStringPos] = *autoBuyPosition;
  5019. *autoBuyPosition = ' ';
  5020. ++newStringPos;
  5021. ++autoBuyPosition;
  5022. }
  5023. newString[newStringPos++] = ' ';
  5024. }
  5025. }
  5026. // now just copy anything left in the autobuyString to the new string in the order it's in already.
  5027. char *autobuyPosition = autobuyString;
  5028. while (*autobuyPosition != 0)
  5029. {
  5030. // skip spaces
  5031. while (*autobuyPosition == ' ')
  5032. {
  5033. ++autobuyPosition;
  5034. }
  5035. // copy the token over to the new string.
  5036. while ((*autobuyPosition != 0) && (*autobuyPosition != ' '))
  5037. {
  5038. newString[newStringPos] = *autobuyPosition;
  5039. ++newStringPos;
  5040. ++autobuyPosition;
  5041. }
  5042. // add a space at the end.
  5043. newString[newStringPos++] = ' ';
  5044. }
  5045. // terminate the string. Trailing spaces shouldn't matter.
  5046. newString[newStringPos] = 0;
  5047. Q_snprintf(autobuyString, sizeof(autobuyString), "%s", newString);
  5048. }
  5049. //==============================================================
  5050. // ReBuy
  5051. // system for attempting to buy the weapons you had last round
  5052. //==============================================================
  5053. static void Rebuy( void )
  5054. {
  5055. CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() );
  5056. if ( player )
  5057. player->Rebuy();
  5058. }
  5059. static ConCommand rebuy( "rebuy", Rebuy, "Attempt to repurchase items with the order listed in cl_rebuy" );
  5060. void CCSPlayer::BuildRebuyStruct()
  5061. {
  5062. if (m_bIsInRebuy)
  5063. {
  5064. // if we are in the middle of a rebuy, we don't want to update the buy struct.
  5065. return;
  5066. }
  5067. CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  5068. CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  5069. // do the primary weapon/ammo stuff.
  5070. if (primary == NULL)
  5071. {
  5072. // count a shieldgun as a primary.
  5073. if (HasShield())
  5074. {
  5075. //m_rebuyStruct.m_primaryWeapon = WEAPON_SHIELDGUN;
  5076. Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, "shield", sizeof(m_rebuyStruct.m_szPrimaryWeapon) );
  5077. m_rebuyStruct.m_primaryAmmo = 0; // shields don't have ammo.
  5078. }
  5079. else
  5080. {
  5081. m_rebuyStruct.m_szPrimaryWeapon[0] = 0; // if we don't have a shield and we don't have a primary weapon, we got nuthin.
  5082. m_rebuyStruct.m_primaryAmmo = 0; // can't have ammo if we don't have a gun right?
  5083. }
  5084. }
  5085. else
  5086. {
  5087. //strip off the "weapon_"
  5088. const char *wpnName = primary->GetClassname();
  5089. Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szPrimaryWeapon) );
  5090. if( primary->GetPrimaryAmmoType() != -1 )
  5091. {
  5092. m_rebuyStruct.m_primaryAmmo = GetAmmoCount( primary->GetPrimaryAmmoType() );
  5093. }
  5094. }
  5095. // do the secondary weapon/ammo stuff.
  5096. if (secondary == NULL)
  5097. {
  5098. m_rebuyStruct.m_szSecondaryWeapon[0] = 0;
  5099. m_rebuyStruct.m_secondaryAmmo = 0; // can't have ammo if we don't have a gun right?
  5100. }
  5101. else
  5102. {
  5103. const char *wpnName = secondary->GetClassname();
  5104. Q_strncpy( m_rebuyStruct.m_szSecondaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szSecondaryWeapon) );
  5105. if( secondary->GetPrimaryAmmoType() != -1 )
  5106. {
  5107. m_rebuyStruct.m_secondaryAmmo = GetAmmoCount( secondary->GetPrimaryAmmoType() );
  5108. }
  5109. }
  5110. CBaseCombatWeapon *pGrenade;
  5111. //MATTTODO: right now you can't buy more than one grenade. make it so you can
  5112. //buy more and query the number you have.
  5113. // HE Grenade
  5114. pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" );
  5115. if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 )
  5116. {
  5117. m_rebuyStruct.m_heGrenade = GetAmmoCount(pGrenade->GetPrimaryAmmoType());
  5118. }
  5119. else
  5120. m_rebuyStruct.m_heGrenade = 0;
  5121. // flashbang
  5122. pGrenade = Weapon_OwnsThisType( "weapon_flashbang" );
  5123. if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 )
  5124. {
  5125. m_rebuyStruct.m_flashbang = GetAmmoCount(pGrenade->GetPrimaryAmmoType());
  5126. }
  5127. else
  5128. m_rebuyStruct.m_flashbang = 0;
  5129. // smokegrenade
  5130. pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" );
  5131. if ( pGrenade /*&& pGrenade->GetPrimaryAmmoType() != -1*/ )
  5132. {
  5133. m_rebuyStruct.m_smokeGrenade = 1; //GetAmmoCount(pGrenade->GetPrimaryAmmoType());
  5134. }
  5135. else
  5136. m_rebuyStruct.m_smokeGrenade = 0;
  5137. // defuser
  5138. m_rebuyStruct.m_defuser = HasDefuser();
  5139. // night vision
  5140. m_rebuyStruct.m_nightVision = m_bHasNightVision.Get(); //cast to avoid strange compiler warning
  5141. // check for armor.
  5142. m_rebuyStruct.m_armor = ( m_bHasHelmet ? 2 : ( ArmorValue() > 0 ? 1 : 0 ) );
  5143. }
  5144. void CCSPlayer::Rebuy( void )
  5145. {
  5146. if ( !IsInBuyZone() )
  5147. {
  5148. EmitPrivateSound( "BuyPreset.CantBuy" );
  5149. return;
  5150. }
  5151. const char *rebuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_rebuy" );
  5152. if ( !rebuyString || !*rebuyString )
  5153. {
  5154. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  5155. return;
  5156. }
  5157. m_bIsInRebuy = true;
  5158. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  5159. char token[256];
  5160. rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );
  5161. while (rebuyString != NULL)
  5162. {
  5163. BuyResult_e result = BUY_ALREADY_HAVE;
  5164. if (!Q_strncmp(token, "PrimaryWeapon", 14))
  5165. {
  5166. result = RebuyPrimaryWeapon();
  5167. }
  5168. else if (!Q_strncmp(token, "PrimaryAmmo", 12))
  5169. {
  5170. result = RebuyPrimaryAmmo();
  5171. }
  5172. else if (!Q_strncmp(token, "SecondaryWeapon", 16))
  5173. {
  5174. result = RebuySecondaryWeapon();
  5175. }
  5176. else if (!Q_strncmp(token, "SecondaryAmmo", 14))
  5177. {
  5178. result = RebuySecondaryAmmo();
  5179. }
  5180. else if (!Q_strncmp(token, "HEGrenade", 10))
  5181. {
  5182. result = RebuyHEGrenade();
  5183. }
  5184. else if (!Q_strncmp(token, "Flashbang", 10))
  5185. {
  5186. result = RebuyFlashbang();
  5187. }
  5188. else if (!Q_strncmp(token, "SmokeGrenade", 13))
  5189. {
  5190. result = RebuySmokeGrenade();
  5191. }
  5192. else if (!Q_strncmp(token, "Defuser", 8))
  5193. {
  5194. result = RebuyDefuser();
  5195. }
  5196. else if (!Q_strncmp(token, "NightVision", 12))
  5197. {
  5198. result = RebuyNightVision();
  5199. }
  5200. else if (!Q_strncmp(token, "Armor", 6))
  5201. {
  5202. result = RebuyArmor();
  5203. }
  5204. overallResult = CombineBuyResults( overallResult, result );
  5205. rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );
  5206. }
  5207. m_bIsInRebuy = false;
  5208. // after we're done buying, the user is done with their equipment purchasing experience.
  5209. // so we are effectively out of the buy zone.
  5210. // if (TheTutor != NULL)
  5211. // {
  5212. // TheTutor->OnEvent(EVENT_PLAYER_LEFT_BUY_ZONE);
  5213. // }
  5214. m_bAutoReload = true;
  5215. if ( overallResult == BUY_CANT_AFFORD )
  5216. {
  5217. EmitPrivateSound( "BuyPreset.CantBuy" );
  5218. }
  5219. else if ( overallResult == BUY_ALREADY_HAVE )
  5220. {
  5221. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  5222. }
  5223. else if ( overallResult == BUY_BOUGHT )
  5224. {
  5225. g_iReBuyPurchases++;
  5226. }
  5227. }
  5228. BuyResult_e CCSPlayer::RebuyPrimaryWeapon()
  5229. {
  5230. CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  5231. if (primary != NULL)
  5232. {
  5233. return BUY_ALREADY_HAVE; // don't drop primary weapons via rebuy - if the player picked up a different weapon, he wants to keep it.
  5234. }
  5235. if( strlen( m_rebuyStruct.m_szPrimaryWeapon ) > 0 )
  5236. return HandleCommand_Buy(m_rebuyStruct.m_szPrimaryWeapon);
  5237. return BUY_ALREADY_HAVE;
  5238. }
  5239. BuyResult_e CCSPlayer::RebuySecondaryWeapon()
  5240. {
  5241. CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  5242. if (pistol != NULL && !m_bUsingDefaultPistol)
  5243. {
  5244. return BUY_ALREADY_HAVE; // don't drop pistols via rebuy if we've bought one other than the default pistol
  5245. }
  5246. if( strlen( m_rebuyStruct.m_szSecondaryWeapon ) > 0 )
  5247. return HandleCommand_Buy(m_rebuyStruct.m_szSecondaryWeapon);
  5248. return BUY_ALREADY_HAVE;
  5249. }
  5250. BuyResult_e CCSPlayer::RebuyPrimaryAmmo()
  5251. {
  5252. CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  5253. if (primary == NULL)
  5254. {
  5255. return BUY_ALREADY_HAVE; // can't buy ammo when we don't even have a gun.
  5256. }
  5257. // Ensure that the weapon uses ammo
  5258. int nAmmo = primary->GetPrimaryAmmoType();
  5259. if ( nAmmo == -1 )
  5260. {
  5261. return BUY_ALREADY_HAVE;
  5262. }
  5263. // if we had more ammo before than we have now, buy more.
  5264. if (m_rebuyStruct.m_primaryAmmo > GetAmmoCount( nAmmo ))
  5265. {
  5266. return HandleCommand_Buy("primammo");
  5267. }
  5268. return BUY_ALREADY_HAVE;
  5269. }
  5270. BuyResult_e CCSPlayer::RebuySecondaryAmmo()
  5271. {
  5272. CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  5273. if (secondary == NULL)
  5274. {
  5275. return BUY_ALREADY_HAVE; // can't buy ammo when we don't even have a gun.
  5276. }
  5277. // Ensure that the weapon uses ammo
  5278. int nAmmo = secondary->GetPrimaryAmmoType();
  5279. if ( nAmmo == -1 )
  5280. {
  5281. return BUY_ALREADY_HAVE;
  5282. }
  5283. if (m_rebuyStruct.m_secondaryAmmo > GetAmmoCount( nAmmo ))
  5284. {
  5285. return HandleCommand_Buy("secammo");
  5286. }
  5287. return BUY_ALREADY_HAVE;
  5288. }
  5289. BuyResult_e CCSPlayer::RebuyHEGrenade()
  5290. {
  5291. CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" );
  5292. int numGrenades = 0;
  5293. if( pGrenade )
  5294. {
  5295. int nAmmo = pGrenade->GetPrimaryAmmoType();
  5296. if ( nAmmo == -1 )
  5297. {
  5298. return BUY_ALREADY_HAVE;
  5299. }
  5300. numGrenades = GetAmmoCount( nAmmo );
  5301. }
  5302. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  5303. int numToBuy = MAX( 0, m_rebuyStruct.m_heGrenade - numGrenades );
  5304. for (int i = 0; i < numToBuy; ++i)
  5305. {
  5306. BuyResult_e result = HandleCommand_Buy("hegrenade");
  5307. overallResult = CombineBuyResults( overallResult, result );
  5308. }
  5309. return overallResult;
  5310. }
  5311. BuyResult_e CCSPlayer::RebuyFlashbang()
  5312. {
  5313. CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_flashbang" );
  5314. int numGrenades = 0;
  5315. if( pGrenade )
  5316. {
  5317. int nAmmo = pGrenade->GetPrimaryAmmoType();
  5318. if ( nAmmo == -1 )
  5319. {
  5320. return BUY_ALREADY_HAVE;
  5321. }
  5322. numGrenades = GetAmmoCount( nAmmo );
  5323. }
  5324. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  5325. int numToBuy = MAX( 0, m_rebuyStruct.m_flashbang - numGrenades );
  5326. for (int i = 0; i < numToBuy; ++i)
  5327. {
  5328. BuyResult_e result = HandleCommand_Buy("flashbang");
  5329. overallResult = CombineBuyResults( overallResult, result );
  5330. }
  5331. return overallResult;
  5332. }
  5333. BuyResult_e CCSPlayer::RebuySmokeGrenade()
  5334. {
  5335. CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" );
  5336. int numGrenades = 0;
  5337. if( pGrenade )
  5338. {
  5339. int nAmmo = pGrenade->GetPrimaryAmmoType();
  5340. if ( nAmmo == -1 )
  5341. {
  5342. return BUY_ALREADY_HAVE;
  5343. }
  5344. numGrenades = GetAmmoCount( nAmmo );
  5345. }
  5346. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  5347. int numToBuy = MAX( 0, m_rebuyStruct.m_smokeGrenade - numGrenades );
  5348. for (int i = 0; i < numToBuy; ++i)
  5349. {
  5350. BuyResult_e result = HandleCommand_Buy("smokegrenade");
  5351. overallResult = CombineBuyResults( overallResult, result );
  5352. }
  5353. return overallResult;
  5354. }
  5355. BuyResult_e CCSPlayer::RebuyDefuser()
  5356. {
  5357. //If we don't have a defuser, and we want one, buy it!
  5358. if( !HasDefuser() && m_rebuyStruct.m_defuser )
  5359. {
  5360. return HandleCommand_Buy("defuser");
  5361. }
  5362. return BUY_ALREADY_HAVE;
  5363. }
  5364. BuyResult_e CCSPlayer::RebuyNightVision()
  5365. {
  5366. //if we don't have night vision and we want one, buy it!
  5367. if( !m_bHasNightVision && m_rebuyStruct.m_nightVision )
  5368. {
  5369. return HandleCommand_Buy("nvgs");
  5370. }
  5371. return BUY_ALREADY_HAVE;
  5372. }
  5373. BuyResult_e CCSPlayer::RebuyArmor()
  5374. {
  5375. if (m_rebuyStruct.m_armor > 0 )
  5376. {
  5377. int armor = 0;
  5378. if( m_bHasHelmet )
  5379. armor = 2;
  5380. else if( ArmorValue() > 0 )
  5381. armor = 1;
  5382. if( armor < m_rebuyStruct.m_armor )
  5383. {
  5384. if (m_rebuyStruct.m_armor == 1)
  5385. {
  5386. return HandleCommand_Buy("vest");
  5387. }
  5388. else
  5389. {
  5390. return HandleCommand_Buy("vesthelm");
  5391. }
  5392. }
  5393. }
  5394. return BUY_ALREADY_HAVE;
  5395. }
  5396. bool CCSPlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps )
  5397. {
  5398. CWeaponCSBase *pCSWepaon = dynamic_cast<CWeaponCSBase*>(pEntity);
  5399. if( pCSWepaon )
  5400. {
  5401. // we can't USE dropped weapons
  5402. return true;
  5403. }
  5404. CBaseCSGrenadeProjectile *pGrenade = dynamic_cast<CBaseCSGrenadeProjectile*>(pEntity);
  5405. if ( pGrenade )
  5406. {
  5407. // we can't USE thrown grenades
  5408. }
  5409. return BaseClass::IsUseableEntity( pEntity, requiredCaps );
  5410. }
  5411. CBaseEntity *CCSPlayer::FindUseEntity()
  5412. {
  5413. CBaseEntity *entity = NULL;
  5414. // Check to see if the bomb is close enough to use before attempting to use anything else.
  5415. if ( CSGameRules()->IsBombDefuseMap() && GetTeamNumber() == TEAM_CT )
  5416. {
  5417. // This is done separately since there might be something blocking our LOS to it
  5418. // but we might want to use it anyway if it's close enough. This should eliminate
  5419. // the vast majority of bomb placement exploits (places where the bomb can be planted
  5420. // but can't be "used". This also mimics goldsrc cstrike behavior.
  5421. CBaseEntity *bomb = gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME );
  5422. if (bomb != NULL)
  5423. {
  5424. Vector bombPos = bomb->GetAbsOrigin();
  5425. Vector vecLOS = EyePosition() - bombPos;
  5426. if (vecLOS.LengthSqr() < (96*96)) // 64 is the distance in Goldsrc. However since Goldsrc did distance from the player's origin and we're doing distance from the player's eye, make the radius a bit bigger.
  5427. {
  5428. // bomb is close enough, now make sure the player is facing the bomb.
  5429. Vector forward;
  5430. AngleVectors(EyeAngles(), &forward, NULL, NULL);
  5431. vecLOS.NormalizeInPlace();
  5432. float flDot = DotProduct(forward, vecLOS);
  5433. if (flDot < -0.7) // 0.7 taken from Goldsrc, +/- ~45 degrees
  5434. {
  5435. entity = bomb;
  5436. }
  5437. }
  5438. }
  5439. }
  5440. if ( entity == NULL )
  5441. {
  5442. entity = BaseClass::FindUseEntity();
  5443. }
  5444. return entity;
  5445. }
  5446. void CCSPlayer::StockPlayerAmmo( CBaseCombatWeapon *pNewWeapon )
  5447. {
  5448. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >( pNewWeapon );
  5449. if ( pWeapon )
  5450. {
  5451. if ( pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE )
  5452. return;
  5453. int nAmmo = pWeapon->GetPrimaryAmmoType();
  5454. if ( nAmmo != -1 )
  5455. {
  5456. GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
  5457. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  5458. }
  5459. return;
  5460. }
  5461. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
  5462. if ( pWeapon )
  5463. {
  5464. int nAmmo = pWeapon->GetPrimaryAmmoType();
  5465. if ( nAmmo != -1 )
  5466. {
  5467. GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
  5468. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  5469. }
  5470. }
  5471. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
  5472. if ( pWeapon )
  5473. {
  5474. int nAmmo = pWeapon->GetPrimaryAmmoType();
  5475. if ( nAmmo != -1 )
  5476. {
  5477. GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
  5478. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  5479. }
  5480. }
  5481. }
  5482. CBaseEntity *CCSPlayer::GiveNamedItem( const char *pszName, int iSubType )
  5483. {
  5484. EHANDLE pent;
  5485. if ( !pszName || !pszName[0] )
  5486. return NULL;
  5487. #ifndef CS_SHIELD_ENABLED
  5488. if ( !Q_stricmp( pszName, "weapon_shield" ) )
  5489. return NULL;
  5490. #endif
  5491. pent = CreateEntityByName(pszName);
  5492. if ( pent == NULL )
  5493. {
  5494. Msg( "NULL Ent in GiveNamedItem!\n" );
  5495. return NULL;
  5496. }
  5497. pent->SetLocalOrigin( GetLocalOrigin() );
  5498. pent->AddSpawnFlags( SF_NORESPAWN );
  5499. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pent );
  5500. if ( pWeapon )
  5501. {
  5502. if ( iSubType )
  5503. {
  5504. pWeapon->SetSubType( iSubType );
  5505. }
  5506. }
  5507. DispatchSpawn( pent );
  5508. m_bIsBeingGivenItem = true;
  5509. if ( pent != NULL && !(pent->IsMarkedForDeletion()) )
  5510. {
  5511. pent->Touch( this );
  5512. }
  5513. m_bIsBeingGivenItem = false;
  5514. StockPlayerAmmo( pWeapon );
  5515. return pent;
  5516. }
  5517. void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  5518. {
  5519. if ( event == PLAYERANIMEVENT_THROW_GRENADE )
  5520. {
  5521. // Grenade throwing has to synchronize exactly with the player's grenade weapon going away,
  5522. // and events get delayed a bit, so we let CCSPlayerAnimState pickup the change to this
  5523. // variable.
  5524. m_iThrowGrenadeCounter = (m_iThrowGrenadeCounter+1) % (1<<THROWGRENADE_COUNTER_BITS);
  5525. }
  5526. else
  5527. {
  5528. m_PlayerAnimState->DoAnimationEvent( event, nData );
  5529. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  5530. }
  5531. }
  5532. //-----------------------------------------------------------------------------
  5533. //-----------------------------------------------------------------------------
  5534. int CCSPlayer::FlashlightIsOn( void )
  5535. {
  5536. return IsEffectActive( EF_DIMLIGHT );
  5537. }
  5538. extern ConVar flashlight;
  5539. //-----------------------------------------------------------------------------
  5540. //-----------------------------------------------------------------------------
  5541. void CCSPlayer::FlashlightTurnOn( void )
  5542. {
  5543. if( flashlight.GetInt() > 0 && IsAlive() )
  5544. {
  5545. AddEffects( EF_DIMLIGHT );
  5546. EmitSound( "Player.FlashlightOn" );
  5547. }
  5548. }
  5549. //-----------------------------------------------------------------------------
  5550. //-----------------------------------------------------------------------------
  5551. void CCSPlayer::FlashlightTurnOff( void )
  5552. {
  5553. RemoveEffects( EF_DIMLIGHT );
  5554. if( IsAlive() )
  5555. {
  5556. EmitSound( "Player.FlashlightOff" );
  5557. }
  5558. }
  5559. //Drop the appropriate weapons:
  5560. // Defuser if we have one
  5561. // C4 if we have one
  5562. // The best weapon we have, first check primary,
  5563. // then secondary and drop the best one
  5564. //=============================================================================
  5565. // HPE_BEGIN:
  5566. // [tj] Added a parameter so we know if it was death that caused the drop
  5567. // [menglish] Clear all previously dropped equipment and add the c4 to the dropped equipment
  5568. //=============================================================================
  5569. void CCSPlayer::DropWeapons( bool fromDeath, bool friendlyFire )
  5570. {
  5571. for ( int i = 0; i < DROPPED_COUNT; ++i )
  5572. {
  5573. m_hDroppedEquipment[i] = NULL;
  5574. }
  5575. CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
  5576. if ( pC4 )
  5577. {
  5578. CSWeaponDrop( pC4, false, true );
  5579. if( fromDeath )
  5580. {
  5581. if( friendlyFire )
  5582. {
  5583. (static_cast<CC4*> (pC4))->SetDroppedFromDeath(true);
  5584. }
  5585. m_hDroppedEquipment[DROPPED_C4] = static_cast<CBaseEntity *>(pC4);
  5586. }
  5587. }
  5588. //NOTE: Function continues beyond comment block. This is just the part I touched.
  5589. //=============================================================================
  5590. // HPE_END
  5591. //=============================================================================
  5592. if( HasDefuser() )
  5593. {
  5594. //Drop an item_defuser
  5595. Vector vForward, vRight;
  5596. AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
  5597. CBaseAnimating *pDefuser = (CBaseAnimating *)CBaseEntity::Create( "item_defuser", WorldSpaceCenter(), GetLocalAngles(), this );
  5598. pDefuser->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );
  5599. RemoveDefuser();
  5600. //=============================================================================
  5601. // HPE_BEGIN:
  5602. // [menglish] Add the newly created defuser to the dropped equipment list
  5603. //=============================================================================
  5604. if(fromDeath)
  5605. {
  5606. m_hDroppedEquipment[DROPPED_DEFUSE] = static_cast<CBaseEntity *>(pDefuser);
  5607. }
  5608. //=============================================================================
  5609. // HPE_END
  5610. //=============================================================================
  5611. }
  5612. if( HasShield() )
  5613. {
  5614. DropShield();
  5615. }
  5616. else
  5617. {
  5618. //drop the best weapon we have
  5619. if( !DropRifle( true ) )
  5620. DropPistol( true );
  5621. }
  5622. // drop any live grenades so they explode
  5623. CBaseCSGrenade *pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_hegrenade"));
  5624. if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
  5625. {
  5626. pGrenade->DropGrenade();
  5627. pGrenade->DecrementAmmo( this );
  5628. }
  5629. else
  5630. {
  5631. pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_flashbang"));
  5632. if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
  5633. {
  5634. pGrenade->DropGrenade();
  5635. pGrenade->DecrementAmmo( this );
  5636. }
  5637. else
  5638. {
  5639. pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_smokegrenade"));
  5640. if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
  5641. {
  5642. pGrenade->DropGrenade();
  5643. pGrenade->DecrementAmmo( this );
  5644. }
  5645. }
  5646. }
  5647. // drop the "best" grenade remaining
  5648. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType("weapon_hegrenade");
  5649. bool grenadeDrop = false;
  5650. if ( pWeapon && pWeapon->HasAmmo() )
  5651. {
  5652. grenadeDrop = CSWeaponDrop(pWeapon, false);
  5653. }
  5654. else
  5655. {
  5656. pWeapon = Weapon_OwnsThisType("weapon_flashbang");
  5657. if ( pWeapon && pWeapon->HasAmmo() )
  5658. {
  5659. grenadeDrop = CSWeaponDrop(pWeapon, false);
  5660. }
  5661. else
  5662. {
  5663. pWeapon = Weapon_OwnsThisType("weapon_smokegrenade");
  5664. if ( pWeapon && pWeapon->HasAmmo() )
  5665. {
  5666. grenadeDrop = CSWeaponDrop(pWeapon, false);
  5667. }
  5668. }
  5669. }
  5670. //=============================================================================
  5671. // HPE_BEGIN:
  5672. // [menglish] Add whichever, if any, grenade was dropped
  5673. //=============================================================================
  5674. if( pWeapon && grenadeDrop )
  5675. {
  5676. m_hDroppedEquipment[DROPPED_GRENADE] = static_cast<CBaseEntity *>(pWeapon);
  5677. }
  5678. //=============================================================================
  5679. // HPE_END
  5680. //=============================================================================
  5681. }
  5682. //-----------------------------------------------------------------------------
  5683. // Purpose: Put the player in the specified team
  5684. //-----------------------------------------------------------------------------
  5685. void CCSPlayer::ChangeTeam( int iTeamNum )
  5686. {
  5687. if ( !GetGlobalTeam( iTeamNum ) )
  5688. {
  5689. Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
  5690. return;
  5691. }
  5692. int iOldTeam = GetTeamNumber();
  5693. // if this is our current team, just abort
  5694. if ( iTeamNum == iOldTeam )
  5695. return;
  5696. //=============================================================================
  5697. // HPE_BEGIN:
  5698. //=============================================================================
  5699. // [tj] Added a parameter so we know if it was death that caused the drop
  5700. // Drop Our best weapon
  5701. DropWeapons(false, false);
  5702. // [tj] Clear out dominations
  5703. RemoveNemesisRelationships();
  5704. //=============================================================================
  5705. // HPE_END
  5706. //=============================================================================
  5707. // Always allow a change to spectator, and don't count it as one of our team changes.
  5708. // We now store the old team, so if a player changes once to one team, then to spectator,
  5709. // they won't be able to change back to their old old team, but will still be able to join
  5710. // the team they initially changed to.
  5711. if( iTeamNum != TEAM_SPECTATOR )
  5712. {
  5713. m_bTeamChanged = true;
  5714. }
  5715. else
  5716. {
  5717. m_iOldTeam = iOldTeam;
  5718. }
  5719. // do the team change:
  5720. BaseClass::ChangeTeam( iTeamNum );
  5721. //reset class
  5722. m_iClass = (int)CS_CLASS_NONE;
  5723. // update client state
  5724. if ( iTeamNum == TEAM_UNASSIGNED )
  5725. {
  5726. State_Transition( STATE_OBSERVER_MODE );
  5727. }
  5728. else if ( iTeamNum == TEAM_SPECTATOR )
  5729. {
  5730. //=============================================================================
  5731. // HPE_BEGIN:
  5732. // [tj] Removed these lines so players keep their money when switching to spectator.
  5733. //=============================================================================
  5734. //Reset money
  5735. //m_iAccount = 0;
  5736. //=============================================================================
  5737. // HPE_END
  5738. //=============================================================================
  5739. RemoveAllItems( true );
  5740. State_Transition( STATE_OBSERVER_MODE );
  5741. }
  5742. else // active player
  5743. {
  5744. if ( iOldTeam == TEAM_SPECTATOR )
  5745. {
  5746. // If they're switching from being a spectator to ingame player
  5747. //=============================================================================
  5748. // HPE_BEGIN:
  5749. // [tj] Changed this so players either retain their existing money or,
  5750. // if they have less than the default, give them the default.
  5751. //=============================================================================
  5752. int startMoney = CSGameRules()->GetStartMoney();
  5753. if (startMoney > m_iAccount)
  5754. {
  5755. m_iAccount = startMoney;
  5756. }
  5757. //=============================================================================
  5758. // HPE_END
  5759. //=============================================================================
  5760. }
  5761. // bots get to this state on TEAM_UNASSIGNED, yet they are marked alive. Don't kill them.
  5762. else if ( iOldTeam != TEAM_UNASSIGNED && !IsDead() )
  5763. {
  5764. // Kill player if switching teams while alive
  5765. CommitSuicide();
  5766. }
  5767. // Put up the class selection menu.
  5768. State_Transition( STATE_PICKINGCLASS );
  5769. }
  5770. // Initialize the player counts now that a player has switched teams
  5771. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  5772. CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  5773. }
  5774. //-----------------------------------------------------------------------------
  5775. // Purpose: Put the player in the specified team without penalty
  5776. //-----------------------------------------------------------------------------
  5777. void CCSPlayer::SwitchTeam( int iTeamNum )
  5778. {
  5779. if ( !GetGlobalTeam( iTeamNum ) || (iTeamNum != TEAM_CT && iTeamNum != TEAM_TERRORIST) )
  5780. {
  5781. Warning( "CCSPlayer::SwitchTeam( %d ) - invalid team index.\n", iTeamNum );
  5782. return;
  5783. }
  5784. int iOldTeam = GetTeamNumber();
  5785. // if this is our current team, just abort
  5786. if ( iTeamNum == iOldTeam )
  5787. return;
  5788. // Always allow a change to spectator, and don't count it as one of our team changes.
  5789. // We now store the old team, so if a player changes once to one team, then to spectator,
  5790. // they won't be able to change back to their old old team, but will still be able to join
  5791. // the team they initially changed to.
  5792. m_bTeamChanged = true;
  5793. // do the team change:
  5794. BaseClass::ChangeTeam( iTeamNum );
  5795. if( HasDefuser() )
  5796. {
  5797. RemoveDefuser();
  5798. }
  5799. //reset class
  5800. switch ( m_iClass )
  5801. {
  5802. // Terrorist -> CT
  5803. case CS_CLASS_PHOENIX_CONNNECTION:
  5804. m_iClass = (int)CS_CLASS_SEAL_TEAM_6;
  5805. break;
  5806. case CS_CLASS_L337_KREW:
  5807. m_iClass = (int)CS_CLASS_GSG_9;
  5808. break;
  5809. case CS_CLASS_ARCTIC_AVENGERS:
  5810. m_iClass = (int)CS_CLASS_SAS;
  5811. break;
  5812. case CS_CLASS_GUERILLA_WARFARE:
  5813. m_iClass = (int)CS_CLASS_GIGN;
  5814. break;
  5815. // CT -> Terrorist
  5816. case CS_CLASS_SEAL_TEAM_6:
  5817. m_iClass = (int)CS_CLASS_PHOENIX_CONNNECTION;
  5818. break;
  5819. case CS_CLASS_GSG_9:
  5820. m_iClass = (int)CS_CLASS_L337_KREW;
  5821. break;
  5822. case CS_CLASS_SAS:
  5823. m_iClass = (int)CS_CLASS_ARCTIC_AVENGERS;
  5824. break;
  5825. case CS_CLASS_GIGN:
  5826. m_iClass = (int)CS_CLASS_GUERILLA_WARFARE;
  5827. break;
  5828. case CS_CLASS_NONE:
  5829. default:
  5830. break;
  5831. }
  5832. // Initialize the player counts now that a player has switched teams
  5833. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  5834. CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  5835. }
  5836. void CCSPlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set )
  5837. {
  5838. // this is for giving player info to the hostage response system
  5839. // and is as yet unused.
  5840. // Eventually we could give the hostage a few tidbits about this player,
  5841. // eg their health, what weapons they have, and the hostage could
  5842. // comment accordingly.
  5843. //do not append any player data to the Criteria!
  5844. //we don't know which player we should be caring about
  5845. }
  5846. static unsigned int s_BulletGroupCounter = 0;
  5847. void CCSPlayer::StartNewBulletGroup()
  5848. {
  5849. s_BulletGroupCounter++;
  5850. }
  5851. //=======================================================
  5852. // Remember this amount of damage that we dealt for stats
  5853. //=======================================================
  5854. void CCSPlayer::RecordDamageGiven( const char *szDamageTaker, int iDamageGiven )
  5855. {
  5856. FOR_EACH_LL( m_DamageGivenList, i )
  5857. {
  5858. if( Q_strncmp( szDamageTaker, m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
  5859. {
  5860. m_DamageGivenList[i]->AddDamage( iDamageGiven, s_BulletGroupCounter );
  5861. return;
  5862. }
  5863. }
  5864. CDamageRecord *record = new CDamageRecord( szDamageTaker, iDamageGiven, s_BulletGroupCounter );
  5865. int k = m_DamageGivenList.AddToTail();
  5866. m_DamageGivenList[k] = record;
  5867. }
  5868. //=======================================================
  5869. // Remember this amount of damage that we took for stats
  5870. //=======================================================
  5871. void CCSPlayer::RecordDamageTaken( const char *szDamageDealer, int iDamageTaken )
  5872. {
  5873. FOR_EACH_LL( m_DamageTakenList, i )
  5874. {
  5875. if( Q_strncmp( szDamageDealer, m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
  5876. {
  5877. m_DamageTakenList[i]->AddDamage( iDamageTaken, s_BulletGroupCounter );
  5878. return;
  5879. }
  5880. }
  5881. CDamageRecord *record = new CDamageRecord( szDamageDealer, iDamageTaken, s_BulletGroupCounter );
  5882. int k = m_DamageTakenList.AddToTail();
  5883. m_DamageTakenList[k] = record;
  5884. }
  5885. //=======================================================
  5886. // Reset our damage given and taken counters
  5887. //=======================================================
  5888. void CCSPlayer::ResetDamageCounters()
  5889. {
  5890. m_DamageGivenList.PurgeAndDeleteElements();
  5891. m_DamageTakenList.PurgeAndDeleteElements();
  5892. }
  5893. //=======================================================
  5894. // Output the damage that we dealt to other players
  5895. //=======================================================
  5896. void CCSPlayer::OutputDamageTaken( void )
  5897. {
  5898. bool bPrintHeader = true;
  5899. CDamageRecord *pRecord;
  5900. char buf[64];
  5901. int msg_dest = HUD_PRINTCONSOLE;
  5902. FOR_EACH_LL( m_DamageTakenList, i )
  5903. {
  5904. if( bPrintHeader )
  5905. {
  5906. ClientPrint( this, msg_dest, "Player: %s1 - Damage Taken\n", GetPlayerName() );
  5907. ClientPrint( this, msg_dest, "-------------------------\n" );
  5908. bPrintHeader = false;
  5909. }
  5910. pRecord = m_DamageTakenList[i];
  5911. if( pRecord )
  5912. {
  5913. if (pRecord->GetNumHits() == 1)
  5914. {
  5915. Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
  5916. }
  5917. else
  5918. {
  5919. Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
  5920. }
  5921. ClientPrint( this, msg_dest, "Damage Taken from \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
  5922. }
  5923. }
  5924. }
  5925. //=======================================================
  5926. // Output the damage that we took from other players
  5927. //=======================================================
  5928. void CCSPlayer::OutputDamageGiven( void )
  5929. {
  5930. bool bPrintHeader = true;
  5931. CDamageRecord *pRecord;
  5932. char buf[64];
  5933. int msg_dest = HUD_PRINTCONSOLE;
  5934. FOR_EACH_LL( m_DamageGivenList, i )
  5935. {
  5936. if( bPrintHeader )
  5937. {
  5938. ClientPrint( this, msg_dest, "Player: %s1 - Damage Given\n", GetPlayerName() );
  5939. ClientPrint( this, msg_dest, "-------------------------\n" );
  5940. bPrintHeader = false;
  5941. }
  5942. pRecord = m_DamageGivenList[i];
  5943. if( pRecord )
  5944. {
  5945. if (pRecord->GetNumHits() == 1)
  5946. {
  5947. Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
  5948. }
  5949. else
  5950. {
  5951. Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
  5952. }
  5953. ClientPrint( this, msg_dest, "Damage Given to \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
  5954. }
  5955. }
  5956. }
  5957. void CCSPlayer::CreateViewModel( int index /*=0*/ )
  5958. {
  5959. Assert( index >= 0 && index < MAX_VIEWMODELS );
  5960. if ( GetViewModel( index ) )
  5961. return;
  5962. CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
  5963. if ( vm )
  5964. {
  5965. vm->SetAbsOrigin( GetAbsOrigin() );
  5966. vm->SetOwner( this );
  5967. vm->SetIndex( index );
  5968. DispatchSpawn( vm );
  5969. vm->FollowEntity( this, false );
  5970. m_hViewModel.Set( index, vm );
  5971. }
  5972. }
  5973. bool CCSPlayer::HasC4() const
  5974. {
  5975. return ( Weapon_OwnsThisType( "weapon_c4" ) != NULL );
  5976. }
  5977. int CCSPlayer::GetNextObserverSearchStartPoint( bool bReverse )
  5978. {
  5979. // If we are currently watching someone who is dead, they must have died while we were watching (since
  5980. // a dead guy is not a valid pick to start watching). He was given his killer as an observer target
  5981. // when he died, so let's start by trying to observe his killer. If we fail, we'll use the normal way.
  5982. // And this is just the start point anyway, but we want to start the search here in case it is okay.
  5983. if( m_hObserverTarget && !m_hObserverTarget->IsAlive() )
  5984. {
  5985. CCSPlayer *targetPlayer = ToCSPlayer(m_hObserverTarget);
  5986. if( targetPlayer && targetPlayer->GetObserverTarget() )
  5987. return targetPlayer->GetObserverTarget()->entindex();
  5988. }
  5989. return BaseClass::GetNextObserverSearchStartPoint( bReverse );
  5990. }
  5991. void CCSPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
  5992. {
  5993. BaseClass::PlayStepSound( vecOrigin, psurface, fvol, force );
  5994. if ( !sv_footsteps.GetFloat() )
  5995. return;
  5996. if ( !psurface )
  5997. return;
  5998. IGameEvent * event = gameeventmanager->CreateEvent( "player_footstep" );
  5999. if ( event )
  6000. {
  6001. event->SetInt("userid", GetUserID() );
  6002. gameeventmanager->FireEvent( event );
  6003. }
  6004. m_bMadeFootstepNoise = true;
  6005. }
  6006. void CCSPlayer::SelectDeathPose( const CTakeDamageInfo &info )
  6007. {
  6008. MDLCACHE_CRITICAL_SECTION();
  6009. if ( !GetModelPtr() )
  6010. return;
  6011. Activity aActivity = ACT_INVALID;
  6012. int iDeathFrame = 0;
  6013. SelectDeathPoseActivityAndFrame( this, info, m_LastHitGroup, aActivity, iDeathFrame );
  6014. if ( aActivity == ACT_INVALID )
  6015. {
  6016. SetDeathPose( ACT_INVALID );
  6017. SetDeathPoseFrame( 0 );
  6018. return;
  6019. }
  6020. SetDeathPose( SelectWeightedSequence( aActivity ) );
  6021. SetDeathPoseFrame( iDeathFrame );
  6022. }
  6023. void CCSPlayer::HandleAnimEvent( animevent_t *pEvent )
  6024. {
  6025. if ( pEvent->event == 4001 || pEvent->event == 4002 )
  6026. {
  6027. // Ignore these for now - soon we will be playing footstep sounds based on these events
  6028. // that mark footfalls in the anims.
  6029. }
  6030. else
  6031. {
  6032. BaseClass::HandleAnimEvent( pEvent );
  6033. }
  6034. }
  6035. bool CCSPlayer::CanChangeName( void )
  6036. {
  6037. if ( IsBot() )
  6038. return true;
  6039. // enforce the minimum interval
  6040. if ( (m_flNameChangeHistory[0] + MIN_NAME_CHANGE_INTERVAL) >= gpGlobals->curtime )
  6041. {
  6042. return false;
  6043. }
  6044. // enforce that we dont do more than NAME_CHANGE_HISTORY_SIZE
  6045. // changes within NAME_CHANGE_HISTORY_INTERVAL
  6046. if ( (m_flNameChangeHistory[NAME_CHANGE_HISTORY_SIZE-1] + NAME_CHANGE_HISTORY_INTERVAL) >= gpGlobals->curtime )
  6047. {
  6048. return false;
  6049. }
  6050. return true;
  6051. }
  6052. void CCSPlayer::ChangeName( const char *pszNewName )
  6053. {
  6054. // make sure name is not too long
  6055. char trimmedName[MAX_PLAYER_NAME_LENGTH];
  6056. Q_strncpy( trimmedName, pszNewName, sizeof( trimmedName ) );
  6057. const char *pszOldName = GetPlayerName();
  6058. // send colored message to everyone
  6059. CReliableBroadcastRecipientFilter filter;
  6060. UTIL_SayText2Filter( filter, this, false, "#Cstrike_Name_Change", pszOldName, trimmedName );
  6061. // broadcast event
  6062. IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
  6063. if ( event )
  6064. {
  6065. event->SetInt( "userid", GetUserID() );
  6066. event->SetString( "oldname", pszOldName );
  6067. event->SetString( "newname", trimmedName );
  6068. gameeventmanager->FireEvent( event );
  6069. }
  6070. // change shared player name
  6071. SetPlayerName( trimmedName );
  6072. // tell engine to use new name
  6073. engine->ClientCommand( edict(), "name \"%s\"", trimmedName );
  6074. // remember time of name change
  6075. for ( int i=NAME_CHANGE_HISTORY_SIZE-1; i>0; i-- )
  6076. {
  6077. m_flNameChangeHistory[i] = m_flNameChangeHistory[i-1];
  6078. }
  6079. m_flNameChangeHistory[0] = gpGlobals->curtime; // last change
  6080. }
  6081. bool CCSPlayer::StartReplayMode( float fDelay, float fDuration, int iEntity )
  6082. {
  6083. if ( !BaseClass::StartReplayMode( fDelay, fDuration, iEntity ) )
  6084. return false;
  6085. CSingleUserRecipientFilter filter( this );
  6086. filter.MakeReliable();
  6087. UserMessageBegin( filter, "KillCam" );
  6088. WRITE_BYTE( OBS_MODE_IN_EYE );
  6089. if ( m_hObserverTarget.Get() )
  6090. {
  6091. WRITE_BYTE( m_hObserverTarget.Get()->entindex() ); // first target
  6092. WRITE_BYTE( entindex() ); //second target
  6093. }
  6094. else
  6095. {
  6096. WRITE_BYTE( entindex() ); // first target
  6097. WRITE_BYTE( 0 ); //second target
  6098. }
  6099. MessageEnd();
  6100. ClientPrint( this, HUD_PRINTCENTER, "Kill Cam Replay" );
  6101. return true;
  6102. }
  6103. void CCSPlayer::StopReplayMode()
  6104. {
  6105. BaseClass::StopReplayMode();
  6106. CSingleUserRecipientFilter filter( this );
  6107. filter.MakeReliable();
  6108. UserMessageBegin( filter, "KillCam" );
  6109. WRITE_BYTE( OBS_MODE_NONE );
  6110. WRITE_BYTE( 0 );
  6111. WRITE_BYTE( 0 );
  6112. MessageEnd();
  6113. }
  6114. void CCSPlayer::PlayUseDenySound()
  6115. {
  6116. // Don't do a sound here because it can mute your footsteps giving you an advantage.
  6117. // The CS:S content for this sound is silent anyways.
  6118. //EmitSound( "Player.UseDeny" );
  6119. }
  6120. //=============================================================================
  6121. // HPE_BEGIN:
  6122. //=============================================================================
  6123. // [menglish, tj] This is where we reset all the per-round information for achievements for this player
  6124. void CCSPlayer::ResetRoundBasedAchievementVariables()
  6125. {
  6126. m_KillingSpreeStartTime = -1;
  6127. int numCTPlayers = 0, numTPlayers = 0;
  6128. for (int i = 0; i < g_Teams.Count(); i++ )
  6129. {
  6130. if(g_Teams[i])
  6131. {
  6132. if ( g_Teams[i]->GetTeamNumber() == TEAM_CT )
  6133. numCTPlayers = g_Teams[i]->GetNumPlayers();
  6134. else if(g_Teams[i]->GetTeamNumber() == TEAM_TERRORIST)
  6135. numTPlayers = g_Teams[i]->GetNumPlayers();
  6136. }
  6137. }
  6138. m_NumEnemiesKilledThisRound = 0;
  6139. if(GetTeamNumber() == TEAM_CT)
  6140. m_NumEnemiesAtRoundStart = numTPlayers;
  6141. else if(GetTeamNumber() == TEAM_TERRORIST)
  6142. m_NumEnemiesAtRoundStart = numCTPlayers;
  6143. //Clear the previous owner field for currently held weapons
  6144. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
  6145. if ( pWeapon )
  6146. {
  6147. pWeapon->SetPreviousOwner(NULL);
  6148. }
  6149. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL));
  6150. if ( pWeapon )
  6151. {
  6152. pWeapon->SetPreviousOwner(NULL);
  6153. }
  6154. //Clear list of weapons used to get kills
  6155. m_killWeapons.RemoveAll();
  6156. //Clear sliding window of kill times
  6157. m_killTimes.RemoveAll();
  6158. //clear round kills
  6159. m_enemyPlayersKilledThisRound.RemoveAll();
  6160. m_killsWhileBlind = 0;
  6161. m_bSurvivedHeadshotDueToHelmet = false;
  6162. m_gooseChaseStep = GC_NONE;
  6163. m_defuseDefenseStep = DD_NONE;
  6164. m_pGooseChaseDistractingPlayer = NULL;
  6165. m_bMadeFootstepNoise = false;
  6166. m_bombPickupTime = -1;
  6167. m_bMadePurchseThisRound = false;
  6168. m_bKilledDefuser = false;
  6169. m_bKilledRescuer = false;
  6170. m_maxGrenadeKills = 0;
  6171. m_grenadeDamageTakenThisRound = 0;
  6172. //=============================================================================
  6173. // HPE_BEGIN:
  6174. // [dwenger] Needed for fun-fact implementation
  6175. //=============================================================================
  6176. WieldingKnifeAndKilledByGun(false);
  6177. m_WeaponTypesUsed.RemoveAll();
  6178. m_bPickedUpDefuser = false;
  6179. m_bDefusedWithPickedUpKit = false;
  6180. //=============================================================================
  6181. // HPE_END
  6182. //=============================================================================
  6183. }
  6184. /**
  6185. * static public CCSPlayer::GetCSWeaponIDCausingDamage()
  6186. *
  6187. * Helper function to get the ID of the weapon used to kill a player.
  6188. * This is slightly non-trivial because the grenade because a separate
  6189. * entity when thrown.
  6190. *
  6191. * Parameters:
  6192. * info -
  6193. *
  6194. * Returns:
  6195. * int -
  6196. */
  6197. CSWeaponID CCSPlayer::GetWeaponIdCausingDamange( const CTakeDamageInfo &info )
  6198. {
  6199. CBaseEntity *pInflictor = info.GetInflictor();
  6200. CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
  6201. if (pAttacker == pInflictor)
  6202. {
  6203. CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon());
  6204. if (!pAttackerWeapon)
  6205. return WEAPON_NONE;
  6206. return pAttackerWeapon->GetWeaponID();
  6207. }
  6208. else if (pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0)
  6209. {
  6210. return WEAPON_HEGRENADE;
  6211. }
  6212. return WEAPON_NONE;
  6213. }
  6214. //=============================================================================
  6215. // HPE_BEGIN:
  6216. // [dwenger] adding tracking for weapon used fun fact
  6217. //=============================================================================
  6218. void CCSPlayer::PlayerUsedFirearm( CBaseCombatWeapon* pBaseWeapon )
  6219. {
  6220. if ( pBaseWeapon )
  6221. {
  6222. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  6223. if ( pWeapon )
  6224. {
  6225. CSWeaponType weaponType = pWeapon->GetCSWpnData().m_WeaponType;
  6226. CSWeaponID weaponID = pWeapon->GetWeaponID();
  6227. if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
  6228. {
  6229. if ( m_WeaponTypesUsed.Find( weaponID ) == -1 )
  6230. {
  6231. // Add this weapon to the list of weapons used by the player
  6232. m_WeaponTypesUsed.AddToTail( weaponID );
  6233. }
  6234. }
  6235. }
  6236. }
  6237. }
  6238. /**
  6239. * public CCSPlayer::ProcessPlayerDeathAchievements()
  6240. *
  6241. * Do Achievement processing whenever a player is killed
  6242. *
  6243. * Parameters:
  6244. * pAttacker -
  6245. * pVictim -
  6246. * info -
  6247. */
  6248. void CCSPlayer::ProcessPlayerDeathAchievements( CCSPlayer *pAttacker, CCSPlayer *pVictim, const CTakeDamageInfo &info )
  6249. {
  6250. Assert(pVictim != NULL);
  6251. CBaseEntity *pInflictor = info.GetInflictor();
  6252. // all these achievements require a valid attacker on a different team
  6253. if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() )
  6254. {
  6255. // get the weapon used - some of the achievements will need this data
  6256. CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon());
  6257. //=============================================================================
  6258. // HPE_BEGIN:
  6259. // [dwenger] Fun-fact processing
  6260. //=============================================================================
  6261. CWeaponCSBase* pVictimWeapon = dynamic_cast< CWeaponCSBase* >(pVictim->GetActiveWeapon());
  6262. //=============================================================================
  6263. // HPE_END
  6264. //=============================================================================
  6265. CSWeaponID attackerWeaponId = GetWeaponIdCausingDamange(info);
  6266. if (pVictim->m_bIsDefusing)
  6267. {
  6268. pAttacker->AwardAchievement(CSKilledDefuser);
  6269. pAttacker->m_bKilledDefuser = true;
  6270. if (attackerWeaponId == WEAPON_HEGRENADE)
  6271. {
  6272. pAttacker->AwardAchievement(CSKilledDefuserWithGrenade);
  6273. }
  6274. }
  6275. // [pfreese] Achievement check for attacker killing player while reloading
  6276. if (pVictim->IsReloading())
  6277. {
  6278. pAttacker->AwardAchievement(CSKillEnemyReloading);
  6279. }
  6280. if (pVictim->IsRescuing())
  6281. {
  6282. // Ensure the killer did not injure any hostages
  6283. if ( !pAttacker->InjuredAHostage() && pVictim->GetNumFollowers() == g_Hostages.Count() )
  6284. {
  6285. pAttacker->AwardAchievement(CSKilledRescuer);
  6286. pAttacker->m_bKilledRescuer = true;
  6287. }
  6288. }
  6289. // [menglish] Achievement check for doing 95% or more damage to a player and having another player kill them
  6290. FOR_EACH_LL( pVictim->m_DamageTakenList, i )
  6291. {
  6292. if( pVictim->m_DamageTakenList[i]->GetDamage() >= pVictim->GetMaxHealth() - AchievementConsts::DamageNoKill_MaxHealthLeftOnKill &&
  6293. Q_strncmp( pAttacker->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) != 0 )
  6294. {
  6295. //Now find the player who did that amount of damage
  6296. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  6297. {
  6298. CBasePlayer *pPlayerIter = UTIL_PlayerByIndex( j );
  6299. if ( pPlayerIter && Q_strncmp( pPlayerIter->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 &&
  6300. pPlayerIter->GetTeamNumber() != pVictim->GetTeamNumber() )
  6301. {
  6302. ToCSPlayer(pPlayerIter)->AwardAchievement(CSDamageNoKill);
  6303. break;
  6304. }
  6305. }
  6306. }
  6307. }
  6308. pAttacker->m_NumEnemiesKilledThisRound++;
  6309. //store a list of kill times for spree tracking
  6310. pAttacker->m_killTimes.AddToTail(gpGlobals->curtime);
  6311. //Add the victim to the list of players killed this round
  6312. pAttacker->m_enemyPlayersKilledThisRound.AddToTail(pVictim);
  6313. //Calculate Avenging for all players the victim has killed
  6314. for ( int avengedIndex = 0; avengedIndex < pVictim->m_enemyPlayersKilledThisRound.Count(); avengedIndex++ )
  6315. {
  6316. CCSPlayer* avengedPlayer = pVictim->m_enemyPlayersKilledThisRound[avengedIndex];
  6317. if (avengedPlayer)
  6318. {
  6319. //Make sure you are avenging someone on your own team (This is the expected flow. Just here to avoid edge cases like team-switching).
  6320. if (pAttacker->GetTeamNumber() == avengedPlayer->GetTeamNumber())
  6321. {
  6322. CCS_GameStats.Event_PlayerAvengedTeammate(pAttacker, pVictim->m_enemyPlayersKilledThisRound[avengedIndex]);
  6323. }
  6324. }
  6325. }
  6326. //remove elements older than a certain time
  6327. while (pAttacker->m_killTimes.Count() > 0 && pAttacker->m_killTimes[0] + AchievementConsts::KillingSpree_WindowTime < gpGlobals->curtime)
  6328. {
  6329. pAttacker->m_killTimes.Remove(0);
  6330. }
  6331. //If we killed enough players in the time window, award the achievement
  6332. if (pAttacker->m_killTimes.Count() >= AchievementConsts::KillingSpree_Kills)
  6333. {
  6334. pAttacker->m_KillingSpreeStartTime = gpGlobals->curtime;
  6335. pAttacker->AwardAchievement(CSKillingSpree);
  6336. }
  6337. // Did the attacker just kill someone on a killing spree?
  6338. if (pVictim->m_KillingSpreeStartTime >= 0 && pVictim->m_KillingSpreeStartTime - gpGlobals->curtime <= AchievementConsts::KillingSpreeEnder_TimeWindow)
  6339. {
  6340. pAttacker->AwardAchievement(CSKillingSpreeEnder);
  6341. }
  6342. //Check the "killed someone with their own weapon" achievement
  6343. if (pAttackerWeapon && pAttackerWeapon->GetPreviousOwner() == pVictim)
  6344. {
  6345. pAttacker->AwardAchievement(CSKillEnemyWithFormerGun);
  6346. }
  6347. //If this player has killed the entire team award him the achievement
  6348. if (pAttacker->m_NumEnemiesKilledThisRound == pAttacker->m_NumEnemiesAtRoundStart && pAttacker->m_NumEnemiesKilledThisRound >= AchievementConsts::KillEnemyTeam_MinKills)
  6349. {
  6350. pAttacker->AwardAchievement(CSKillEnemyTeam);
  6351. }
  6352. //If this is a posthumous kill award the achievement
  6353. if (!pAttacker->IsAlive() && attackerWeaponId == WEAPON_HEGRENADE)
  6354. {
  6355. CCS_GameStats.IncrementStat(pAttacker, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1);
  6356. ToCSPlayer(pAttacker)->AwardAchievement(CSPosthumousGrenadeKill);
  6357. }
  6358. if (pAttacker->GetActiveWeapon() && pAttacker->GetActiveWeapon()->Clip1() == 0 && pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE)
  6359. {
  6360. if (pInflictor == pAttacker)
  6361. {
  6362. pAttacker->AwardAchievement(CSKillEnemyLastBullet);
  6363. CCS_GameStats.IncrementStat(pAttacker, CSSTAT_KILLS_WITH_LAST_ROUND, 1);
  6364. }
  6365. }
  6366. //=============================================================================
  6367. // HPE_BEGIN:
  6368. // [dwenger] Fun-fact processing
  6369. //=============================================================================
  6370. if (pVictimWeapon && pVictimWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE && pAttackerWeapon &&
  6371. pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_KNIFE && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_C4 && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE)
  6372. {
  6373. // Victim was wielding knife when killed by a gun
  6374. pVictim->WieldingKnifeAndKilledByGun(true);
  6375. }
  6376. //=============================================================================
  6377. // HPE_END
  6378. //=============================================================================
  6379. //see if this is a unique weapon
  6380. if (attackerWeaponId != WEAPON_NONE)
  6381. {
  6382. if (pAttacker->m_killWeapons.Find(attackerWeaponId) == -1)
  6383. {
  6384. pAttacker->m_killWeapons.AddToTail(attackerWeaponId);
  6385. if (pAttacker->m_killWeapons.Count() >= AchievementConsts::KillsWithMultipleGuns_MinWeapons)
  6386. {
  6387. pAttacker->AwardAchievement(CSKillsWithMultipleGuns);
  6388. }
  6389. }
  6390. }
  6391. //Check for kills while blind
  6392. if (pAttacker->IsBlindForAchievement())
  6393. {
  6394. //if this is from a different blinding, restart the kill counter and set the time
  6395. if (pAttacker->m_blindStartTime != pAttacker->m_firstKillBlindStartTime)
  6396. {
  6397. pAttacker->m_killsWhileBlind = 0;
  6398. pAttacker->m_firstKillBlindStartTime = pAttacker->m_blindStartTime;
  6399. }
  6400. ++pAttacker->m_killsWhileBlind;
  6401. if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlind_Kills)
  6402. {
  6403. pAttacker->AwardAchievement(CSKillEnemiesWhileBlind);
  6404. }
  6405. if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlindHard_Kills)
  6406. {
  6407. pAttacker->AwardAchievement(CSKillEnemiesWhileBlindHard);
  6408. }
  6409. }
  6410. //Check sniper killing achievements
  6411. bool victimZoomed = ( pVictim->GetFOV() != pVictim->GetDefaultFOV() );
  6412. bool attackerZoomed = ( pAttacker->GetFOV() != pAttacker->GetDefaultFOV() );
  6413. bool attackerUsedSniperRifle = pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE && pInflictor == pAttacker;
  6414. if (victimZoomed && attackerUsedSniperRifle)
  6415. {
  6416. pAttacker->AwardAchievement(CSKillSniperWithSniper);
  6417. }
  6418. if (attackerWeaponId == WEAPON_KNIFE && victimZoomed)
  6419. {
  6420. pAttacker->AwardAchievement(CSKillSniperWithKnife);
  6421. }
  6422. if (attackerUsedSniperRifle && !attackerZoomed)
  6423. {
  6424. pAttacker->AwardAchievement(CSHipShot);
  6425. }
  6426. //Kill a player at low health
  6427. if (pAttacker->IsAlive() && pAttacker->GetHealth() <= AchievementConsts::KillWhenAtLowHealth_MaxHealth)
  6428. {
  6429. pAttacker->AwardAchievement(CSKillWhenAtLowHealth);
  6430. }
  6431. //Kill a player with a knife during the pistol round
  6432. if (CSGameRules()->IsPistolRound())
  6433. {
  6434. if (attackerWeaponId == WEAPON_KNIFE)
  6435. {
  6436. pAttacker->AwardAchievement(CSPistolRoundKnifeKill);
  6437. }
  6438. }
  6439. //[tj] Check for dual elites fight
  6440. CWeaponCSBase* victimWeapon = pVictim->GetActiveCSWeapon();
  6441. if (victimWeapon)
  6442. {
  6443. CSWeaponID victimWeaponID = victimWeapon->GetWeaponID();
  6444. if (attackerWeaponId == WEAPON_ELITE && victimWeaponID == WEAPON_ELITE)
  6445. {
  6446. pAttacker->AwardAchievement(CSWinDualDuel);
  6447. }
  6448. }
  6449. //[tj] See if the attacker or defender are in the air [sbodenbender] dont include ladders
  6450. bool attackerInAir = pAttacker->GetMoveType() != MOVETYPE_LADDER && pAttacker->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL;
  6451. bool victimInAir = pVictim->GetMoveType() != MOVETYPE_LADDER && pVictim->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL;
  6452. if (attackerInAir)
  6453. {
  6454. pAttacker->AwardAchievement(CSKillWhileInAir);
  6455. }
  6456. if (victimInAir)
  6457. {
  6458. pAttacker->AwardAchievement(CSKillEnemyInAir);
  6459. }
  6460. if (attackerInAir && victimInAir)
  6461. {
  6462. pAttacker->AwardAchievement(CSKillerAndEnemyInAir);
  6463. }
  6464. //[tj] advance to the next stage of the defuse defense achievement
  6465. if (pAttacker->m_defuseDefenseStep == DD_STARTED_DEFUSE)
  6466. {
  6467. pAttacker->m_defuseDefenseStep = DD_KILLED_TERRORIST;
  6468. }
  6469. if (pVictim->HasC4() && pVictim->GetBombPickuptime() + AchievementConsts::KillBombPickup_MaxTime > gpGlobals->curtime)
  6470. {
  6471. pAttacker->AwardAchievement(CSKillBombPickup);
  6472. }
  6473. }
  6474. //If you kill a friendly player while blind (from an enemy player), give the guy that blinded you an achievement
  6475. if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() == pAttacker->GetTeamNumber() && pAttacker->IsBlind())
  6476. {
  6477. CCSPlayer* flashbangAttacker = pAttacker->GetLastFlashbangAttacker();
  6478. if (flashbangAttacker && pAttacker->GetTeamNumber() != flashbangAttacker->GetTeamNumber())
  6479. {
  6480. flashbangAttacker->AwardAchievement(CSCauseFriendlyFireWithFlashbang);
  6481. }
  6482. }
  6483. // do a scan to determine count of players still alive
  6484. int livePlayerCount = 0;
  6485. int teamCount[TEAM_MAXCOUNT];
  6486. int teamIgnoreCount[TEAM_MAXCOUNT];
  6487. memset(teamCount, 0, sizeof(teamCount));
  6488. memset(teamIgnoreCount, 0, sizeof(teamIgnoreCount));
  6489. CCSPlayer *pAlivePlayer = NULL;
  6490. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6491. {
  6492. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  6493. if (pPlayer)
  6494. {
  6495. int teamNum = pPlayer->GetTeamNumber();
  6496. if ( teamNum >= 0 )
  6497. {
  6498. ++teamCount[teamNum];
  6499. if (pPlayer->WasNotKilledNaturally())
  6500. {
  6501. teamIgnoreCount[teamNum]++;
  6502. }
  6503. }
  6504. if (pPlayer->IsAlive() && pPlayer != pVictim)
  6505. {
  6506. ++livePlayerCount;
  6507. pAlivePlayer = pPlayer;
  6508. }
  6509. }
  6510. }
  6511. // Achievement check for being the last player alive in a match
  6512. if (pAlivePlayer)
  6513. {
  6514. int alivePlayerTeam = pAlivePlayer->GetTeamNumber();
  6515. int alivePlayerOpposingTeam = alivePlayerTeam == TEAM_CT ? TEAM_TERRORIST : TEAM_CT;
  6516. if (livePlayerCount == 1
  6517. && CSGameRules()->m_iRoundWinStatus == WINNER_NONE
  6518. && teamCount[alivePlayerTeam] - teamIgnoreCount[alivePlayerTeam] >= AchievementConsts::LastPlayerAlive_MinPlayersOnTeam
  6519. && teamCount[alivePlayerOpposingTeam] - teamIgnoreCount[alivePlayerOpposingTeam] >= AchievementConsts::DefaultMinOpponentsForAchievement
  6520. && ( !(pAlivePlayer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) ))
  6521. {
  6522. pAlivePlayer->AwardAchievement(CSLastPlayerAlive);
  6523. }
  6524. }
  6525. // [tj] Added hook into player killed stat that happens before weapon drop
  6526. CCS_GameStats.Event_PlayerKilled_PreWeaponDrop(pVictim, info);
  6527. }
  6528. //[tj] traces up to maxTrace units down and returns any standable object it hits
  6529. // (doesn't check slope for standability)
  6530. CBaseEntity* CCSPlayer::GetNearestSurfaceBelow(float maxTrace)
  6531. {
  6532. trace_t trace;
  6533. Ray_t ray;
  6534. Vector traceStart = this->GetAbsOrigin();
  6535. Vector traceEnd = traceStart;
  6536. traceEnd.z -= maxTrace;
  6537. Vector minExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN_SCALED( this ) : VEC_HULL_MIN_SCALED( this );
  6538. Vector maxExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX_SCALED( this ) : VEC_HULL_MAX_SCALED( this );
  6539. ray.Init( traceStart, traceEnd, minExtent, maxExtent );
  6540. UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  6541. return trace.m_pEnt;
  6542. }
  6543. // [tj] Added a way to react to the round ending before we reset.
  6544. // It is important to note that this happens before the bomb explodes, so a player may die
  6545. // after this from a bomb explosion or a late kill after a defuse/detonation/rescue.
  6546. void CCSPlayer::OnRoundEnd(int winningTeam, int reason)
  6547. {
  6548. if (winningTeam == WINNER_CT || winningTeam == WINNER_TER)
  6549. {
  6550. int losingTeamId = (winningTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
  6551. CTeam* losingTeam = GetGlobalTeam(losingTeamId);
  6552. int losingTeamPlayers = 0;
  6553. if (losingTeam)
  6554. {
  6555. losingTeamPlayers = losingTeam->GetNumPlayers();
  6556. int ignoreCount = 0;
  6557. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6558. {
  6559. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  6560. if (pPlayer)
  6561. {
  6562. int teamNum = pPlayer->GetTeamNumber();
  6563. if ( teamNum == losingTeamId )
  6564. {
  6565. if (pPlayer->WasNotKilledNaturally())
  6566. {
  6567. ignoreCount++;
  6568. }
  6569. }
  6570. }
  6571. }
  6572. losingTeamPlayers -= ignoreCount;
  6573. }
  6574. //Check fast round win achievement
  6575. if ( IsAlive() &&
  6576. gpGlobals->curtime - CSGameRules()->GetRoundStartTime() < AchievementConsts::FastRoundWin_Time &&
  6577. GetTeamNumber() == winningTeam &&
  6578. losingTeamPlayers >= AchievementConsts::DefaultMinOpponentsForAchievement)
  6579. {
  6580. AwardAchievement(CSFastRoundWin);
  6581. }
  6582. //Check goosechase achievement
  6583. if (IsAlive() && reason == Target_Bombed && m_gooseChaseStep == GC_STOPPED_AFTER_GETTING_SHOT && m_pGooseChaseDistractingPlayer)
  6584. {
  6585. m_pGooseChaseDistractingPlayer->AwardAchievement(CSGooseChase);
  6586. }
  6587. //Check Defuse Defense achievement
  6588. if (IsAlive() && reason == Bomb_Defused && m_defuseDefenseStep == DD_KILLED_TERRORIST)
  6589. {
  6590. AwardAchievement(CSDefuseDefense);
  6591. }
  6592. //Check silent win
  6593. if (m_NumEnemiesKilledThisRound > 0 && GetTeamNumber() == winningTeam && !m_bMadeFootstepNoise)
  6594. {
  6595. AwardAchievement(CSSilentWin);
  6596. }
  6597. //Process && Check "win rounds without buying" achievement
  6598. if (GetTeamNumber() == winningTeam && !m_bMadePurchseThisRound)
  6599. {
  6600. m_roundsWonWithoutPurchase++;
  6601. if (m_roundsWonWithoutPurchase > AchievementConsts::WinRoundsWithoutBuying_Rounds)
  6602. {
  6603. AwardAchievement(CSWinRoundsWithoutBuying);
  6604. }
  6605. }
  6606. else
  6607. {
  6608. m_roundsWonWithoutPurchase = 0;
  6609. }
  6610. }
  6611. m_lastRoundResult = reason;
  6612. }
  6613. void CCSPlayer::OnPreResetRound()
  6614. {
  6615. //Check headshot survival achievement
  6616. if (IsAlive() && m_bSurvivedHeadshotDueToHelmet)
  6617. {
  6618. AwardAchievement(CSSurvivedHeadshotDueToHelmet);
  6619. }
  6620. if (IsAlive() && m_grenadeDamageTakenThisRound > AchievementConsts::SurviveGrenade_MinDamage)
  6621. {
  6622. AwardAchievement(CSSurviveGrenade);
  6623. }
  6624. //Check achievement for surviving attacks from multiple players.
  6625. if (IsAlive())
  6626. {
  6627. int numberOfEnemyDamagers = GetNumEnemyDamagers();
  6628. if (numberOfEnemyDamagers >= AchievementConsts::SurviveManyAttacks_NumberDamagingPlayers)
  6629. {
  6630. AwardAchievement(CSSurviveManyAttacks);
  6631. }
  6632. }
  6633. }
  6634. void CCSPlayer::OnCanceledDefuse()
  6635. {
  6636. if (m_gooseChaseStep == GC_SHOT_DURING_DEFUSE)
  6637. {
  6638. m_gooseChaseStep = GC_STOPPED_AFTER_GETTING_SHOT;
  6639. }
  6640. }
  6641. void CCSPlayer::OnStartedDefuse()
  6642. {
  6643. if (m_defuseDefenseStep == DD_NONE)
  6644. {
  6645. m_defuseDefenseStep = DD_STARTED_DEFUSE;
  6646. }
  6647. }
  6648. //-----------------------------------------------------------------------------
  6649. // Purpose:
  6650. //-----------------------------------------------------------------------------
  6651. void CCSPlayer::AttemptToExitFreezeCam( void )
  6652. {
  6653. float fEndFreezeTravel = m_flDeathTime + CS_DEATH_ANIMATION_TIME + spec_freeze_traveltime.GetFloat();
  6654. if ( gpGlobals->curtime < fEndFreezeTravel )
  6655. return;
  6656. m_bAbortFreezeCam = true;
  6657. }
  6658. //-----------------------------------------------------------------------------
  6659. // Purpose: Sets whether this player is dominating the specified other player
  6660. //-----------------------------------------------------------------------------
  6661. void CCSPlayer::SetPlayerDominated( CCSPlayer *pPlayer, bool bDominated )
  6662. {
  6663. int iPlayerIndex = pPlayer->entindex();
  6664. m_bPlayerDominated.Set( iPlayerIndex, bDominated );
  6665. pPlayer->SetPlayerDominatingMe( this, bDominated );
  6666. }
  6667. //-----------------------------------------------------------------------------
  6668. // Purpose: Sets whether this player is being dominated by the other player
  6669. //-----------------------------------------------------------------------------
  6670. void CCSPlayer::SetPlayerDominatingMe( CCSPlayer *pPlayer, bool bDominated )
  6671. {
  6672. int iPlayerIndex = pPlayer->entindex();
  6673. m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
  6674. }
  6675. //-----------------------------------------------------------------------------
  6676. // Purpose: Returns whether this player is dominating the specified other player
  6677. //-----------------------------------------------------------------------------
  6678. bool CCSPlayer::IsPlayerDominated( int iPlayerIndex )
  6679. {
  6680. return m_bPlayerDominated.Get( iPlayerIndex );
  6681. }
  6682. bool CCSPlayer::IsPlayerDominatingMe( int iPlayerIndex )
  6683. {
  6684. return m_bPlayerDominatingMe.Get( iPlayerIndex );
  6685. }
  6686. //=============================================================================
  6687. // HPE_BEGIN:
  6688. // [menglish] MVP functions
  6689. //=============================================================================
  6690. void CCSPlayer::IncrementNumMVPs( CSMvpReason_t mvpReason )
  6691. {
  6692. //=============================================================================
  6693. // HPE_BEGIN:
  6694. // [Forrest] Allow MVP to be turned off for a server
  6695. //=============================================================================
  6696. if ( sv_nomvp.GetBool() )
  6697. {
  6698. Msg( "Round MVP disabled: sv_nomvp is set.\n" );
  6699. return;
  6700. }
  6701. //=============================================================================
  6702. // HPE_END
  6703. //=============================================================================
  6704. m_iMVPs++;
  6705. CCS_GameStats.Event_MVPEarned( this );
  6706. IGameEvent *mvpEvent = gameeventmanager->CreateEvent( "round_mvp" );
  6707. if ( mvpEvent )
  6708. {
  6709. mvpEvent->SetInt( "userid", GetUserID() );
  6710. mvpEvent->SetInt( "reason", mvpReason );
  6711. gameeventmanager->FireEvent( mvpEvent );
  6712. }
  6713. }
  6714. //-----------------------------------------------------------------------------
  6715. // Purpose: Sets the number of rounds this player has caused to be won for their team
  6716. //-----------------------------------------------------------------------------
  6717. void CCSPlayer::SetNumMVPs( int iNumMVP )
  6718. {
  6719. m_iMVPs = iNumMVP;
  6720. }
  6721. //-----------------------------------------------------------------------------
  6722. // Purpose: Returns the number of rounds this player has caused to be won for their team
  6723. //-----------------------------------------------------------------------------
  6724. int CCSPlayer::GetNumMVPs()
  6725. {
  6726. return m_iMVPs;
  6727. }
  6728. //=============================================================================
  6729. // HPE_END
  6730. //=============================================================================
  6731. //-----------------------------------------------------------------------------
  6732. // Purpose: Removes all nemesis relationships between this player and others
  6733. //-----------------------------------------------------------------------------
  6734. void CCSPlayer::RemoveNemesisRelationships()
  6735. {
  6736. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  6737. {
  6738. CCSPlayer *pTemp = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  6739. if ( pTemp && pTemp != this )
  6740. {
  6741. // set this player to be not dominating anyone else
  6742. SetPlayerDominated( pTemp, false );
  6743. // set no one else to be dominating this player
  6744. pTemp->SetPlayerDominated( this, false );
  6745. }
  6746. }
  6747. }
  6748. void CCSPlayer::CheckMaxGrenadeKills(int grenadeKills)
  6749. {
  6750. if (grenadeKills > m_maxGrenadeKills)
  6751. {
  6752. m_maxGrenadeKills = grenadeKills;
  6753. }
  6754. }
  6755. void CCSPlayer::CommitSuicide( bool bExplode /*= false*/, bool bForce /*= false*/ )
  6756. {
  6757. m_wasNotKilledNaturally = true;
  6758. BaseClass::CommitSuicide(bExplode, bForce);
  6759. }
  6760. void CCSPlayer::CommitSuicide( const Vector &vecForce, bool bExplode /*= false*/, bool bForce /*= false*/ )
  6761. {
  6762. m_wasNotKilledNaturally = true;
  6763. BaseClass::CommitSuicide(vecForce, bExplode, bForce);
  6764. }
  6765. int CCSPlayer::GetNumEnemyDamagers()
  6766. {
  6767. int numberOfEnemyDamagers = 0;
  6768. FOR_EACH_LL( m_DamageTakenList, i )
  6769. {
  6770. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  6771. {
  6772. CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );
  6773. if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 &&
  6774. pPlayer->GetTeamNumber() != GetTeamNumber() )
  6775. {
  6776. numberOfEnemyDamagers++;
  6777. }
  6778. }
  6779. }
  6780. return numberOfEnemyDamagers;
  6781. }
  6782. int CCSPlayer::GetNumEnemiesDamaged()
  6783. {
  6784. int numberOfEnemiesDamaged = 0;
  6785. FOR_EACH_LL( m_DamageGivenList, i )
  6786. {
  6787. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  6788. {
  6789. CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );
  6790. if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 &&
  6791. pPlayer->GetTeamNumber() != GetTeamNumber() )
  6792. {
  6793. numberOfEnemiesDamaged++;
  6794. }
  6795. }
  6796. }
  6797. return numberOfEnemiesDamaged;
  6798. }
  6799. //=============================================================================
  6800. // HPE_END
  6801. //=============================================================================
  6802. void UTIL_AwardMoneyToTeam( int iAmount, int iTeam, CBaseEntity *pIgnore )
  6803. {
  6804. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6805. {
  6806. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  6807. if ( !pPlayer )
  6808. continue;
  6809. if ( pPlayer->GetTeamNumber() != iTeam )
  6810. continue;
  6811. if ( pPlayer == pIgnore )
  6812. continue;
  6813. pPlayer->AddAccount( iAmount );
  6814. }
  6815. }