Counter Strike : Global Offensive Source Code
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.

16549 lines
479 KiB

  1. //========= Copyright � 1996-2005, 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 "items.h"
  26. #include "weapon_c4.h"
  27. #include "weapon_parse.h"
  28. #include "weapon_knife.h"
  29. #include "movehelper_server.h"
  30. #include "tier0/vprof.h"
  31. #include "te_effect_dispatch.h"
  32. #include "vphysics/player_controller.h"
  33. #include "weapon_hegrenade.h"
  34. #include "weapon_flashbang.h"
  35. #include "weapon_smokegrenade.h"
  36. #include "weapon_molotov.h"
  37. #include "weapon_decoy.h"
  38. #include "weapon_sensorgrenade.h"
  39. //#include "weapon_carriable_item.h"
  40. #include <keyvalues.h>
  41. #include "engine/IEngineSound.h"
  42. #include "bot.h"
  43. #include "studio.h"
  44. #include <coordsize.h>
  45. #include "predicted_viewmodel.h"
  46. #include "props_shared.h"
  47. #include "tier0/icommandline.h"
  48. #include "info_camera_link.h"
  49. #include "hintmessage.h"
  50. #include "obstacle_pushaway.h"
  51. #include "movevars_shared.h"
  52. #include "death_pose.h"
  53. #include "basecsgrenade_projectile.h"
  54. #include "hegrenade_projectile.h"
  55. #include "SoundEmitterSystem/isoundemittersystembase.h"
  56. #include "CRagdollMagnet.h"
  57. #include "datacache/imdlcache.h"
  58. #include "npcevent.h"
  59. #include "cs_gamestats.h"
  60. #include "GameStats.h"
  61. #include "cs_achievement_constants.h"
  62. #include "cs_simple_hostage.h"
  63. #include "cs_weapon_parse.h"
  64. #include "sendprop_priorities.h"
  65. #include "achievementmgr.h"
  66. #include "fmtstr.h"
  67. #include "gametypes/igametypes.h"
  68. #include "weapon_c4.h"
  69. #include "cs_shareddefs.h"
  70. #include "inputsystem/iinputsystem.h"
  71. #include "platforminputdevice.h"
  72. #include "Effects/inferno.h"
  73. #include "cs_entity_spotting.h"
  74. #include "entityutil.h"
  75. #include "vehicle_base.h"
  76. #include "cs_player_resource.h"
  77. #include "cs_entity_spotting.h"
  78. #include "particle_parse.h"
  79. #include "mapinfo.h"
  80. #include "cstrike15_item_system.h"
  81. //#include "particle_parse.h"
  82. #include "../public/vstdlib/vstrtools.h"
  83. #include "../public/vgui/ILocalize.h"
  84. #include "../shared/cstrike15/flashbang_projectile.h"
  85. #include "usermessages.h"
  86. #include "teamplayroundbased_gamerules.h"
  87. #include "animation.h"
  88. #include "cs_team.h"
  89. #include "econ_game_account_client.h"
  90. #include "world.h"
  91. #include "item_healthshot.h"
  92. #include "hltvdirector.h"
  93. #include "ihltv.h"
  94. #include "netmessages.pb.h"
  95. #include "econ_item_view_helpers.h"
  96. #include "playerdecals_signature.h"
  97. #if defined( CLIENT_DLL )
  98. #include "custom_material.h"
  99. #include "cs_custom_clothing_visualsdata_processor.h"
  100. #endif
  101. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  102. #include "econ_gcmessages.h"
  103. #include "econ_entity_creation.h"
  104. #endif
  105. // memdbgon must be the last include file in a .cpp file!!!
  106. #include "tier0/memdbgon.h"
  107. // HPE TEMP - REMOVE WHEN DONE TESTING
  108. #define REPORT_PLAYER_DAMAGE 0
  109. #pragma warning( disable : 4355 )
  110. // Minimum interval between rate-limited commands that players can run.
  111. #define CS_COMMAND_MAX_RATE 0.3
  112. #define WEARABLE_VEST_IFF_KEVLAR 0 // If set to 1, character wears vest if and only if they have game kevlar.
  113. const char *g_pszHeavyPhoenixModel = "models/player/custom_player/legacy/tm_phoenix_heavy.mdl";
  114. const float CycleLatchInterval = 0.2f;
  115. const int cGunGameSelectMaxAmmoAmount = 250;
  116. #define SPOTTED_ENTITY_UPDATE_INTERVAL 0.5f
  117. #define SPOTTED_ENTITY_COUNT_MESSAGE_MAX 40
  118. #define CS_PUSHAWAY_THINK_CONTEXT "CSPushawayThink"
  119. extern ConVar mp_maxrounds;
  120. ConVar cs_ShowStateTransitions( "cs_ShowStateTransitions", "-2", FCVAR_CHEAT, "cs_ShowStateTransitions <ent index or -1 for all>. Show player state transitions." );
  121. 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." );
  122. ConVar mp_logmoney( "mp_logmoney", "0", FCVAR_RELEASE, "Enables money logging. Values are: 0=off, 1=on", true, 0.0f, true, 1.0f );
  123. extern ConVar cs_AssistDamageThreshold;
  124. extern ConVar mp_spawnprotectiontime;
  125. extern ConVar mp_td_spawndmgthreshold;
  126. extern ConVar mp_td_dmgtowarn;
  127. extern ConVar mp_td_dmgtokick;
  128. extern ConVar sv_kick_ban_duration;
  129. extern ConVar dev_reportmoneychanges;
  130. extern ConVar mp_randomspawn;
  131. extern ConVar mp_dm_bonus_percent;
  132. extern ConVar mp_respawn_on_death_t;
  133. extern ConVar mp_respawn_on_death_ct;
  134. extern ConVar mp_ct_default_melee;
  135. extern ConVar mp_ct_default_secondary;
  136. extern ConVar mp_ct_default_primary;
  137. extern ConVar mp_ct_default_grenades;
  138. extern ConVar mp_t_default_melee;
  139. extern ConVar mp_t_default_secondary;
  140. extern ConVar mp_t_default_primary;
  141. extern ConVar mp_t_default_grenades;
  142. extern ConVar mp_damage_scale_ct_body;
  143. extern ConVar mp_damage_scale_ct_head;
  144. extern ConVar mp_damage_scale_t_body;
  145. extern ConVar mp_damage_scale_t_head;
  146. extern ConVar mp_randomspawn_los;
  147. extern ConVar mp_randomspawn_dist;
  148. extern ConVar mp_use_respawn_waves;
  149. extern ConVar cash_player_respawn_amount;
  150. extern ConVar cash_player_get_killed;
  151. extern ConVar mp_death_drop_c4;
  152. extern ConVar mp_buy_anywhere;
  153. extern ConVar sv_staminamax;
  154. extern ConVar sv_coaching_enabled;
  155. extern ConVar mp_coop_force_join_ct;
  156. extern ConVar mp_guardian_special_weapon_needed;
  157. extern ConVar mp_guardian_player_dist_min;
  158. extern ConVar mp_guardian_player_dist_max;
  159. extern ConVar mp_guardian_target_site;
  160. extern ConVar mp_player_healthbuffer_decay_rate;
  161. // friendly fire damage scalers
  162. extern ConVar ff_damage_reduction_grenade;
  163. extern ConVar ff_damage_reduction_grenade_self;
  164. extern ConVar ff_damage_reduction_bullets;
  165. extern ConVar ff_damage_reduction_other;
  166. ConVar phys_playerscale( "phys_playerscale", "10.0", FCVAR_REPLICATED, "This multiplies the bullet impact impuse on players for more dramatic results when players are shot." );
  167. ConVar phys_headshotscale( "phys_headshotscale", "1.3", FCVAR_REPLICATED, "Modifier for the headshot impulse hits on players" );
  168. ConVar sv_damage_print_enable( "sv_damage_print_enable", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Turn this off to disable the player's damage feed in the console after getting killed.");
  169. ConVar sv_spawn_afk_bomb_drop_time( "sv_spawn_afk_bomb_drop_time", "15", FCVAR_REPLICATED | FCVAR_RELEASE, "Players that have never moved since they spawned will drop the bomb after this amount of time." );
  170. extern ConVar spec_replay_winddown_time;
  171. ConVar mp_drop_knife_enable( "mp_drop_knife_enable", "0", FCVAR_RELEASE, "Allows players to drop knives." );
  172. static ConVar tv_relayradio( "tv_relayradio", "0", FCVAR_RELEASE, "Relay team radio commands to TV: 0=off, 1=on" );
  173. // [Jason] Allow us to turn down the frequency of the damage notification
  174. ConVar CS_WarnFriendlyDamageInterval( "CS_WarnFriendlyDamageInterval", "3.0", FCVAR_CHEAT, "Defines how frequently the server notifies clients that a player damaged a friend" );
  175. const char* g_pszLootModelName = "models/props/props_gameplay/money_bag.mdl";
  176. ConVar sv_guardian_min_wave_for_heavy( "sv_guardian_min_wave_for_heavy", "0", FCVAR_RELEASE | FCVAR_GAMEDLL );
  177. ConVar sv_guardian_max_wave_for_heavy( "sv_guardian_max_wave_for_heavy", "0", FCVAR_RELEASE | FCVAR_GAMEDLL );
  178. ConVar sv_guardian_heavy_count( "sv_guardian_heavy_count", "0", FCVAR_RELEASE | FCVAR_GAMEDLL );
  179. ConVar sv_guardian_heavy_all( "sv_guardian_heavy_all", "0", FCVAR_RELEASE | FCVAR_GAMEDLL );
  180. // [Forrest] Allow MVP to be turned off for a server
  181. // [Forrest] Allow freezecam to be turned off for a server
  182. // [Forrest] Allow win panel to be turned off for a server
  183. static void SvNoMVPChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
  184. {
  185. ConVarRef var( pConVar );
  186. if ( var.IsValid() && var.GetBool() )
  187. {
  188. // Clear the MVPs of all players when MVP is turned off.
  189. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  190. {
  191. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  192. if ( pPlayer )
  193. {
  194. pPlayer->SetNumMVPs( 0 );
  195. }
  196. }
  197. }
  198. }
  199. ConVar sv_nomvp( "sv_nomvp", "0", 0, "Disable MVP awards.", SvNoMVPChangeCallback );
  200. ConVar sv_disablefreezecam( "sv_disablefreezecam", "0", FCVAR_REPLICATED, "Turn on/off freezecam on server" );
  201. ConVar sv_nowinpanel( "sv_nowinpanel", "0", 0, "Turn on/off win panel on server" );
  202. ConVar sv_show_voip_indicator_for_enemies( "sv_show_voip_indicator_for_enemies", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Makes it so the voip icon is shown over enemies as well as allies when they are talking" );
  203. ConVar bot_mimic( "bot_mimic", "0", FCVAR_CHEAT );
  204. ConVar bot_freeze( "bot_freeze", "0", FCVAR_CHEAT );
  205. ConVar bot_crouch( "bot_crouch", "0", FCVAR_CHEAT );
  206. ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "180", FCVAR_CHEAT );
  207. ConVar bot_chatter_use_rr( "bot_chatter_use_rr", "1", FCVAR_DEVELOPMENTONLY, "0 = Use old bot chatter system, 1 = Use response rules" );
  208. // [jpaquin] allow buy zones to refill carried ammo automatically
  209. #define SECONDS_BETWEEN_AUTOAMMOBUY_CHECKS .5f
  210. ConVar sv_autobuyammo( "sv_autobuyammo", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Enable automatic ammo purchase when inside buy zones during buy periods" );
  211. ConVar gg_knife_kill_demotes( "gg_knife_kill_demotes", "1", FCVAR_REPLICATED, "0 = knife kill in gungame has no effect on player level, 1 = knife kill demotes player by one level" );
  212. // [mlowrance] allow adjustment of velocitymodifier based on dmg_burn (molotov )
  213. // ConVar sv_velocitymod_dmgtype_burn( "sv_velocitymod_dmgtype_burn", "0.7", FCVAR_REPLICATED, "Sets the players speed modifier when hit by Damagetype DMG_BURN" );
  214. // [jhail] adjusting the UI tint based on gameplay/team mode
  215. static const int g_CT_Tint = 1;
  216. static const int g_T_Tint = 2;
  217. static const int g_KnifeLevelTint = 4;
  218. extern ConVar mp_autokick;
  219. extern ConVar sv_turbophysics;
  220. extern ConVar spec_freeze_time;
  221. extern ConVar spec_freeze_time_lock;
  222. extern ConVar spec_freeze_traveltime;
  223. extern ConVar spec_freeze_deathanim_time;
  224. extern ConVar spec_allow_roaming;
  225. ConVar cs_hostage_near_rescue_music_distance( "cs_hostage_near_rescue_music_distance", "2000", FCVAR_CHEAT );
  226. // players don't need physics boxes in csgo, but maybe in mods?
  227. ConVar cs_enable_player_physics_box( "cs_enable_player_physics_box", "0", FCVAR_RELEASE );
  228. ConVar mp_deathcam_skippable( "mp_deathcam_skippable", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether a player can early-out of the deathcam." );
  229. #define THROWGRENADE_COUNTER_BITS 3
  230. EHANDLE g_pLastCTSpawn;
  231. EHANDLE g_pLastTerroristSpawn;
  232. void TE_RadioIcon( IRecipientFilter& filter, float delay, CBaseEntity *pPlayer );
  233. // -------------------------------------------------------------------------------- //
  234. // Classes
  235. // -------------------------------------------------------------------------------- //
  236. class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
  237. {
  238. public:
  239. int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
  240. {
  241. CCSPlayer *pPlayer = (CCSPlayer * )pObject->GetGameData();
  242. if ( pPlayer )
  243. {
  244. if ( pPlayer->TouchedPhysics() )
  245. {
  246. return 0;
  247. }
  248. }
  249. return 1;
  250. }
  251. };
  252. static CPhysicsPlayerCallback playerCallback;
  253. // -------------------------------------------------------------------------------- //
  254. // Ragdoll entities.
  255. // -------------------------------------------------------------------------------- //
  256. class CCSRagdoll : public CBaseAnimatingOverlay
  257. {
  258. public:
  259. DECLARE_CLASS( CCSRagdoll, CBaseAnimatingOverlay );
  260. DECLARE_SERVERCLASS();
  261. // Transmit ragdolls to everyone.
  262. virtual int UpdateTransmitState()
  263. {
  264. return SetTransmitState( FL_EDICT_ALWAYS );
  265. }
  266. void Init( void )
  267. {
  268. CBasePlayer *pPlayer = assert_cast< CBasePlayer* >( m_hPlayer.Get() );
  269. SetSolid( SOLID_BBOX );
  270. SetMoveType( MOVETYPE_STEP );
  271. SetFriction( 1.0f );
  272. SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
  273. m_takedamage = DAMAGE_NO;
  274. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  275. SetAbsAngles( QAngle( 0, m_flAbsYaw, 0 ) );
  276. SetAbsOrigin( pPlayer->GetAbsOrigin() );
  277. SetAbsVelocity( pPlayer->GetAbsVelocity() );
  278. AddSolidFlags( FSOLID_NOT_SOLID );
  279. ChangeTeam( pPlayer->GetTeamNumber() );
  280. UseClientSideAnimation();
  281. }
  282. public:
  283. // In case the client has the player entity, we transmit the player index.
  284. // In case the client doesn't have it, we transmit the player's model index, origin, and angles
  285. // so they can create a ragdoll in the right place.
  286. CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
  287. CNetworkVector( m_vecRagdollVelocity );
  288. CNetworkVector( m_vecRagdollOrigin );
  289. CNetworkVar(int, m_iDeathPose );
  290. CNetworkVar(int, m_iDeathFrame );
  291. CNetworkVar(float, m_flDeathYaw );
  292. CNetworkVar(float, m_flAbsYaw );
  293. };
  294. LINK_ENTITY_TO_CLASS( cs_ragdoll, CCSRagdoll );
  295. IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll )
  296. SendPropVector (SENDINFO(m_vecOrigin ), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  297. SendPropVector( SENDINFO(m_vecRagdollOrigin ), -1, SPROP_COORD ),
  298. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  299. SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
  300. SendPropInt ( SENDINFO(m_nForceBone ), 8, 0 ),
  301. SendPropVector ( SENDINFO( m_vecForce ) ),
  302. SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
  303. SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS, SPROP_UNSIGNED ),
  304. SendPropInt( SENDINFO( m_iDeathFrame ), 5 ),
  305. SendPropInt( SENDINFO(m_iTeamNum ), TEAMNUM_NUM_BITS, 0 ),
  306. SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
  307. SendPropFloat( SENDINFO( m_flDeathYaw ), 0, SPROP_NOSCALE ),
  308. SendPropFloat( SENDINFO( m_flAbsYaw ), 0, SPROP_NOSCALE )
  309. END_SEND_TABLE()
  310. // -------------------------------------------------------------------------------- //
  311. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  312. // -------------------------------------------------------------------------------- //
  313. class CTEPlayerAnimEvent : public CBaseTempEntity
  314. {
  315. public:
  316. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  317. DECLARE_SERVERCLASS();
  318. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  319. {
  320. }
  321. CNetworkHandle( CBasePlayer, m_hPlayer );
  322. CNetworkVar( int, m_iEvent );
  323. CNetworkVar( int, m_nData );
  324. };
  325. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  326. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  327. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  328. SendPropInt( SENDINFO( m_nData ), 32 )
  329. END_SEND_TABLE()
  330. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  331. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  332. {
  333. CPVSFilter filter( (const Vector& )pPlayer->EyePosition() );
  334. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  335. g_TEPlayerAnimEvent.m_iEvent = event;
  336. g_TEPlayerAnimEvent.m_nData = nData;
  337. g_TEPlayerAnimEvent.Create( filter, 0 );
  338. }
  339. // -------------------------------------------------------------------------------- //
  340. // Tables.
  341. // -------------------------------------------------------------------------------- //
  342. LINK_ENTITY_TO_CLASS( player, CCSPlayer );
  343. PRECACHE_REGISTER(player );
  344. BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSLocalPlayerExclusive )
  345. SendPropVectorXY(SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginXY, SENDPROP_LOCALPLAYER_ORIGINXY_PRIORITY ),
  346. SendPropFloat (SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginZ, SENDPROP_LOCALPLAYER_ORIGINZ_PRIORITY ),
  347. SendPropFloat( SENDINFO( m_flStamina ), 14, 0, 0, 100.0f ),
  348. SendPropInt( SENDINFO( m_iDirection ), 1, SPROP_UNSIGNED ),
  349. SendPropInt( SENDINFO( m_iShotsFired ), 8, SPROP_UNSIGNED ),
  350. SendPropInt( SENDINFO( m_nNumFastDucks ), 8, SPROP_UNSIGNED ), // unused
  351. SendPropBool( SENDINFO( m_bDuckOverride ) ),
  352. SendPropFloat( SENDINFO( m_flVelocityModifier ), 8, 0, 0, 1 ),
  353. // [tj]Set up the send table for per-client domination data
  354. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ),
  355. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ),
  356. SendPropArray3( SENDINFO_ARRAY3( m_iWeaponPurchasesThisRound ), SendPropInt( SENDINFO_ARRAY( m_iWeaponPurchasesThisRound ), 4, SPROP_UNSIGNED ) ),
  357. SendPropInt( SENDINFO( m_nQuestProgressReason ), QuestProgress::QuestReasonBits, SPROP_UNSIGNED ),
  358. END_SEND_TABLE()
  359. BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSNonLocalPlayerExclusive )
  360. SendPropVectorXY(SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginXY, SENDPROP_NONLOCALPLAYER_ORIGINXY_PRIORITY ),
  361. SendPropFloat (SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginZ, SENDPROP_NONLOCALPLAYER_ORIGINZ_PRIORITY ),
  362. END_SEND_TABLE()
  363. IMPLEMENT_SERVERCLASS_ST( CCSPlayer, DT_CSPlayer )
  364. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  365. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  366. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  367. SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
  368. SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
  369. SendPropExclude( "DT_BaseAnimating", "m_nMuzzleFlashParity" ),
  370. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  371. //SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  372. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  373. SendPropExclude( "DT_BaseEntity", "m_cellbits" ),
  374. SendPropExclude( "DT_BaseEntity", "m_cellX" ),
  375. SendPropExclude( "DT_BaseEntity", "m_cellY" ),
  376. SendPropExclude( "DT_BaseEntity", "m_cellZ" ),
  377. // cs_playeranimstate and clientside animation takes care of these on the client
  378. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  379. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  380. // Data that only gets sent to the local player.
  381. SendPropDataTable( "cslocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSLocalPlayerExclusive ), SendProxy_SendLocalDataTable ),
  382. SendPropDataTable( "csnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSNonLocalPlayerExclusive ), SendProxy_SendNonLocalDataTable ),
  383. SendPropAngle( SENDINFO_VECTORELEM( m_angEyeAngles, 0 ), -1, SPROP_NOSCALE | SPROP_CHANGES_OFTEN ),
  384. SendPropAngle( SENDINFO_VECTORELEM( m_angEyeAngles, 1 ), -1, SPROP_NOSCALE | SPROP_CHANGES_OFTEN ),
  385. SendPropInt( SENDINFO( m_iAddonBits ), NUM_ADDON_BITS, SPROP_UNSIGNED ),
  386. SendPropInt( SENDINFO( m_iPrimaryAddon ), 8, SPROP_UNSIGNED ),
  387. SendPropInt( SENDINFO( m_iSecondaryAddon ), 8, SPROP_UNSIGNED ),
  388. SendPropInt( SENDINFO( m_iThrowGrenadeCounter ), THROWGRENADE_COUNTER_BITS, SPROP_UNSIGNED ),
  389. SendPropBool( SENDINFO( m_bWaitForNoAttack ) ),
  390. SendPropBool( SENDINFO( m_bIsRespawningForDMBonus ) ),
  391. SendPropInt( SENDINFO( m_iPlayerState ), Q_log2( NUM_PLAYER_STATES )+1, SPROP_UNSIGNED ),
  392. SendPropInt( SENDINFO( m_iAccount ), 16, SPROP_UNSIGNED ),
  393. SendPropInt( SENDINFO( m_iStartAccount ), 16, SPROP_UNSIGNED ),
  394. SendPropInt( SENDINFO( m_totalHitsOnServer ), 8, SPROP_UNSIGNED ),
  395. SendPropInt( SENDINFO( m_bInBombZone ), 1, SPROP_UNSIGNED ),
  396. SendPropInt( SENDINFO( m_bInBuyZone ), 1, SPROP_UNSIGNED ),
  397. SendPropInt( SENDINFO( m_bInNoDefuseArea ), 1, SPROP_UNSIGNED ),
  398. SendPropBool( SENDINFO( m_bKilledByTaser ) ),
  399. SendPropInt( SENDINFO( m_iMoveState ), 0, SPROP_CHANGES_OFTEN ),
  400. SendPropInt( SENDINFO( m_iClass ), Q_log2( CS_MAX_PLAYER_MODELS )+1, SPROP_UNSIGNED ),
  401. SendPropInt( SENDINFO( m_ArmorValue ), 8, SPROP_UNSIGNED ),
  402. SendPropInt( SENDINFO( m_bHasDefuser ), 1, SPROP_UNSIGNED ),
  403. SendPropInt( SENDINFO( m_bNightVisionOn ), 1, SPROP_UNSIGNED ), //send as int so we can use a RecvProxy on the client
  404. SendPropBool( SENDINFO( m_bHasNightVision ) ),
  405. SendPropBool( SENDINFO( m_bInHostageRescueZone ) ),
  406. SendPropBool( SENDINFO( m_bIsDefusing ) ),
  407. SendPropBool( SENDINFO( m_bIsGrabbingHostage ) ),
  408. SendPropBool( SENDINFO( m_bIsScoped ) ),
  409. SendPropBool( SENDINFO( m_bIsWalking ) ),
  410. SendPropBool( SENDINFO( m_bResumeZoom ) ),
  411. SendPropFloat( SENDINFO( m_fImmuneToGunGameDamageTime ) ),
  412. SendPropBool( SENDINFO( m_bGunGameImmunity ) ),
  413. SendPropBool( SENDINFO( m_bHasMovedSinceSpawn ) ),
  414. SendPropBool( SENDINFO( m_bMadeFinalGunGameProgressiveKill ) ),
  415. SendPropInt( SENDINFO( m_iGunGameProgressiveWeaponIndex ), 32, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
  416. SendPropInt( SENDINFO( m_iNumGunGameTRKillPoints ) ),
  417. SendPropInt( SENDINFO( m_iNumGunGameKillsWithCurrentWeapon ) ),
  418. SendPropInt( SENDINFO( m_iNumRoundKills ) ),
  419. SendPropFloat( SENDINFO( m_fMolotovUseTime ) ),
  420. SendPropFloat( SENDINFO( m_fMolotovDamageTime ) ),
  421. SendPropString( SENDINFO( m_szArmsModel ) ),
  422. SendPropEHandle( SENDINFO( m_hCarriedHostage ) ),
  423. SendPropEHandle( SENDINFO( m_hCarriedHostageProp ) ),
  424. SendPropBool( SENDINFO( m_bIsRescuing ) ),
  425. SendPropFloat( SENDINFO( m_flGroundAccelLinearFracLastTime ), 0, SPROP_CHANGES_OFTEN ),
  426. SendPropFloat( SENDINFO( m_flGuardianTooFarDistFrac ) ),
  427. SendPropFloat( SENDINFO( m_flDetectedByEnemySensorTime ) ),
  428. SendPropBool( SENDINFO( m_bCanMoveDuringFreezePeriod ) ),
  429. SendPropBool( SENDINFO( m_isCurrentGunGameLeader ) ),
  430. SendPropBool( SENDINFO( m_isCurrentGunGameTeamLeader ) ),
  431. SendPropArray3( SENDINFO_ARRAY3(m_rank), SendPropInt( SENDINFO_ARRAY(m_rank), 0, SPROP_UNSIGNED ) ),
  432. SendPropInt( SENDINFO( m_unMusicID ), 16, SPROP_UNSIGNED ),
  433. #ifdef CS_SHIELD_ENABLED
  434. SendPropBool( SENDINFO( m_bHasShield ) ),
  435. SendPropBool( SENDINFO( m_bShieldDrawn ) ),
  436. #endif
  437. SendPropBool( SENDINFO( m_bHasHelmet ) ),
  438. SendPropBool( SENDINFO( m_bHasHeavyArmor ) ),
  439. SendPropFloat (SENDINFO(m_flFlashDuration ), 0, SPROP_NOSCALE ),
  440. SendPropFloat( SENDINFO(m_flFlashMaxAlpha ), 0, SPROP_NOSCALE ),
  441. SendPropInt( SENDINFO( m_iProgressBarDuration ), 4, SPROP_UNSIGNED ),
  442. SendPropFloat( SENDINFO( m_flProgressBarStartTime ), 0, SPROP_NOSCALE ),
  443. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  444. SendPropInt( SENDINFO( m_cycleLatch ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
  445. SendPropInt( SENDINFO( m_unCurrentEquipmentValue ), 16, SPROP_UNSIGNED ),
  446. SendPropInt( SENDINFO( m_unRoundStartEquipmentValue ), 16, SPROP_UNSIGNED ),
  447. SendPropInt( SENDINFO( m_unFreezetimeEndEquipmentValue ), 16, SPROP_UNSIGNED ),
  448. #if CS_CONTROLLABLE_BOTS_ENABLED
  449. SendPropBool( SENDINFO( m_bIsControllingBot ) ),
  450. SendPropBool( SENDINFO( m_bHasControlledBotThisRound ) ),
  451. SendPropBool( SENDINFO( m_bCanControlObservedBot ) ),
  452. SendPropInt( SENDINFO( m_iControlledBotEntIndex ) ),
  453. #endif
  454. // data used to show and hide hud via scripts in the training map
  455. SendPropBool( SENDINFO( m_bHud_MiniScoreHidden ) ),
  456. SendPropBool( SENDINFO( m_bHud_RadarHidden ) ),
  457. SendPropInt( SENDINFO( m_nLastKillerIndex ), 8, SPROP_UNSIGNED ),
  458. // when a player dies, we send to the client the number of unbroken times in a row the player has been killed by their last killer
  459. SendPropInt( SENDINFO( m_nLastConcurrentKilled ), 8, SPROP_UNSIGNED ),
  460. SendPropInt( SENDINFO( m_nDeathCamMusic ), 8, SPROP_UNSIGNED ),
  461. SendPropBool( SENDINFO( m_bIsLookingAtWeapon ) ),
  462. SendPropBool( SENDINFO( m_bIsHoldingLookAtWeapon ) ),
  463. SendPropInt( SENDINFO( m_iNumRoundKillsHeadshots ) ),
  464. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_Kills), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_Kills), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  465. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_Damage), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_Damage), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  466. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_EquipmentValue), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_EquipmentValue), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  467. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_MoneySaved), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_MoneySaved), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  468. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_KillReward), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_KillReward), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  469. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_LiveTime), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_LiveTime), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  470. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_Deaths), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_Deaths), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  471. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_Assists), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_Assists), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  472. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_HeadShotKills), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_HeadShotKills), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  473. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_Objective), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_Objective), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  474. SendPropArray3( SENDINFO_ARRAY3(m_iMatchStats_CashEarned), SendPropInt( SENDINFO_ARRAY(m_iMatchStats_CashEarned), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  475. SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_UtilityDamage ), SendPropInt( SENDINFO_ARRAY( m_iMatchStats_UtilityDamage ), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  476. SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_EnemiesFlashed ), SendPropInt( SENDINFO_ARRAY( m_iMatchStats_EnemiesFlashed ), 0, SPROP_UNSIGNED, 0, SENDPROP_MATCHSTATS_PRIORITY ) ),
  477. #if defined( PLAYER_TAUNT_SHIPPING_FEATURE )
  478. SendPropBool( SENDINFO( m_bIsTaunting ) ),
  479. SendPropBool( SENDINFO( m_bIsThirdPersonTaunt ) ),
  480. SendPropBool( SENDINFO( m_bIsHoldingTaunt ) ),
  481. SendPropFloat( SENDINFO( m_flTauntYaw ), 0, SPROP_NOSCALE ),
  482. #endif
  483. #if defined( USE_PLAYER_ATTRIBUTE_MANAGER )
  484. SendPropDataTable( SENDINFO_DT( m_AttributeManager ), &REFERENCE_SEND_TABLE(DT_AttributeManager) ),
  485. #endif
  486. SendPropFloat( SENDINFO( m_flLowerBodyYawTarget ), 8, SPROP_NOSCALE ),
  487. SendPropBool( SENDINFO( m_bStrafing ) ),
  488. SendPropFloat( SENDINFO( m_flThirdpersonRecoil ), 8, SPROP_NOSCALE ),
  489. END_SEND_TABLE()
  490. BEGIN_DATADESC( CCSPlayer )
  491. DEFINE_INPUTFUNC( FIELD_VOID, "OnRescueZoneTouch", RescueZoneTouch ),
  492. DEFINE_THINKFUNC( PushawayThink )
  493. END_DATADESC()
  494. // has to be included after above macros
  495. #include "cs_bot.h"
  496. // memdbgon must be the last include file in a .cpp file!!!
  497. #include "tier0/memdbgon.h"
  498. // -------------------------------------------------------------------------------- //
  499. void cc_CreatePredictionError_f( const CCommand &args )
  500. {
  501. float distance = 32;
  502. if ( args.ArgC() >= 2 )
  503. {
  504. distance = atof(args[1] );
  505. }
  506. CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
  507. pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( distance, 0, 0 ) );
  508. }
  509. ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
  510. // -------------------------------------------------------------------------------- //
  511. // CCSPlayer implementation.
  512. // -------------------------------------------------------------------------------- //
  513. CCSPlayer::CCSPlayer()
  514. {
  515. m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
  516. m_PlayerAnimStateCSGO = CreateCSGOPlayerAnimstate( this );
  517. UseClientSideAnimation();
  518. m_numRoundsSurvived = m_maxNumRoundsSurvived = 0;
  519. m_bCanMoveDuringFreezePeriod = false;
  520. m_isCurrentGunGameLeader = false;
  521. m_isCurrentGunGameTeamLeader = false;
  522. m_iLastWeaponFireUsercmd = 0;
  523. m_iAddonBits = 0;
  524. m_bEscaped = false;
  525. m_iAccount = 0;
  526. m_iAccountMoneyEarnedForNextRound = 0;
  527. m_iStartAccount = 0;
  528. m_iTotalCashSpent = 0;
  529. m_iCashSpentThisRound = 0;
  530. m_nEndMatchNextMapVote = -1;
  531. m_bIsVIP = false;
  532. m_iClass = (int )CS_CLASS_NONE;
  533. m_angEyeAngles.Init();
  534. m_flThirdpersonRecoil = 0;
  535. SetViewOffset( VEC_VIEW );
  536. m_pCurStateInfo = NULL; // no state yet
  537. m_iThrowGrenadeCounter = 0;
  538. m_bWaitForNoAttack = true;
  539. m_bIsSpawning = false;
  540. m_lifeState = LIFE_DEAD; // Start "dead".
  541. m_bWasInBombZoneTrigger = false;
  542. m_bInBombZoneTrigger = false;
  543. m_bInBombZone = false;
  544. m_bWasInBuyZone = false;
  545. m_bInBuyZone = false;
  546. m_bInNoDefuseArea = false;
  547. m_bWasInHostageRescueZone = false;
  548. m_bInHostageRescueZone = false;
  549. m_flDeathTime = 0.0f;
  550. m_fForceTeam = -1.0f;
  551. m_iHostagesKilled = 0;
  552. iRadioMenu = -1;
  553. m_bTeamChanged = false;
  554. m_iShotsFired = 0;
  555. m_bulletsFiredSinceLastSpawn = 0;
  556. m_iDirection = 0;
  557. m_receivesMoneyNextRound = true;
  558. m_bIsBeingGivenItem = false;
  559. m_isVIP = false;
  560. m_bIsRespawningForDMBonus = false;
  561. m_bHasUsedDMBonusRespawn = false;
  562. m_nQuestProgressReason = QuestProgress::QUEST_NONOFFICIAL_SERVER;
  563. m_unCurrentEquipmentValue = 0;
  564. m_unRoundStartEquipmentValue = 0;
  565. m_unFreezetimeEndEquipmentValue = 0;
  566. m_nLastKillerIndex = 0;
  567. m_nLastConcurrentKilled = 0;
  568. m_nDeathCamMusic = 0;
  569. m_bJustBecameSpectator = false;
  570. m_bJustKilledTeammate = false;
  571. m_bPunishedForTK = false;
  572. m_iTeamKills = 0;
  573. m_flLastAction = gpGlobals->curtime;
  574. m_iNextTimeCheck = 0;
  575. m_szNewName[0] = 0;
  576. m_szClanTag[0] = 0;
  577. m_szClanName[0] = 0;
  578. m_iTeammatePreferredColor = -1;
  579. for ( int i=0; i<NAME_CHANGE_HISTORY_SIZE; i++ )
  580. {
  581. m_flNameChangeHistory[i] = -NAME_CHANGE_HISTORY_INTERVAL;
  582. }
  583. m_iIgnoreGlobalChat = 0;
  584. m_bIgnoreRadio = false;
  585. m_pHintMessageQueue = new CHintMessageQueue(this );
  586. m_iDisplayHistoryBits = 0;
  587. m_bShowHints = true;
  588. m_flNextMouseoverUpdate = gpGlobals->curtime;
  589. m_lastDamageHealth = 0;
  590. m_lastDamageArmor = 0;
  591. m_nTeamDamageGivenForMatch = 0;
  592. m_bTDGaveProtectionWarning = false;
  593. m_bTDGaveProtectionWarningThisRound = false;
  594. m_flLastTHWarningTime = 0.0f;
  595. m_applyDeafnessTime = 0.0f;
  596. m_cycleLatch = 0;
  597. m_cycleLatchTimer.Invalidate();
  598. m_iShouldHaveCash = 0;
  599. m_lastNavArea = NULL;
  600. // [menglish] Init achievement variables
  601. // [menglish] Init bullet collision variables
  602. m_NumEnemiesKilledThisRound = 0;
  603. m_NumEnemiesKilledThisSpawn = 0;
  604. m_maxNumEnemiesKillStreak = 0;
  605. m_NumEnemiesAtRoundStart = 0;
  606. m_NumChickensKilledThisSpawn = 0;
  607. m_bLastKillUsedUniqueWeapon = false;
  608. m_bLastKillUsedUniqueWeaponMatch = false;
  609. m_KillingSpreeStartTime = -1;
  610. m_firstKillBlindStartTime = -1;
  611. m_killsWhileBlind = 0;
  612. m_bombCarrierkills = 0;
  613. m_knifeKillBombPlacer = false;
  614. m_bSurvivedHeadshotDueToHelmet = false;
  615. m_pGooseChaseDistractingPlayer = NULL;
  616. m_gooseChaseStep = GC_NONE;
  617. m_defuseDefenseStep = DD_NONE;
  618. m_lastRoundResult = Invalid_Round_End_Reason;
  619. m_bMadeFootstepNoise = false;
  620. m_bombPickupTime = -1.0f;
  621. m_knifeKillsWhenOutOfAmmo = 0;
  622. m_attemptedBombPlace = false;
  623. m_bombPlacedTime = -1.0f;
  624. m_bombDroppedTime = -1.0f;
  625. m_killedTime = -1.0f;
  626. m_spawnedTime = -1.0f;
  627. m_longestLife = -1.0f;
  628. m_triggerPulled = false;
  629. m_triggerPulls = 0;
  630. m_bMadePurchseThisRound = false;
  631. m_roundsWonWithoutPurchase = 0;
  632. m_iDeathFlags = 0;
  633. m_lastWeaponBeforeC4AutoSwitch = NULL;
  634. m_lastFlashBangAttacker = NULL;
  635. m_iMVPs = 0;
  636. m_iEnemyKills = 0;
  637. m_iEnemyKillHeadshots = 0;
  638. m_iEnemy3Ks = 0;
  639. m_iEnemy4Ks = 0;
  640. m_iEnemy5Ks = 0;
  641. m_iEnemyKillsAgg = 0;
  642. m_numFirstKills = 0;
  643. m_numClutchKills = 0;
  644. m_numPistolKills = 0;
  645. m_numSniperKills = 0;
  646. // m_iQuestEnemyKills = 0;
  647. // m_iQuestEnemyKillHeadshots = 0;
  648. m_iRoundsWon = 0;
  649. // m_iQuestDMBonusPoints = 0;
  650. // m_iQuestChickenKills = 0;
  651. m_uiAccountId = 0;
  652. m_bKilledDefuser = false;
  653. m_bKilledRescuer = false;
  654. m_maxGrenadeKills = 0;
  655. m_grenadeDamageTakenThisRound = 0;
  656. m_firstShotKills = 0;
  657. m_hasReloaded = false;
  658. m_flNextAutoBuyAmmoTime = 0;
  659. m_flGotHostageTalkTimer = 0;
  660. m_flDefusingTalkTimer = 0;
  661. m_flC4PlantTalkTimer = 0;
  662. m_flFlinchStack = 1.0;
  663. m_iTotalCashSpent = 0;
  664. m_iCashSpentThisRound = 0;
  665. // setting this to the current time prevents late-joining players from getting prioritized for receiving the defuser/bomb
  666. m_fLastGivenDefuserTime = gpGlobals->curtime;
  667. m_fLastGivenBombTime = gpGlobals->curtime;
  668. m_wasNotKilledNaturally = false;
  669. m_iGunGameProgressiveWeaponIndex = 0;
  670. m_bRespawning = false;
  671. m_bMadeFinalGunGameProgressiveKill = false;
  672. m_LastDamageType = 0;
  673. m_fImmuneToGunGameDamageTime = 0.0f;
  674. m_fJustLeftImmunityTime = 0.0f;
  675. m_lowHealthGoalTime = 0.0f;
  676. m_bGunGameImmunity = false;
  677. m_bHasMovedSinceSpawn = false;
  678. m_iNumGunGameKillsWithCurrentWeapon = 0;
  679. m_iNumGunGameTRKillPoints = 0;
  680. m_iNumGunGameTRBombTotalPoints = 0;
  681. m_iNumRoundKills = 0;
  682. m_iNumRoundKillsHeadshots = 0;
  683. m_iNumRoundTKs = 0;
  684. m_bShouldProgressGunGameTRBombModeWeapon = false;
  685. m_switchTeamsOnNextRoundReset = false;
  686. m_bGunGameTRModeHasHEGrenade = false;
  687. m_bGunGameTRModeHasFlashbang = false;
  688. m_bGunGameTRModeHasMolotov = false;
  689. m_bGunGameTRModeHasIncendiary = false;
  690. m_fMolotovUseTime = 0.0f;
  691. m_fMolotovDamageTime = 0.0f;
  692. m_flGuardianTooFarDistFrac = 0.0f;
  693. m_flNextGuardianTooFarHurtTime = 0.0f;
  694. m_flDetectedByEnemySensorTime = 0.0f;
  695. #if CS_CONTROLLABLE_BOTS_ENABLED
  696. m_bIsControllingBot = false;
  697. m_bCanControlObservedBot = false;
  698. m_iControlledBotEntIndex = -1;
  699. #endif
  700. m_botsControlled = 0;
  701. m_iFootsteps = 0;
  702. m_iMediumHealthKills = 0;
  703. m_bHud_MiniScoreHidden = false;
  704. m_bHud_RadarHidden = false;
  705. m_iMoveState = MOVESTATE_IDLE;
  706. m_iLastTeam = TEAM_UNASSIGNED;
  707. m_bHasSeenJoinGame = false;
  708. m_bInvalidSteamLogonDelayed = false;
  709. m_iPlayerState = -1;
  710. m_storedSpawnPosition = vec3_origin;
  711. m_storedSpawnAngle.Init( );
  712. m_flDominateEffectDelayTime = -1;
  713. m_hDominateEffectPlayer = NULL;
  714. m_nPreferredGrenadeDrop = 0;
  715. V_memset( (void*)m_rank.Base(), 0, sizeof( m_rank ) );
  716. m_bNeedToUpdateCoinFromInventory = true;
  717. SetMusicID( 0 );
  718. m_bNeedToUpdateMusicFromInventory = true;
  719. V_memset( m_unEquippedPlayerSprayIDs, 0, sizeof( m_unEquippedPlayerSprayIDs ) );
  720. m_bNeedToUpdatePlayerSprayFromInventory = true;
  721. m_pPersonaDataPublic = NULL;
  722. m_bNeedToUpdatePersonaDataPublicFromInventory = true;
  723. m_duckUntilOnGround = false;
  724. ClearScore();
  725. ClearContributionScore();
  726. SetSpotRules( CCSEntitySpotting::SPOT_RULE_ENEMY | CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_FRIEND );
  727. }
  728. void CCSPlayer::ClearScore( void )
  729. {
  730. // give us some points to start a round, to keep us from going negative
  731. ConVarRef matchStartScore( "score_default" );
  732. m_iScore = matchStartScore.GetInt();
  733. ClearRoundContributionScore();
  734. ClearRoundProximityScore();
  735. }
  736. CCSPlayer::~CCSPlayer()
  737. {
  738. delete m_pHintMessageQueue;
  739. m_pHintMessageQueue = NULL;
  740. // delete the records of damage taken and given
  741. ResetDamageCounters();
  742. if ( m_PlayerAnimState )
  743. m_PlayerAnimState->Release();
  744. if ( m_PlayerAnimStateCSGO )
  745. m_PlayerAnimStateCSGO->Release();
  746. delete m_pPersonaDataPublic;
  747. m_pPersonaDataPublic = NULL;
  748. }
  749. CCSPlayer *CCSPlayer::CreatePlayer( const char *className, edict_t *ed )
  750. {
  751. CCSPlayer::s_PlayerEdict = ed;
  752. return (CCSPlayer* )CreateEntityByName( className );
  753. }
  754. void CCSPlayer::Precache()
  755. {
  756. Vector mins( -13, -13, -10 );
  757. Vector maxs( 13, 13, 75 );
  758. bool bPreload = true;
  759. PlayerModelInfo::GetPtr()->InitializeForCurrentMap();
  760. // The following code is allowed to load non-existant .phy files due to the nested nature of the loads.
  761. // Tell the cache system not to worry if file are not found.
  762. const PlayerViewmodelArmConfig *pViewmodelArmConfig = NULL;
  763. // Only precache the models that are referenced by the map.
  764. for ( int classID=PlayerModelInfo::GetPtr()->GetFirstClass(); classID<=PlayerModelInfo::GetPtr()->GetLastClass(); ++classID )
  765. {
  766. g_pMDLCache->DisableFileNotFoundWarnings();
  767. engine->PrecacheModel( PlayerModelInfo::GetPtr()->GetClassModelPath(classID ), bPreload );
  768. engine->ForceModelBounds( PlayerModelInfo::GetPtr()->GetClassModelPath(classID ), mins, maxs );
  769. g_pMDLCache->EnableFileNotFoundWarnings();
  770. // non-econ viewmodel arms and gloves get precached here
  771. pViewmodelArmConfig = GetPlayerViewmodelArmConfigForPlayerModel( PlayerModelInfo::GetPtr()->GetClassModelPath(classID ) );
  772. if ( pViewmodelArmConfig )
  773. {
  774. g_pMDLCache->DisableVCollideLoad();
  775. if ( pViewmodelArmConfig->szAssociatedGloveModel && pViewmodelArmConfig->szAssociatedGloveModel[0] != '\0' )
  776. PrecacheModel( pViewmodelArmConfig->szAssociatedGloveModel );
  777. if ( pViewmodelArmConfig->szAssociatedSleeveModel && pViewmodelArmConfig->szAssociatedSleeveModel[0] != '\0' )
  778. PrecacheModel( pViewmodelArmConfig->szAssociatedSleeveModel );
  779. if ( pViewmodelArmConfig->szAssociatedSleeveModelEconOverride && pViewmodelArmConfig->szAssociatedSleeveModelEconOverride[0] != '\0' )
  780. PrecacheModel( pViewmodelArmConfig->szAssociatedSleeveModelEconOverride );
  781. g_pMDLCache->EnableVCollideLoad();
  782. }
  783. }
  784. #ifdef CS_SHIELD_ENABLED
  785. PrecacheModel( SHIELD_VIEW_MODEL );
  786. #endif
  787. PrecacheScriptSound( "Player.DeathHeadShot" );
  788. PrecacheScriptSound( "Player.Death" );
  789. PrecacheScriptSound( "Player.DamageHelmet" );
  790. //PrecacheScriptSound( "Player.DamageHelmetOtherFar" );
  791. PrecacheScriptSound( "Player.DamageHeadShot" );
  792. PrecacheScriptSound( "Player.DamageHeadShotOtherFar" );
  793. PrecacheScriptSound( "Flesh.BulletImpact" );
  794. PrecacheScriptSound( "Player.DamageKevlar" );
  795. PrecacheScriptSound( "Player.PickupWeapon" );
  796. PrecacheScriptSound( "Player.PickupWeaponSilent" );
  797. PrecacheScriptSound( "Player.Dominate" );
  798. PrecacheScriptSound( "Default.Land" );
  799. PrecacheScriptSound( "HealthShot.Pickup" );
  800. PrecacheScriptSound( "Music.Kill_01" );
  801. PrecacheScriptSound( "Music.Kill_02" );
  802. PrecacheScriptSound( "Music.Kill_03" );
  803. PrecacheScriptSound( "Music.GG_DeathCam_01" );
  804. PrecacheScriptSound( "Music.GG_DeathCam_02" );
  805. PrecacheScriptSound( "Music.GG_DeathCam_03" );
  806. PrecacheScriptSound( "Music.Final_Round_Stinger" );
  807. PrecacheScriptSound( "Music.Match_Point_Stinger" );
  808. PrecacheScriptSound( "Music.GG_Nemesis" );
  809. PrecacheScriptSound( "Music.GG_Revenge" );
  810. PrecacheScriptSound( "Music.GG_Dominating" );
  811. PrecacheScriptSound( "Player.Respawn" );
  812. PrecacheScriptSound( "UI.DeathMatchBonusKill" );
  813. PrecacheScriptSound( "UI.ArmsRace.BecomeMatchLeader" );
  814. PrecacheScriptSound( "UI.ArmsRace.BecomeTeamLeader" );
  815. PrecacheScriptSound( "UI.ArmsRace.Demoted" );
  816. PrecacheScriptSound( "UI.ArmsRace.LevelUp" );
  817. PrecacheScriptSound( "UI.Guardian.TooFarWarning" );
  818. PrecacheScriptSound( "UI.DeathMatchBonusAlertEnd" );
  819. PrecacheScriptSound( "UI.DeathMatchBonusAlertStart" );
  820. PrecacheScriptSound( "UI.DeathNotice" );
  821. PrecacheScriptSound( "Hostage.Breath" );
  822. PrecacheScriptSound( "SprayCan.Shake" );
  823. PrecacheScriptSound( "SprayCan.Paint" );
  824. // CS Bot sounds
  825. PrecacheScriptSound( "Bot.StuckSound" );
  826. PrecacheScriptSound( "Bot.StuckStart" );
  827. PrecacheScriptSound( "Bot.FellOff" );
  828. UTIL_PrecacheOther( "item_kevlar" );
  829. UTIL_PrecacheOther( "item_assaultsuit" );
  830. UTIL_PrecacheOther( "item_heavyassaultsuit" );
  831. UTIL_PrecacheOther( "item_defuser" );
  832. PrecacheModel( "sprites/glow01.vmt" );
  833. PrecacheEffect( "csblood" );
  834. PrecacheEffect( "gunshotsplash" );
  835. PrecacheParticleSystem( "speech_voice" );
  836. PrecacheParticleSystem( "impact_helmet_headshot" );
  837. PrecacheParticleSystem( "ar_screenglow_leader_red" );
  838. PrecacheModel( "models/player/holiday/santahat.mdl" );
  839. PrecacheModel( "models/ghost/ghost.mdl" ); // halloween
  840. PrecacheModel( "models/player/holiday/facemasks/facemask_battlemask.mdl" );
  841. if ( CSGameRules()->IsPlayingCooperativeGametype() )
  842. PrecacheModel( g_pszHeavyPhoenixModel );
  843. PrecacheModel( "models/weapons/w_muzzlefireshape.mdl" );
  844. PrecacheModel( "models/tools/bullet_hit_marker.mdl" );
  845. // Not shipping loot quests this operation, don't precache the model
  846. PrecacheModel( "models/matlibrary/matlibrary_default.mdl" );
  847. //PrecacheModel( g_pszLootModelName );
  848. PrecacheModel( "models/player/contactshadow/contactshadow_leftfoot.mdl" );
  849. PrecacheModel( "models/player/contactshadow/contactshadow_rightfoot.mdl" );
  850. BaseClass::Precache();
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Purpose: Allow pre-frame adjustments on the player
  854. //-----------------------------------------------------------------------------
  855. ConVar sv_runcmds( "sv_runcmds", "1" );
  856. void CCSPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper )
  857. {
  858. VPROF( "CCSPlayer::PlayerRunCommand" );
  859. if ( !sv_runcmds.GetInt() )
  860. return;
  861. // don't run commands in the future
  862. if ( !IsEngineThreaded() &&
  863. ( ucmd->tick_count > (gpGlobals->tickcount + sv_max_usercmd_future_ticks.GetInt() ) ) )
  864. {
  865. DevMsg( "Client cmd out of sync (delta %i).\n", ucmd->tick_count - gpGlobals->tickcount );
  866. return;
  867. }
  868. // If they use a negative bot_mimic value, then don't process their usercmds, but have
  869. // bots process them instead (so they can stay still and have the bot move around ).
  870. CUserCmd tempCmd;
  871. if ( -bot_mimic.GetInt() == entindex() )
  872. {
  873. tempCmd = *ucmd;
  874. ucmd = &tempCmd;
  875. ucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0;
  876. ucmd->buttons = 0;
  877. ucmd->impulse = 0;
  878. }
  879. if ( IsBot() && bot_crouch.GetInt() )
  880. {
  881. ucmd->buttons |= IN_DUCK;
  882. }
  883. if ( ( IsTaunting() && !IsThirdPersonTaunt() ) || IsLookingAtWeapon() )
  884. {
  885. if ( ( ucmd->buttons & ( IN_ATTACK | IN_ATTACK2 | IN_RELOAD ) ) != 0 /*|| ucmd->forwardmove || ucmd->sidemove || ucmd->upmove*/ )
  886. {
  887. StopTaunting();
  888. StopLookingAtWeapon();
  889. if ( ( ucmd->buttons & IN_ATTACK2 ) != 0 && ( ucmd->buttons & ( IN_ATTACK | IN_RELOAD ) ) == 0 )
  890. {
  891. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  892. if ( pWeapon && pWeapon->HasZoom() )
  893. {
  894. // Force the animation back to idle since changing zoom has no specific animation
  895. CBaseViewModel *pViewModel = GetViewModel();
  896. if ( pViewModel )
  897. {
  898. int nSequence = pViewModel->LookupSequence( "idle" );
  899. if ( nSequence != ACTIVITY_NOT_AVAILABLE )
  900. {
  901. pViewModel->ForceCycle( 0 );
  902. pViewModel->ResetSequence( nSequence );
  903. }
  904. }
  905. }
  906. }
  907. }
  908. }
  909. if ( IsTaunting() && IsThirdPersonTaunt() )
  910. {
  911. // For some taunts, it is critical that the player not move once they start
  912. if ( m_bMustNotMoveDuringTaunt )
  913. {
  914. ucmd->forwardmove = 0;
  915. ucmd->upmove = 0;
  916. ucmd->sidemove = 0;
  917. ucmd->viewangles = pl.v_angle;
  918. }
  919. }
  920. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  921. }
  922. bool CCSPlayer::RunMimicCommand( CUserCmd& cmd )
  923. {
  924. if ( !IsBot() )
  925. return false;
  926. int iMimic = abs( bot_mimic.GetInt() );
  927. if ( iMimic > gpGlobals->maxClients )
  928. return false;
  929. CBasePlayer *pPlayer = UTIL_PlayerByIndex( iMimic );
  930. if ( !pPlayer )
  931. return false;
  932. if ( !pPlayer->GetLastUserCommand() )
  933. return false;
  934. cmd = *pPlayer->GetLastUserCommand();
  935. cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
  936. pl.fixangle = FIXANGLE_NONE;
  937. return true;
  938. }
  939. //-----------------------------------------------------------------------------
  940. // Purpose: Simulates a single frame of movement for a player
  941. //-----------------------------------------------------------------------------
  942. void CCSPlayer::RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
  943. {
  944. CUserCmd cmd;
  945. // Store off the globals.. they're gonna get whacked
  946. float flOldFrametime = gpGlobals->frametime;
  947. float flOldCurtime = gpGlobals->curtime;
  948. float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
  949. this->SetTimeBase( flTimeBase );
  950. CUserCmd lastUserCmd = *GetLastUserCommand();
  951. Q_memset( &cmd, 0, sizeof( cmd ) );
  952. if ( !RunMimicCommand( cmd ) )
  953. {
  954. cmd.forwardmove = forwardmove;
  955. cmd.sidemove = sidemove;
  956. cmd.upmove = upmove;
  957. cmd.buttons = buttons;
  958. cmd.impulse = impulse;
  959. VectorCopy( viewangles, cmd.viewangles );
  960. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  961. }
  962. MoveHelperServer()->SetHost( this );
  963. PlayerRunCommand( &cmd, MoveHelperServer() );
  964. // save off the last good usercmd
  965. if ( -bot_mimic.GetInt() == entindex() )
  966. {
  967. CUserCmd lastCmd = *GetLastUserCommand();
  968. lastCmd.command_number = cmd.command_number;
  969. lastCmd.tick_count = cmd.tick_count;
  970. SetLastUserCommand( lastCmd );
  971. }
  972. else
  973. {
  974. SetLastUserCommand( cmd );
  975. }
  976. // Clear out any fixangle that has been set
  977. pl.fixangle = FIXANGLE_NONE;
  978. // Restore the globals..
  979. gpGlobals->frametime = flOldFrametime;
  980. gpGlobals->curtime = flOldCurtime;
  981. MoveHelperServer()->SetHost( NULL );
  982. }
  983. void CCSPlayer::InitialSpawn( void )
  984. {
  985. #if defined( USE_PLAYER_ATTRIBUTE_MANAGER )
  986. m_AttributeManager.InitializeAttributes( this );
  987. m_AttributeManager.SetPlayer( this );
  988. m_AttributeList.SetManager( &m_AttributeManager );
  989. #endif
  990. BaseClass::InitialSpawn();
  991. // we're going to give the bots money here instead of FinishClientPutInServer()
  992. // because of the bots' timing for purchasing weapons/items.
  993. if ( IsBot() )
  994. {
  995. m_iAccount = CSGameRules()->GetStartMoney();
  996. m_iAccountMoneyEarnedForNextRound = 0;
  997. if ( CSGameRules()->ShouldRecordMatchStats() )
  998. {
  999. m_iMatchStats_CashEarned.GetForModify( CSGameRules()->GetTotalRoundsPlayed() ) = m_iAccount;
  1000. }
  1001. // Keep track in QMM data
  1002. if ( m_uiAccountId && CSGameRules() )
  1003. {
  1004. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  1005. {
  1006. pQMM->m_cash = m_iAccount;
  1007. if ( CSGameRules()->ShouldRecordMatchStats() )
  1008. {
  1009. pQMM->m_iMatchStats_CashEarned[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_CashEarned.Get( CSGameRules()->GetTotalRoundsPlayed() );
  1010. }
  1011. }
  1012. }
  1013. }
  1014. if ( !engine->IsDedicatedServer() && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() && !IsGameConsole() )
  1015. {
  1016. ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
  1017. }
  1018. Assert( GetAbsVelocity().Length() == 0.0f );
  1019. State_Enter( STATE_WELCOME );
  1020. UpdateInventory( true );
  1021. // [tj] We reset the stats at the beginning of the map (including domination tracking )
  1022. CCS_GameStats.ResetPlayerStats(this );
  1023. RemoveNemesisRelationships();
  1024. // for late joiners, we want to give them a fighting chance in gun game so, give them the lowest level reached by a player already
  1025. int nMinWep = 0;
  1026. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1027. {
  1028. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  1029. if ( pPlayer )
  1030. {
  1031. int nCurWep = pPlayer->GetPlayerGunGameWeaponIndex();
  1032. if ( nCurWep < nMinWep )
  1033. nMinWep = nCurWep;
  1034. }
  1035. }
  1036. if ( nMinWep > 0 )
  1037. {
  1038. // +1 because when they select a team, we reduce their level by 1
  1039. m_iGunGameProgressiveWeaponIndex = nMinWep + 1;
  1040. }
  1041. }
  1042. void CCSPlayer::SetModelFromClass( void )
  1043. {
  1044. if ( GetTeamNumber() == TEAM_TERRORIST )
  1045. {
  1046. if ( !PlayerModelInfo::GetPtr()->IsTClass( m_iClass ) )
  1047. {
  1048. m_iClass = PlayerModelInfo::GetPtr()->GetNextClassForTeam( GetTeamNumber() );
  1049. }
  1050. }
  1051. else if ( GetTeamNumber() == TEAM_CT )
  1052. {
  1053. if ( !PlayerModelInfo::GetPtr()->IsCTClass( m_iClass ) )
  1054. {
  1055. m_iClass = PlayerModelInfo::GetPtr()->GetNextClassForTeam( GetTeamNumber() );
  1056. }
  1057. }
  1058. else
  1059. {
  1060. // Default model / class if we are not on a team.
  1061. SetModel( PlayerModelInfo::GetPtr()->GetClassModelPath( PlayerModelInfo::GetPtr()->GetFirstTClass() ) );
  1062. return;
  1063. }
  1064. SetModel( PlayerModelInfo::GetPtr()->GetClassModelPath( m_iClass ) );
  1065. }
  1066. void CCSPlayer::SetCSSpawnLocation( Vector position, QAngle angle )
  1067. {
  1068. m_storedSpawnPosition = position;
  1069. m_storedSpawnAngle = angle;
  1070. }
  1071. void CCSPlayer::Spawn()
  1072. {
  1073. m_RateLimitLastCommandTimes.Purge();
  1074. // Get rid of the progress bar...
  1075. SetProgressBarTime( 0 );
  1076. CreateViewModel();
  1077. // Set their player model.
  1078. bool bStartsWithHeavyArmorThisRound = false;
  1079. // Sometimes spawn a heavy in guardian mode, don't pick model based on player class
  1080. if ( GetTeamNumber() == TEAM_TERRORIST && CSGameRules()->IsPlayingCoopGuardian() && IsBot() &&
  1081. (
  1082. (
  1083. CSGameRules()->GetCoopWaveNumber() >= sv_guardian_min_wave_for_heavy.GetInt() &&
  1084. CSGameRules()->GetCoopWaveNumber() <= sv_guardian_max_wave_for_heavy.GetInt() &&
  1085. CSGameRules()->m_nNumHeaviesToSpawn > 0 && !CSGameRules()->IsWarmupPeriod()
  1086. )
  1087. ||
  1088. sv_guardian_heavy_all.GetBool()
  1089. )
  1090. )
  1091. {
  1092. //
  1093. // Also see cs_bot_manager.cpp that manages spawning sniper bots
  1094. // Ensure that we have a sniper profile on the bot team for cooperative game
  1095. //
  1096. // Check my bot profile if I am a sniper?
  1097. bool bThisCanBeHeavyBot = true;
  1098. if ( CCSBot *pMyBot = dynamic_cast< CCSBot * >( this ) )
  1099. {
  1100. const BotProfile *pExistingBotProfile = pMyBot->GetProfile();
  1101. if ( pExistingBotProfile && pExistingBotProfile->GetWeaponPreferenceCount() && WeaponClassFromWeaponID( pExistingBotProfile->GetWeaponPreference( 0 ) ) == WEAPONTYPE_SNIPER_RIFLE )
  1102. { // found a bot who will buy a sniper rifle guaranteed
  1103. bThisCanBeHeavyBot = false;
  1104. }
  1105. }
  1106. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  1107. {
  1108. CCSPlayer *player = static_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
  1109. if ( player == NULL )
  1110. continue;
  1111. // skip players on other teams
  1112. if ( player->GetTeamNumber() != GetTeamNumber() )
  1113. continue;
  1114. // if not a bot, fail the test
  1115. if ( !player->IsBot() )
  1116. continue ;
  1117. if ( player->HasHeavyArmor() )
  1118. {
  1119. bThisCanBeHeavyBot = false;
  1120. break;
  1121. }
  1122. }
  1123. int minNumSniperBots = 1;
  1124. extern ConVar mp_guardian_special_weapon_needed;
  1125. char const *szWepShortName = mp_guardian_special_weapon_needed.GetString();
  1126. if ( szWepShortName && *szWepShortName && V_strcmp( szWepShortName, "any" ) )
  1127. {
  1128. CSWeaponID csWeaponIdGuardianRequired = AliasToWeaponID( szWepShortName );
  1129. if ( ( csWeaponIdGuardianRequired != WEAPON_NONE ) &&
  1130. ( WeaponClassFromWeaponID( csWeaponIdGuardianRequired ) == WEAPONTYPE_SNIPER_RIFLE ) )
  1131. {
  1132. ++minNumSniperBots;
  1133. }
  1134. }
  1135. //
  1136. // End of code that is matching cs_bot_manager.cpp for enumerating guardian sniper bots
  1137. //
  1138. if ( sv_guardian_heavy_all.GetBool() )
  1139. {
  1140. bStartsWithHeavyArmorThisRound = true;
  1141. }
  1142. else if ( bThisCanBeHeavyBot )
  1143. {
  1144. // Each bot has an equal chance of being heavy distributed across all rounds where heavies are possible, except last round where we slam odds to 100% if any unspawned heavies remain.
  1145. int iNumWavesTillMax = sv_guardian_max_wave_for_heavy.GetInt() - CSGameRules()->GetCoopWaveNumber();
  1146. float flHeavyChance = 1.0f;
  1147. if ( iNumWavesTillMax >= CSGameRules()->m_nNumHeaviesToSpawn )
  1148. {
  1149. flHeavyChance = 1 / ( float ) ( iNumWavesTillMax - CSGameRules()->m_nNumHeaviesToSpawn + 2 );
  1150. }
  1151. if ( RandomFloat() < flHeavyChance )
  1152. {
  1153. CSGameRules()->m_nNumHeaviesToSpawn--;
  1154. bStartsWithHeavyArmorThisRound = true;
  1155. }
  1156. }
  1157. }
  1158. if ( bStartsWithHeavyArmorThisRound )
  1159. {
  1160. SetModel( g_pszHeavyPhoenixModel );
  1161. GiveNamedItem( "item_heavyassaultsuit" );
  1162. m_iClass = 1; // Set some valid class, doesn't matter which.
  1163. }
  1164. else
  1165. {
  1166. SetModelFromClass();
  1167. }
  1168. if ( GetTeamNumber() == TEAM_TERRORIST && CSGameRules()->IsPlayingCoopGuardian() && IsBot() )
  1169. {
  1170. CCSBot *pMyBot = dynamic_cast< CCSBot * >( this );
  1171. const BotProfile *pExistingBotProfile = pMyBot ? pMyBot->GetProfile() : NULL;
  1172. if ( pExistingBotProfile )
  1173. {
  1174. // Mimic cs_bot_init.cpp that is naming bots for coop missions
  1175. char const *szHeavy = bStartsWithHeavyArmorThisRound ? "Heavy Phoenix " : "Attacker ";
  1176. char szName[ 128 ] = {};
  1177. V_sprintf_safe( szName, "%s%s", szHeavy, pExistingBotProfile->GetName() );
  1178. // Override the player name
  1179. SetPlayerName( szName );
  1180. // have to inform the engine that the bot name has been updated
  1181. engine->SetFakeClientConVarValue( edict(), "name", szName );
  1182. }
  1183. }
  1184. m_bCanMoveDuringFreezePeriod = false;
  1185. // m_isCurrentGunGameLeader = false;
  1186. // m_isCurrentGunGameTeamLeader = false;
  1187. if ( GetParent() )
  1188. SetParent( NULL );
  1189. BaseClass::Spawn();
  1190. // After base class spawn strips our last-round items and grants defaults, give us our heavy armor if we've been chosen this round
  1191. if ( bStartsWithHeavyArmorThisRound )
  1192. {
  1193. m_bHasHeavyArmor = true;
  1194. GiveNamedItem( "item_heavyassaultsuit" );
  1195. }
  1196. UpdateInventory( false );
  1197. // [pfreese] Clear the last known nav area (used to be done by CBasePlayer )
  1198. m_lastNavArea = NULL;
  1199. AddFlag(FL_ONGROUND ); // set the player on the ground at the start of the round.
  1200. // Override what CBasePlayer set for the view offset.
  1201. SetViewOffset( VEC_VIEW );
  1202. //
  1203. // Our player movement speed is set once here. This will override the cl_xxxx
  1204. // cvars unless they are set to be lower than this.
  1205. //
  1206. SetMaxSpeed( CS_PLAYER_SPEED_RUN );
  1207. SetFOV( this, 0 );
  1208. m_bIsDefusing = false;
  1209. m_bIsGrabbingHostage = false;
  1210. m_bIsWalking = false;
  1211. // [jpaquin] variable to keep entities for checking to refil ammo every tick
  1212. m_flNextAutoBuyAmmoTime = gpGlobals->curtime + RandomFloat( 0, SECONDS_BETWEEN_AUTOAMMOBUY_CHECKS );
  1213. // [dwenger] Reset hostage-related variables
  1214. m_bIsRescuing = false;
  1215. m_bInjuredAHostage = false;
  1216. m_iNumFollowers = 0;
  1217. // [tj] Reset this flag if the player is not in observer mode (as happens when a player spawns late )
  1218. if (m_iPlayerState != STATE_OBSERVER_MODE )
  1219. {
  1220. m_wasNotKilledNaturally = false;
  1221. }
  1222. m_bulletsFiredSinceLastSpawn = 0;
  1223. m_iShotsFired = 0;
  1224. m_iDirection = 0;
  1225. m_bWaitForNoAttack = true;
  1226. if ( m_pHintMessageQueue )
  1227. {
  1228. m_pHintMessageQueue->Reset();
  1229. }
  1230. m_iDisplayHistoryBits &= ~DHM_ROUND_CLEAR;
  1231. // Special-case here. A bunch of things happen in CBasePlayer::Spawn(), and we really want the
  1232. // player states to control these things, so give whatever player state we're in a chance
  1233. // to reinitialize itself.
  1234. State_Transition( m_iPlayerState );
  1235. ClearFlashbangScreenFade();
  1236. m_flVelocityModifier = 1.0f;
  1237. m_flGroundAccelLinearFracLastTime = 0.0f;
  1238. ResetStamina();
  1239. m_fNextRadarUpdateTime = 0.0f;
  1240. m_flLastMoneyUpdateTime = 0.0f;
  1241. m_fMolotovDamageTime = 0.0f;
  1242. m_iLastWeaponFireUsercmd = 0;
  1243. m_iNumSpawns++;
  1244. if ( !engine->IsDedicatedServer() && CSGameRules()->m_iTotalRoundsPlayed < 2 && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() && !IsGameConsole() )
  1245. {
  1246. ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
  1247. }
  1248. m_bTeamChanged = false;
  1249. m_iOldTeam = TEAM_UNASSIGNED;
  1250. m_bHasMovedSinceSpawn = false;
  1251. m_iRadioMessages = 60;
  1252. m_flRadioTime = gpGlobals->curtime;
  1253. if ( m_hRagdoll )
  1254. {
  1255. UTIL_Remove( m_hRagdoll );
  1256. }
  1257. m_hRagdoll = NULL;
  1258. // did we change our name while we were dead?
  1259. if ( m_szNewName[0] != 0 )
  1260. {
  1261. ChangeName( m_szNewName );
  1262. m_szNewName[0] = 0;
  1263. }
  1264. if ( m_bIsVIP )
  1265. {
  1266. HintMessage( "#Hint_you_are_the_vip", true, true );
  1267. }
  1268. m_bIsInAutoBuy = false;
  1269. m_bIsInRebuy = false;
  1270. m_bAutoReload = false;
  1271. // reset the number of enemies killed this round only we're not respawning to get the bonus weapon.
  1272. if ( !m_bIsRespawningForDMBonus )
  1273. {
  1274. m_NumEnemiesKilledThisSpawn = 0;
  1275. m_NumChickensKilledThisSpawn = 0;
  1276. }
  1277. SetContextThink( &CCSPlayer::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );
  1278. if ( GetActiveWeapon() && !IsObserver() )
  1279. {
  1280. GetActiveWeapon()->Deploy();
  1281. m_flNextAttack = gpGlobals->curtime; // Allow reloads to finish, since we're playing the deploy anim instead. This mimics goldsrc behavior, anyway.
  1282. }
  1283. m_flLastTHWarningTime = 0.0f;
  1284. m_applyDeafnessTime = 0.0f;
  1285. m_lowHealthGoalTime = 0.0f;
  1286. m_cycleLatch = 0;
  1287. if ( !m_bUseNewAnimstate )
  1288. m_cycleLatchTimer.Start( RandomFloat( 0.0f, CycleLatchInterval ) );
  1289. StockPlayerAmmo();
  1290. // Calculate timeout for gun game immunity
  1291. ConVarRef mp_respawn_immunitytime( "mp_respawn_immunitytime" );
  1292. float flImmuneTime = mp_respawn_immunitytime.GetFloat();
  1293. if ( flImmuneTime > 0 || CSGameRules()->IsWarmupPeriod() )
  1294. {
  1295. //Make sure we can't move if we respawn in gun game after the rounds ends
  1296. CCSMatch* pMatch = CSGameRules()->GetMatch();
  1297. if ( pMatch && pMatch->GetPhase() == GAMEPHASE_MATCH_ENDED )
  1298. {
  1299. AddFlag( FL_FROZEN );
  1300. }
  1301. if ( CSGameRules()->IsPlayingGunGameDeathmatch() && !IsBot() )
  1302. {
  1303. // set immune time to super high and open the buy menu
  1304. m_bInBuyZone = true;
  1305. }
  1306. else if ( CSGameRules()->IsWarmupPeriod() )
  1307. {
  1308. flImmuneTime = 3;
  1309. }
  1310. m_fImmuneToGunGameDamageTime = gpGlobals->curtime + flImmuneTime;
  1311. m_bGunGameImmunity = true;
  1312. }
  1313. else
  1314. {
  1315. m_fImmuneToGunGameDamageTime = 0.0f;
  1316. m_bGunGameImmunity = false;
  1317. }
  1318. m_knifeKillsWhenOutOfAmmo = 0;
  1319. m_botsControlled = 0;
  1320. m_iFootsteps = 0;
  1321. m_iMediumHealthKills = 0;
  1322. m_killedTime = -1.0f;
  1323. m_spawnedTime = gpGlobals->curtime;
  1324. m_bKilledByTaser = false;
  1325. m_bHasBeenControlledByPlayerThisRound = false;
  1326. m_bHasControlledBotThisRound = false;
  1327. StopTaunting();
  1328. m_bIsHoldingTaunt = false;
  1329. StopLookingAtWeapon();
  1330. m_bIsHoldingLookAtWeapon = false;
  1331. m_bDuckOverride = false;
  1332. // save off how much money we started with; OGS will want this later
  1333. m_iStartAccount = m_iAccount;
  1334. // reset the money that is being held until next round
  1335. m_iAccountMoneyEarnedForNextRound = 0;
  1336. for ( int i = 0; i < MAX_WEAPONS; ++i )
  1337. {
  1338. CBaseCombatWeapon *pWeapon = GetWeapon( i );
  1339. if ( pWeapon )
  1340. {
  1341. CWeaponCSBase *pCSwep = dynamic_cast< CWeaponCSBase * >( pWeapon );
  1342. if ( pCSwep )
  1343. {
  1344. pCSwep->WeaponReset();
  1345. }
  1346. }
  1347. }
  1348. for ( int i = 0; i < m_iWeaponPurchasesThisRound.Count(); ++i )
  1349. {
  1350. m_iWeaponPurchasesThisRound.Set(i, 0);
  1351. }
  1352. if ( IsAbleToInstantRespawn() && !CSGameRules()->IsPlayingOffline() && IsBot() )
  1353. {
  1354. // Modify this bot's difficulty in Arms Race mode on respawn since arms race doesn't have a round end except at match end
  1355. CSGameRules()->ModifyRealtimeBotDifficulty( this );
  1356. }
  1357. if ( IsAbleToInstantRespawn() )
  1358. {
  1359. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  1360. {
  1361. int nCurWepKills = GetNumGunGameKillsWithCurrentWeapon();
  1362. m_iNumRoundKills = MIN( nCurWepKills, m_iNumRoundKills );
  1363. m_iNumRoundKillsHeadshots = MIN( nCurWepKills, m_iNumRoundKillsHeadshots );
  1364. }
  1365. else
  1366. {
  1367. m_iNumRoundKills = 0;
  1368. m_iNumRoundKillsHeadshots = 0;
  1369. }
  1370. m_iNumRoundTKs = 0;
  1371. }
  1372. // if( IsBot() )
  1373. // {
  1374. // SetDeathCamMusicIndex( RandomInt(1, 8) );
  1375. // }
  1376. //
  1377. // Transfer all parameters that we know about
  1378. //
  1379. CSteamID const *pThisSteamId = engine->GetClientSteamID( this->edict() );
  1380. if ( pThisSteamId && pThisSteamId->IsValid() && pThisSteamId->GetAccountID() && !IsBot() && CSGameRules() )
  1381. {
  1382. SetHumanPlayerAccountID( pThisSteamId->GetAccountID() );
  1383. }
  1384. // If we're constantly respawning then reset damage stats on spawn. Otherwise this'll happen on roundrespawn after damage is reported.
  1385. if ( IsAbleToInstantRespawn() )
  1386. {
  1387. ResetDamageCounters();
  1388. RemoveSelfFromOthersDamageCounters();
  1389. if ( cash_player_respawn_amount.GetInt() > 0 )
  1390. {
  1391. AddAccountAward( PlayerCashAward::RESPAWN );
  1392. }
  1393. }
  1394. // clear out and carried hostage stuff
  1395. RemoveCarriedHostage();
  1396. ResetKillStreak();
  1397. // play a respawn sound if you're in deathmatch
  1398. // TODO: turn this into a convar and put it inside the above IsAbleToInstantRespawn check
  1399. if ( State_Get() == STATE_ACTIVE )
  1400. {
  1401. bool bPlaySound = false;
  1402. int nTeam = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  1403. if ( CSGameRules()->IsPlayingCoopMission() )
  1404. {
  1405. if ( GetTeamNumber() == TEAM_CT )
  1406. bPlaySound = true;
  1407. }
  1408. else if ( (CSGameRules()->IsPlayingGunGameDeathmatch() && GetTeamNumber() >= TEAM_TERRORIST) ||
  1409. (CSGameRules()->IsPlayingCooperativeGametype() && GetTeamNumber() == nTeam) )
  1410. {
  1411. bPlaySound = true;
  1412. }
  1413. if ( bPlaySound )
  1414. EmitSound( "Player.Respawn" );
  1415. }
  1416. if ( m_bUseNewAnimstate && m_PlayerAnimStateCSGO )
  1417. {
  1418. m_PlayerAnimStateCSGO->Reset();
  1419. m_PlayerAnimStateCSGO->Update( EyeAngles()[YAW], EyeAngles()[PITCH], true );
  1420. DoAnimationEvent( PLAYERANIMEVENT_DEPLOY ); // re-deploy default weapon when spawning
  1421. }
  1422. // if a player spawns with a smoke grenade in competitive, it counts as being "bought" so they can't end up with 2 per player that round
  1423. if ( CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() )
  1424. {
  1425. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_SMOKEGRENADE ) ) )
  1426. m_iWeaponPurchasesThisRound.GetForModify(WEAPON_SMOKEGRENADE)++;
  1427. }
  1428. engine->ClientResetReplayRequestTime( entindex() - 1 );
  1429. // coop bots with custom models
  1430. if ( CSGameRules()->IsPlayingCoopMission() && IsBot() )
  1431. {
  1432. if ( GetTeamNumber() == TEAM_TERRORIST )
  1433. {
  1434. CCSBot* pBot = static_cast< CCSBot* >( this );
  1435. if ( pBot )
  1436. {
  1437. SpawnPointCoopEnemy* pSpawn = pBot->GetLastCoopSpawnPoint();
  1438. if ( pSpawn )
  1439. {
  1440. if ( V_stricmp( pSpawn->GetPlayerModelToUse(), "" ) != 0 )
  1441. {
  1442. SetModel( pSpawn->GetPlayerModelToUse() );
  1443. //SetViewModelArms( viewModelArms );
  1444. return;
  1445. }
  1446. }
  1447. }
  1448. }
  1449. }
  1450. if ( m_bInvalidSteamLogonDelayed )
  1451. {
  1452. m_bInvalidSteamLogonDelayed = false;
  1453. Warning( "Invalid Steam Logon Delayed: Kicking client [U:1:%d] %s\n", GetHumanPlayerAccountID(), GetPlayerName() );
  1454. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d No Steam logon\n", this->GetUserID(), /*bIsPlayingOffline*/ false ? 0 : 1 ) );
  1455. }
  1456. }
  1457. void CCSPlayer::SetHumanPlayerAccountID( uint32 uiAccountId )
  1458. {
  1459. m_uiAccountId = uiAccountId;
  1460. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  1461. {
  1462. CCSGameRules::CQMMPlayerData_t &qmmPlayerData = *pQMM;
  1463. this->m_iFrags = qmmPlayerData.m_numKills;
  1464. this->m_iAssists = qmmPlayerData.m_numAssists;
  1465. this->m_iDeaths = qmmPlayerData.m_numDeaths;
  1466. this->m_iMVPs = qmmPlayerData.m_numMVPs;
  1467. this->m_iContributionScore = qmmPlayerData.m_numScorePoints;
  1468. this->m_iAccount = qmmPlayerData.m_cash;
  1469. this->m_iTeamKills = qmmPlayerData.m_numTeamKills;
  1470. this->m_nTeamDamageGivenForMatch = qmmPlayerData.m_numTeamDamagePoints;
  1471. this->m_iHostagesKilled = qmmPlayerData.m_numHostageKills;
  1472. this->m_iEnemyKills = qmmPlayerData.m_numEnemyKills;
  1473. this->m_iEnemyKillHeadshots = qmmPlayerData.m_numEnemyKillHeadshots;
  1474. this->m_iEnemy3Ks = qmmPlayerData.m_numEnemy3Ks;
  1475. this->m_iEnemy4Ks = qmmPlayerData.m_numEnemy4Ks;
  1476. this->m_iEnemy5Ks = qmmPlayerData.m_numEnemy5Ks;
  1477. this->m_iEnemyKillsAgg = qmmPlayerData.m_numEnemyKillsAgg;
  1478. this->m_numFirstKills = qmmPlayerData.m_numFirstKills;
  1479. this->m_numClutchKills = qmmPlayerData.m_numClutchKills;
  1480. this->m_numPistolKills = qmmPlayerData.m_numPistolKills;
  1481. this->m_numSniperKills = qmmPlayerData.m_numSniperKills;
  1482. this->m_iRoundsWon = qmmPlayerData.m_numRoundsWon;
  1483. this->m_receivesMoneyNextRound = !qmmPlayerData.m_bReceiveNoMoneyNextRound;
  1484. this->pl.frags = this->m_iFrags;
  1485. this->pl.assists = this->m_iAssists;
  1486. this->pl.deaths = this->m_iDeaths;
  1487. this->pl.score = this->m_iContributionScore;
  1488. for ( int i = 0; i < MAX_MATCH_STATS_ROUNDS; i ++ )
  1489. {
  1490. this->m_iMatchStats_Kills.GetForModify( i ) = qmmPlayerData.m_iMatchStats_Kills[ i ];
  1491. this->m_iMatchStats_Damage.GetForModify( i ) = qmmPlayerData.m_iMatchStats_Damage[ i ];
  1492. this->m_iMatchStats_MoneySaved.GetForModify( i ) = qmmPlayerData.m_iMatchStats_MoneySaved[ i ];
  1493. this->m_iMatchStats_EquipmentValue.GetForModify( i ) = qmmPlayerData.m_iMatchStats_EquipmentValue[ i ];
  1494. this->m_iMatchStats_KillReward.GetForModify( i ) = qmmPlayerData.m_iMatchStats_KillReward[ i ];
  1495. this->m_iMatchStats_LiveTime.GetForModify( i ) = qmmPlayerData.m_iMatchStats_LiveTime[ i ];
  1496. this->m_iMatchStats_Deaths.GetForModify( i ) = qmmPlayerData.m_iMatchStats_Deaths[ i ];
  1497. this->m_iMatchStats_Assists.GetForModify( i ) = qmmPlayerData.m_iMatchStats_Assists[ i ];
  1498. this->m_iMatchStats_HeadShotKills.GetForModify( i ) = qmmPlayerData.m_iMatchStats_HeadShotKills[ i ];
  1499. this->m_iMatchStats_Objective.GetForModify( i ) = qmmPlayerData.m_iMatchStats_Objective[ i ];
  1500. this->m_iMatchStats_CashEarned.GetForModify( i ) = qmmPlayerData.m_iMatchStats_CashEarned[ i ];
  1501. this->m_iMatchStats_UtilityDamage.GetForModify( i ) = qmmPlayerData.m_iMatchStats_UtilityDamage[ i ];
  1502. this->m_iMatchStats_EnemiesFlashed.GetForModify( i ) = qmmPlayerData.m_iMatchStats_EnemiesFlashed[ i ];
  1503. }
  1504. // Store last known player name
  1505. Q_strncpy( qmmPlayerData.m_chPlayerName, this->GetPlayerName(), Q_ARRAYSIZE( qmmPlayerData.m_chPlayerName ) );
  1506. }
  1507. // In tournament mode force clan tags for the players according to the reservation
  1508. if ( CCSGameRules::sm_QueuedServerReservation.has_tournament_event() )
  1509. {
  1510. for ( int32 iTeam = 0; iTeam < CCSGameRules::sm_QueuedServerReservation.tournament_teams().size(); ++ iTeam )
  1511. {
  1512. TournamentTeam const &ttTeam = CCSGameRules::sm_QueuedServerReservation.tournament_teams( iTeam );
  1513. for ( int32 iTeamPlayer = 0; iTeamPlayer < ttTeam.players().size(); ++ iTeamPlayer )
  1514. {
  1515. TournamentPlayer const &ttPlayer = ttTeam.players( iTeamPlayer );
  1516. if ( ttPlayer.account_id() && ( ttPlayer.account_id() == m_uiAccountId ) )
  1517. {
  1518. // Set the clan tag and full team name
  1519. // SetClanTag( ttTeam.team_clantag().c_str() );
  1520. SetClanTag( "" );
  1521. SetClanName( ttTeam.team_name().c_str() );
  1522. // Set the player name as well
  1523. SetPlayerName( ttPlayer.player_nick().c_str() );
  1524. // break out of all loops
  1525. iTeam = CCSGameRules::sm_QueuedServerReservation.tournament_teams().size();
  1526. iTeamPlayer = ttTeam.players().size();
  1527. }
  1528. }
  1529. }
  1530. }
  1531. }
  1532. void CCSPlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data )
  1533. {
  1534. if ( CSGameRules()->IsLogoMap() )
  1535. return;
  1536. if ( CommandLine()->FindParm("-makedevshots" ) )
  1537. return;
  1538. BaseClass::ShowViewPortPanel( name, bShow, data );
  1539. }
  1540. void CCSPlayer::ClearFlashbangScreenFade( void )
  1541. {
  1542. if( IsBlind() )
  1543. {
  1544. color32 clr = { 0, 0, 0, 0 };
  1545. UTIL_ScreenFade( this, clr, 0.01, 0.0, FFADE_OUT | FFADE_PURGE );
  1546. m_flFlashDuration = 0.0f;
  1547. m_flFlashMaxAlpha = 255.0f;
  1548. }
  1549. // clear blind time (after screen fades are canceled )
  1550. m_blindUntilTime = 0.0f;
  1551. m_blindStartTime = 0.0f;
  1552. }
  1553. void CCSPlayer::GiveDefaultItems()
  1554. {
  1555. if ( State_Get() != STATE_ACTIVE )
  1556. return;
  1557. GiveDefaultWearables();
  1558. if ( CSGameRules()->IsPlayingCooperativeGametype() )
  1559. {
  1560. if ( IsBot() && GetTeamNumber() == TEAM_TERRORIST && CSGameRules()->IsPlayingCoopMission() )
  1561. {
  1562. RemoveAllItems( true );
  1563. CCSBot* pBot = static_cast< CCSBot* >( this );
  1564. if ( pBot )
  1565. {
  1566. SpawnPointCoopEnemy *pEnemySpawnSpot = pBot->GetLastCoopSpawnPoint();
  1567. if ( pEnemySpawnSpot )
  1568. {
  1569. int nArmor = pEnemySpawnSpot->GetArmorToSpawnWith();
  1570. if ( nArmor == 1 )
  1571. GiveNamedItem( "item_assaultsuit" );
  1572. else if ( nArmor == 2 )
  1573. GiveNamedItem( "item_heavyassaultsuit" );
  1574. const char *szWepsToGive = pEnemySpawnSpot->GetWeaponsToGive();
  1575. CUtlVector< char* > msgStrs;
  1576. V_SplitString( szWepsToGive, ",", msgStrs );
  1577. //run through and give all of the items specified
  1578. FOR_EACH_VEC( msgStrs, nCurrMsg )
  1579. {
  1580. char* szWeapon = msgStrs[ nCurrMsg ];
  1581. V_StripLeadingWhitespace( szWeapon );
  1582. V_StripTrailingWhitespace( szWeapon );
  1583. pBot->GiveWeapon( szWeapon );
  1584. }
  1585. }
  1586. }
  1587. // make sure they have a knife
  1588. CBaseCombatWeapon *knife = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  1589. if ( !knife )
  1590. {
  1591. GiveNamedItem( "weapon_knife_t" );
  1592. }
  1593. return;
  1594. }
  1595. else
  1596. {
  1597. // clear the player's items in case the round was lost
  1598. // due to the terrorists planting or CTs reaching the hostage
  1599. int nOtherTeam = CSGameRules()->IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  1600. if ( IsAlive() && !IsBot() && GetTeamNumber() != nOtherTeam )
  1601. RemoveAllItems( false );
  1602. if ( IsBot() && GetTeamNumber() == nOtherTeam )
  1603. CSGameRules()->GiveGuardianBotGrenades( this );
  1604. if ( CSGameRules()->IsPlayingCoopMission() )
  1605. {
  1606. for ( int i = 0; i < 3; i++ )
  1607. GiveNamedItem( "weapon_healthshot" );
  1608. }
  1609. }
  1610. }
  1611. if( CSGameRules()->IsBombDefuseMap() && mp_defuser_allocation.GetInt() == DefuserAllocation::All && GetTeamNumber() == TEAM_CT )
  1612. {
  1613. GiveDefuser( false );
  1614. }
  1615. if ( CSGameRules()->IsArmorFree() )
  1616. {
  1617. // Armor must be purchased in competitive mode
  1618. if ( CSGameRules()->IsPlayingCoopMission() )
  1619. GiveNamedItem( "item_heavyassaultsuit" );
  1620. else
  1621. GiveNamedItem( "item_assaultsuit" );
  1622. }
  1623. const char *pchTeamKnifeName = GetTeamNumber() == TEAM_TERRORIST ? "weapon_knife_t" : "weapon_knife";
  1624. // don't give default items if the player is in a training map or deathmatch- we control weapon giving in the map for training and in DM, the player could get a random weapon
  1625. if ( CSGameRules()->IsPlayingTraining() || CSGameRules()->IsPlayingGunGameDeathmatch() )
  1626. {
  1627. if ( CSGameRules()->IsPlayingGunGameDeathmatch() )
  1628. {
  1629. CBaseCombatWeapon *knife = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  1630. // if the player doesn't have something in the melee slot, give them a knife
  1631. if ( !knife )
  1632. GiveNamedItem( pchTeamKnifeName );
  1633. // if they don't have any pistol, give them the default pistol
  1634. if ( !Weapon_GetSlot( WEAPON_SLOT_PISTOL ) )
  1635. {
  1636. const char *secondaryString = NULL;
  1637. if ( GetTeamNumber() == TEAM_CT )
  1638. secondaryString = mp_ct_default_secondary.GetString();
  1639. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1640. secondaryString = mp_t_default_secondary.GetString();
  1641. CSWeaponID weaponId = WeaponIdFromString( secondaryString );
  1642. if ( weaponId )
  1643. {
  1644. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  1645. if ( pWeaponInfo && pWeaponInfo->GetWeaponType() == WEAPONTYPE_PISTOL )
  1646. {
  1647. GiveNamedItem( secondaryString );
  1648. m_bUsingDefaultPistol = true;
  1649. }
  1650. }
  1651. }
  1652. }
  1653. m_bPickedUpWeapon = false; // make sure this is set after getting default weapons
  1654. return;
  1655. }
  1656. if ( CSGameRules()->IsPlayingGunGameProgressive() || CSGameRules()->IsPlayingGunGameTRBomb() )
  1657. {
  1658. // Single Player Progressive Gun Game, so give the current weapon
  1659. GiveCurrentProgressiveGunGameWeapon();
  1660. // Give each player the knife as well if they don't have it already
  1661. if ( !Weapon_GetSlot( WEAPON_SLOT_KNIFE ) )
  1662. {
  1663. int thisWeaponID = CSGameRules()->GetCurrentGunGameWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  1664. if ( thisWeaponID != WEAPON_KNIFE_GG )
  1665. {
  1666. GiveNamedItem( pchTeamKnifeName );
  1667. }
  1668. }
  1669. // Award grenades in TR Bomb mode
  1670. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  1671. {
  1672. bool bGiveMolotov = false;
  1673. bool bGiveFlashbang = false;
  1674. bool bGiveHEGrenade = false;
  1675. bool bGiveIncendiary = false;
  1676. int nBonusGrenade = CSGameRules()->GetGunGameTRBonusGrenade( this );
  1677. if ( nBonusGrenade == WEAPON_MOLOTOV && !m_bGunGameTRModeHasMolotov )
  1678. {
  1679. // Award a molotov cocktail
  1680. bGiveMolotov = true;
  1681. m_bGunGameTRModeHasMolotov = true;
  1682. }
  1683. else if ( nBonusGrenade == WEAPON_INCGRENADE && !m_bGunGameTRModeHasIncendiary )
  1684. {
  1685. // Award an incendiary grenade
  1686. bGiveIncendiary = true;
  1687. m_bGunGameTRModeHasIncendiary = true;
  1688. }
  1689. else if ( nBonusGrenade == WEAPON_FLASHBANG && !m_bGunGameTRModeHasFlashbang )
  1690. {
  1691. // Award a flash grenade
  1692. bGiveFlashbang = true;
  1693. m_bGunGameTRModeHasFlashbang = true;
  1694. }
  1695. else if ( nBonusGrenade == WEAPON_HEGRENADE && !m_bGunGameTRModeHasHEGrenade )
  1696. {
  1697. // Award an he grenade
  1698. bGiveHEGrenade = true;
  1699. m_bGunGameTRModeHasHEGrenade = true;
  1700. }
  1701. // Give grenades as necessary based on flags since we want unused grenades to persist between rounds
  1702. if ( m_bGunGameTRModeHasMolotov && !HasWeaponOfType( WEAPON_MOLOTOV ) )
  1703. {
  1704. GiveWeaponFromID( WEAPON_MOLOTOV );
  1705. m_bGunGameTRModeHasMolotov = true;
  1706. }
  1707. if ( m_bGunGameTRModeHasIncendiary && !HasWeaponOfType( WEAPON_INCGRENADE ) )
  1708. {
  1709. GiveWeaponFromID( WEAPON_INCGRENADE );
  1710. m_bGunGameTRModeHasIncendiary = true;
  1711. }
  1712. if ( m_bGunGameTRModeHasFlashbang && !HasWeaponOfType( WEAPON_FLASHBANG ) )
  1713. {
  1714. GiveWeaponFromID( WEAPON_FLASHBANG );
  1715. m_bGunGameTRModeHasFlashbang = true;
  1716. }
  1717. if ( m_bGunGameTRModeHasHEGrenade && !HasWeaponOfType( WEAPON_HEGRENADE ) )
  1718. {
  1719. GiveWeaponFromID( WEAPON_HEGRENADE );
  1720. m_bGunGameTRModeHasHEGrenade = true;
  1721. }
  1722. if ( bGiveMolotov || bGiveFlashbang || bGiveHEGrenade || bGiveIncendiary)
  1723. {
  1724. IGameEvent * event = gameeventmanager->CreateEvent( "gg_bonus_grenade_achieved" );
  1725. if ( event )
  1726. {
  1727. event->SetInt( "userid", GetUserID() );
  1728. gameeventmanager->FireEvent( event );
  1729. }
  1730. //HintMessage( "BONUS GREANDE!", true, true );
  1731. }
  1732. }
  1733. return;
  1734. }
  1735. CBaseCombatWeapon *knife = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  1736. CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  1737. CBaseCombatWeapon *rifle = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  1738. //If the player has the knife, then they survived the previous round and need to display their current inventrory
  1739. //The only question is whether they got rid of their pistol.
  1740. if ( knife )
  1741. {
  1742. if ( pistol )
  1743. {
  1744. CSingleUserRecipientFilter filter(this );
  1745. filter.MakeReliable();
  1746. CCSUsrMsg_DisplayInventory msg;
  1747. msg.set_display( true );
  1748. msg.set_user_id( GetUserID() );
  1749. SendUserMessage( filter, CS_UM_DisplayInventory, msg );
  1750. return;
  1751. }
  1752. else
  1753. {
  1754. CSingleUserRecipientFilter filter(this );
  1755. filter.MakeReliable();
  1756. CCSUsrMsg_DisplayInventory msg;
  1757. msg.set_display( false );
  1758. msg.set_user_id( GetUserID() );
  1759. SendUserMessage( filter, CS_UM_DisplayInventory, msg );
  1760. }
  1761. }
  1762. m_bUsingDefaultPistol = true;
  1763. const char *meleeString = NULL;
  1764. if ( GetTeamNumber() == TEAM_CT )
  1765. meleeString = mp_ct_default_melee.GetString();
  1766. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1767. meleeString = mp_t_default_melee.GetString();
  1768. if ( meleeString && *meleeString )
  1769. {
  1770. // remove everything in the melee slot
  1771. while ( knife )
  1772. {
  1773. DestroyWeapon( knife );
  1774. knife = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  1775. }
  1776. // always give them a knife (mainly because we don't have animations to support no weapons)
  1777. GiveNamedItem( pchTeamKnifeName );
  1778. char token[256];
  1779. meleeString = engine->ParseFile( meleeString, token, sizeof( token ) );
  1780. while (meleeString != NULL )
  1781. {
  1782. // if it's not a knife, give it. This is pretty much only going to be a taser, but we support anything
  1783. if ( V_strcmp( token, "weapon_knife" ) )
  1784. {
  1785. CSWeaponID weaponId = WeaponIdFromString( token );
  1786. if ( weaponId )
  1787. {
  1788. //TODO: NEED ECON ITEM
  1789. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  1790. if ( pWeaponInfo && pWeaponInfo->GetWeaponType() == WEAPONTYPE_KNIFE )
  1791. {
  1792. GiveNamedItem( token );
  1793. }
  1794. }
  1795. }
  1796. meleeString = engine->ParseFile( meleeString, token, sizeof( token ) );
  1797. }
  1798. }
  1799. if ( !pistol )
  1800. {
  1801. const char *secondaryString = NULL;
  1802. if ( GetTeamNumber() == TEAM_CT )
  1803. secondaryString = mp_ct_default_secondary.GetString();
  1804. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1805. secondaryString = mp_t_default_secondary.GetString();
  1806. CSWeaponID weaponId = WeaponIdFromString( secondaryString );
  1807. if ( weaponId )
  1808. {
  1809. //TODO: NEED ECON ITEM
  1810. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  1811. if ( pWeaponInfo && pWeaponInfo->GetWeaponType() == WEAPONTYPE_PISTOL )
  1812. GiveNamedItem( secondaryString );
  1813. }
  1814. }
  1815. if ( !rifle )
  1816. {
  1817. const char *primaryString = NULL;
  1818. if ( GetTeamNumber() == TEAM_CT )
  1819. primaryString = mp_ct_default_primary.GetString();
  1820. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1821. primaryString = mp_t_default_primary.GetString();
  1822. CSWeaponID weaponId = WeaponIdFromString( primaryString );
  1823. if ( weaponId )
  1824. {
  1825. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  1826. if ( pWeaponInfo && pWeaponInfo->GetWeaponType() != WEAPONTYPE_KNIFE && pWeaponInfo->GetWeaponType() != WEAPONTYPE_PISTOL && pWeaponInfo->GetWeaponType() != WEAPONTYPE_C4 && pWeaponInfo->GetWeaponType() != WEAPONTYPE_GRENADE && pWeaponInfo->GetWeaponType() != WEAPONTYPE_EQUIPMENT )
  1827. GiveNamedItem( primaryString );
  1828. }
  1829. }
  1830. // give the player grenades if he needs them
  1831. const char *grenadeString = NULL;
  1832. if ( GetTeamNumber() == TEAM_CT )
  1833. grenadeString = mp_ct_default_grenades.GetString();
  1834. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1835. grenadeString = mp_t_default_grenades.GetString();
  1836. if ( grenadeString && *grenadeString )
  1837. {
  1838. char token[256];
  1839. grenadeString = engine->ParseFile( grenadeString, token, sizeof( token ) );
  1840. while ( grenadeString != NULL )
  1841. {
  1842. CSWeaponID weaponId = WeaponIdFromString( token );
  1843. if ( weaponId )
  1844. {
  1845. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  1846. if ( pWeaponInfo && pWeaponInfo->GetWeaponType() == WEAPONTYPE_GRENADE )
  1847. {
  1848. if ( !HasWeaponOfType( weaponId ) )
  1849. GiveNamedItem( token );
  1850. }
  1851. }
  1852. grenadeString = engine->ParseFile( grenadeString, token, sizeof( token ) );
  1853. }
  1854. }
  1855. if ( Weapon_GetSlot( WEAPON_SLOT_PISTOL ) )
  1856. {
  1857. Weapon_GetSlot( WEAPON_SLOT_PISTOL )->GiveReserveAmmo( AMMO_POSITION_PRIMARY, 250 );
  1858. }
  1859. if ( Weapon_GetSlot( WEAPON_SLOT_RIFLE ) )
  1860. {
  1861. Weapon_GetSlot( WEAPON_SLOT_RIFLE )->GiveReserveAmmo( AMMO_POSITION_PRIMARY, 250 );
  1862. }
  1863. m_bPickedUpWeapon = false; // make sure this is set after getting default weapons
  1864. if ( CSGameRules()->IsPlayingCoopMission() )
  1865. {
  1866. SelectItem( "weapon_healthshot" );
  1867. }
  1868. }
  1869. bool IsWearableEnabled( loadout_positions_t iSlot )
  1870. {
  1871. return iSlot == LOADOUT_POSITION_CLOTHING_HANDS;
  1872. }
  1873. void CCSPlayer::GiveDefaultWearables( void )
  1874. {
  1875. // Must a living human player on a team
  1876. if ( ( GetTeamNumber() != TEAM_CT && GetTeamNumber() != TEAM_TERRORIST ) )
  1877. return;
  1878. // Loop through our current wearables and ensure we're supposed to have them.
  1879. ValidateWearables();
  1880. if ( IsBot() || IsControllingBot() )
  1881. return;
  1882. // Now go through all our loadout slots and find any wearables that we should be wearing.
  1883. for ( int i = LOADOUT_POSITION_FIRST_COSMETIC; i <= LOADOUT_POSITION_LAST_COSMETIC; i++ )
  1884. {
  1885. m_EquippedLoadoutItemIndices[i] = LOADOUT_SLOT_USE_BASE_ITEM;
  1886. }
  1887. for ( int i = LOADOUT_POSITION_FIRST_COSMETIC; i <= LOADOUT_POSITION_LAST_COSMETIC; i++ )
  1888. {
  1889. if ( !IsWearableEnabled( ( loadout_positions_t ) i ) )
  1890. continue;
  1891. GiveWearableFromSlot( ( loadout_positions_t ) i );
  1892. }
  1893. }
  1894. void CCSPlayer::GiveWearableFromSlot( loadout_positions_t position )
  1895. {
  1896. /** Removed for partner depot **/
  1897. }
  1898. void CCSPlayer::SetClanTag( const char *pTag )
  1899. {
  1900. if ( pTag )
  1901. {
  1902. Q_strncpy( m_szClanTag, pTag, sizeof( m_szClanTag ) );
  1903. }
  1904. }
  1905. void CCSPlayer::SetClanName( const char *pName )
  1906. {
  1907. if ( pName )
  1908. {
  1909. Q_strncpy( m_szClanName, pName, sizeof( m_szClanName ) );
  1910. }
  1911. }
  1912. void CCSPlayer::InitTeammatePreferredColor()
  1913. {
  1914. const char *pColor = engine->GetClientConVarValue( entindex(), "cl_color" );
  1915. int nColor = atoi( pColor ); // convar.cpp code parses strings wierdly and cannot enforce range on a value e.g. " 4343" with leading space
  1916. // so we have to just assume that whatever string user supplied would parse as zero
  1917. if ( nColor >= 0 && nColor <= 4 )
  1918. {
  1919. SetTeammatePreferredColor( nColor );
  1920. }
  1921. else
  1922. {
  1923. SetTeammatePreferredColor( 0 );
  1924. }
  1925. }
  1926. void CCSPlayer::SetTeammatePreferredColor( int nColor )
  1927. {
  1928. if ( nColor < 0 || nColor > 4 )
  1929. {
  1930. AssertMsg( nColor >= 0 && nColor <= 4, "SetTeammatePreferredColor called with an invalid color (outside the range of 0 and 4)" );
  1931. return;
  1932. }
  1933. m_iTeammatePreferredColor = nColor;
  1934. }
  1935. void CCSPlayer::CreateRagdollEntity()
  1936. {
  1937. // If we already have a ragdoll, don't make another one.
  1938. CCSRagdoll *pRagdoll = dynamic_cast< CCSRagdoll* >( m_hRagdoll.Get() );
  1939. if ( !pRagdoll )
  1940. {
  1941. // create a new one
  1942. pRagdoll = dynamic_cast< CCSRagdoll* >( CreateEntityByName( "cs_ragdoll" ) );
  1943. }
  1944. if ( pRagdoll )
  1945. {
  1946. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  1947. pRagdoll->m_vecForce = m_vecTotalBulletForce;
  1948. pRagdoll->m_hPlayer = this;
  1949. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  1950. pRagdoll->m_nModelIndex = m_nModelIndex;
  1951. pRagdoll->m_nForceBone = m_nForceBone;
  1952. pRagdoll->m_iDeathPose = m_iDeathPose;
  1953. pRagdoll->m_iDeathFrame = m_iDeathFrame;
  1954. pRagdoll->m_flDeathYaw = m_flDeathYaw;
  1955. pRagdoll->m_flAbsYaw = GetAbsAngles()[YAW];
  1956. pRagdoll->Init();
  1957. }
  1958. // ragdolls will be removed on round restart automatically
  1959. m_hRagdoll = pRagdoll;
  1960. }
  1961. int CCSPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  1962. {
  1963. if ( m_bGunGameImmunity )
  1964. {
  1965. // No damage if immune
  1966. return 0;
  1967. }
  1968. // set damage type sustained
  1969. m_bitsDamageType |= info.GetDamageType();
  1970. if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
  1971. return 0;
  1972. // don't apply damage forces in CS
  1973. // fire global game event
  1974. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  1975. if ( event )
  1976. {
  1977. event->SetInt("userid", GetUserID() );
  1978. event->SetInt("health", Max(0, m_iHealth.Get() ) );
  1979. event->SetInt("armor", Max(0, ArmorValue() ) );
  1980. event->SetInt( "dmg_health", m_lastDamageHealth );
  1981. event->SetInt( "dmg_armor", m_lastDamageArmor );
  1982. if ( info.GetDamageType() & DMG_BLAST )
  1983. {
  1984. event->SetInt( "hitgroup", HITGROUP_GENERIC );
  1985. }
  1986. else
  1987. {
  1988. event->SetInt( "hitgroup", m_LastHitGroup );
  1989. }
  1990. CBaseEntity * attacker = info.GetAttacker();
  1991. const char *weaponName = "";
  1992. // FIXME[pmf]: this could break with dynamic weapons
  1993. if ( attacker->IsPlayer() )
  1994. {
  1995. CBasePlayer *player = ToBasePlayer( attacker );
  1996. event->SetInt("attacker", player->GetUserID() ); // hurt by other player
  1997. CBaseEntity *pInflictor = info.GetInflictor();
  1998. if ( pInflictor )
  1999. {
  2000. if ( pInflictor == player )
  2001. {
  2002. // If the inflictor is the killer, then it must be their current weapon doing the damage
  2003. if ( player->GetActiveWeapon() )
  2004. {
  2005. weaponName = player->GetActiveWeapon()->GetClassname();
  2006. }
  2007. }
  2008. else
  2009. {
  2010. weaponName = STRING( pInflictor->m_iClassname ); // it's just that easy
  2011. }
  2012. }
  2013. }
  2014. else
  2015. {
  2016. event->SetInt("attacker", 0 ); // hurt by "world"
  2017. }
  2018. if ( IsWeaponClassname( weaponName ) )
  2019. {
  2020. weaponName += WEAPON_CLASSNAME_PREFIX_LENGTH;
  2021. }
  2022. else if ( StringHasPrefixCaseSensitive( weaponName, "hegrenade" ) ) //"hegrenade_projectile"
  2023. {
  2024. // [tj] Handle grenade-surviving achievement
  2025. if ( IsOtherEnemy( info.GetAttacker()->entindex() ) )
  2026. {
  2027. m_grenadeDamageTakenThisRound += info.GetDamage();
  2028. }
  2029. weaponName = "hegrenade";
  2030. }
  2031. else if ( StringHasPrefixCaseSensitive( weaponName, "flashbang" ) ) //"flashbang_projectile"
  2032. {
  2033. weaponName = "flashbang";
  2034. }
  2035. else if ( StringHasPrefixCaseSensitive( weaponName, "smokegrenade" ) ) //"smokegrenade_projectile"
  2036. {
  2037. weaponName = "smokegrenade";
  2038. }
  2039. event->SetString( "weapon", weaponName );
  2040. event->SetInt( "priority", 6 ); // player_hurt
  2041. gameeventmanager->FireEvent( event );
  2042. }
  2043. return 1;
  2044. }
  2045. // Returns the % of the enemies this player killed in the round
  2046. int CCSPlayer::GetPercentageOfEnemyTeamKilled()
  2047. {
  2048. if ( m_NumEnemiesAtRoundStart > 0 )
  2049. {
  2050. if ( m_NumEnemiesKilledThisRound > m_NumEnemiesAtRoundStart )
  2051. {
  2052. DevMsg( "Invalid percentage of enemies killed\n" );
  2053. }
  2054. return RoundFloatToInt( (float )m_NumEnemiesKilledThisRound / (float)m_NumEnemiesAtRoundStart * 100.0f );
  2055. }
  2056. return 0;
  2057. }
  2058. void CCSPlayer::HandleOutOfAmmoKnifeKills( CCSPlayer* pAttackerPlayer, CWeaponCSBase* pAttackerWeapon )
  2059. {
  2060. if ( pAttackerWeapon &&
  2061. pAttackerWeapon->IsA( WEAPON_KNIFE ) )
  2062. {
  2063. // if they were out of ammo in their primary and secondary AND had a primary or secondary, log as an out of ammo knife kill
  2064. bool hasValidPrimaryOrSecondary = false; // can't really be out of ammo on anything if we don't have either a primary or a secondary
  2065. bool allPrimaryAndSecondariesOutOfAmmo = true;
  2066. if( pAttackerPlayer->HasPrimaryWeapon() )
  2067. {
  2068. hasValidPrimaryOrSecondary = true;
  2069. CBaseCombatWeapon *pWeapon = pAttackerPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  2070. if( !pWeapon || !pAttackerPlayer->DidPlayerEmptyAmmoForWeapon( pWeapon ) )
  2071. {
  2072. allPrimaryAndSecondariesOutOfAmmo = false;
  2073. }
  2074. }
  2075. if( pAttackerPlayer->HasSecondaryWeapon() )
  2076. {
  2077. hasValidPrimaryOrSecondary = true;
  2078. CBaseCombatWeapon *pWeapon = pAttackerPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2079. if( !pWeapon || !pAttackerPlayer->DidPlayerEmptyAmmoForWeapon( pWeapon ) )
  2080. {
  2081. allPrimaryAndSecondariesOutOfAmmo = false;
  2082. }
  2083. }
  2084. if( hasValidPrimaryOrSecondary && allPrimaryAndSecondariesOutOfAmmo )
  2085. {
  2086. pAttackerPlayer->IncrKnifeKillsWhenOutOfAmmo();
  2087. }
  2088. }
  2089. }
  2090. class CPlayerTrophy : public CPhysicsProp
  2091. {
  2092. public:
  2093. void OnTouchLoot( CBaseEntity *other )
  2094. {
  2095. /** Removed for partner depot **/
  2096. }
  2097. };
  2098. void CCSPlayer::Event_Killed( const CTakeDamageInfo &info )
  2099. {
  2100. SetKilledTime( gpGlobals->curtime );
  2101. // [pfreese] Process on-death achievements
  2102. ProcessPlayerDeathAchievements(ToCSPlayer(info.GetAttacker() ), this, info );
  2103. SetArmorValue( 0 );
  2104. m_bIsRespawningForDMBonus = false;
  2105. // [tj] Added a parameter so we know if it was death that caused the drop
  2106. // [menglish] Keep track of what the player has dropped for the freeze panel callouts
  2107. CBaseEntity* pAttacker = info.GetAttacker();
  2108. bool friendlyFire = pAttacker && IsOtherSameTeam( pAttacker->GetTeamNumber() ) && !IsOtherEnemy( pAttacker->entindex() );
  2109. CCSPlayer* pAttackerPlayer = ToCSPlayer( info.GetAttacker() );
  2110. if ( pAttackerPlayer )
  2111. {
  2112. CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >( info.GetWeapon() ); // this can be NULL if the kill is by HE/molly/impact/etc. (inflictor is non-NULL and points to grenade then)
  2113. if ( CSGameRules()->IsPlayingGunGameProgressive() && gg_knife_kill_demotes.GetBool() )
  2114. {
  2115. if ( pAttackerWeapon && pAttackerWeapon->IsA( WEAPON_KNIFE ) )
  2116. {
  2117. if ( IsOtherEnemy( pAttackerPlayer->entindex() ) ) // Don't demote a team member
  2118. {
  2119. // Killed by a knife, so drop one weapon class
  2120. SubtractProgressiveWeaponIndex();
  2121. CRecipientFilter filter;
  2122. filter.AddRecipient( this );
  2123. filter.MakeReliable();
  2124. CFmtStr fmtEntName( "#ENTNAME[%d]%s", pAttackerPlayer->entindex(), pAttackerPlayer->GetPlayerName() );
  2125. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#Cstrike_TitlesTXT_Hint_lost_a_level", fmtEntName.Access() );
  2126. // todo: play this sound: orch_hit_csharp_short?
  2127. }
  2128. }
  2129. }
  2130. // killed by a taser?
  2131. if ( pAttackerWeapon && pAttackerWeapon->IsA( WEAPON_TASER ) )
  2132. {
  2133. m_bKilledByTaser = true;
  2134. }
  2135. if ( pAttackerPlayer != this && cash_player_get_killed.GetInt() != 0 )
  2136. {
  2137. AddAccountAward( PlayerCashAward::RESPAWN );
  2138. }
  2139. HandleOutOfAmmoKnifeKills( pAttackerPlayer, pAttackerWeapon );
  2140. // here we figure out if the attacker saved another person
  2141. Vector forward;
  2142. AngleVectors( EyeAngles(), &forward, NULL, NULL);
  2143. CTeam *pAttackerTeam = GetGlobalTeam( pAttackerPlayer->GetTeamNumber() );
  2144. if ( pAttackerTeam && !( m_LastDamageType & DMG_FALL ) && !m_wasNotKilledNaturally )
  2145. {
  2146. for ( int iPlayer = 0; iPlayer < pAttackerTeam->GetNumPlayers(); iPlayer++ )
  2147. {
  2148. CCSPlayer *pPlayer = ToCSPlayer( pAttackerTeam->GetPlayer( iPlayer ) );
  2149. if ( !pPlayer || pAttackerPlayer == this || pPlayer == this || pPlayer == pAttackerPlayer )
  2150. continue;
  2151. if ( pAttackerPlayer->IsOtherEnemy( pPlayer->entindex() ) )
  2152. continue;
  2153. Assert( pPlayer->GetTeamNumber() == pAttackerTeam->GetTeamNumber() );
  2154. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  2155. {
  2156. Vector toAimSpot = pPlayer->EyePosition() - EyePosition();
  2157. toAimSpot.NormalizeInPlace();
  2158. float flKillerCone = DotProduct( toAimSpot, forward );
  2159. // aiming tolerance depends on how close the target is - closer targets subtend larger angles
  2160. float aimTolerance = 0.8f;
  2161. if ( flKillerCone >= aimTolerance )
  2162. {
  2163. // the target was aiming at this player, now do a quick trace to them to see if they could actually shoot them
  2164. trace_t result;
  2165. UTIL_TraceLine( EyePosition(), pPlayer->EyePosition(), MASK_SOLID, this, COLLISION_GROUP_NONE, &result );
  2166. if ( !result.m_pEnt || result.m_pEnt != pPlayer )
  2167. continue;
  2168. if ( GetActiveCSWeapon() )
  2169. {
  2170. // if they are holding a grenade or the c4, don't count it
  2171. if ( GetActiveCSWeapon()->GetWeaponType() == WEAPONTYPE_GRENADE || GetActiveCSWeapon()->GetWeaponType() == WEAPONTYPE_C4 )
  2172. continue;
  2173. Vector vecLength = (result.startpos - result.endpos);
  2174. // if they are holding a knife or taser, check knife range
  2175. if ( GetActiveCSWeapon()->GetWeaponType() == WEAPONTYPE_KNIFE && ( vecLength.Length() > 80.0f ) )
  2176. continue;
  2177. if ( GetActiveCSWeapon()->GetCSWeaponID() == WEAPON_TASER && ( vecLength.Length() > 200.0f ) )
  2178. continue;
  2179. }
  2180. // now make sure that the "saved" player wasn't looking directly at the guy who was killed
  2181. Vector vecAttackerFwd;
  2182. AngleVectors( pPlayer->EyeAngles(), &vecAttackerFwd, NULL, NULL);
  2183. Vector toKilledSpot = EyePosition() - pPlayer->EyePosition();
  2184. toKilledSpot.NormalizeInPlace();
  2185. float flKilledCone = DotProduct( toKilledSpot, vecAttackerFwd );
  2186. if ( flKilledCone < 0.65f )
  2187. {
  2188. // we got it! send a message to the "saved"
  2189. CSingleUserRecipientFilter usersaved( pPlayer );
  2190. usersaved.MakeReliable();
  2191. CFmtStr fmtEntName( "#ENTNAME[%d]%s", entindex(), GetPlayerName() );
  2192. UTIL_ClientPrintFilter( usersaved, HUD_PRINTTALK, "#Chat_SavePlayer_Saved",
  2193. CFmtStr( "#ENTNAME[%d]%s", pAttackerPlayer->entindex(), pAttackerPlayer->GetPlayerName() ),
  2194. CFmtStr( "#ENTNAME[%d]%s", entindex(), GetPlayerName() ) );
  2195. // now send a message to the "savior"
  2196. CSingleUserRecipientFilter usersavior( pAttackerPlayer );
  2197. usersavior.MakeReliable();
  2198. UTIL_ClientPrintFilter( usersavior, HUD_PRINTTALK, "#Chat_SavePlayer_Savior",
  2199. CFmtStr( "#ENTNAME[%d]%s", pPlayer->entindex(), pPlayer->GetPlayerName() ),
  2200. CFmtStr( "#ENTNAME[%d]%s", entindex(), GetPlayerName() ) );
  2201. // now send a message to the "savior"
  2202. CTeamRecipientFilter teamfilter( TEAM_SPECTATOR, true );
  2203. UTIL_ClientPrintFilter( teamfilter, HUD_PRINTTALK, "#Chat_SavePlayer_Spectator",
  2204. CFmtStr( "#ENTNAME[%d]%s", pAttackerPlayer->entindex(), pAttackerPlayer->GetPlayerName() ),
  2205. CFmtStr( "#ENTNAME[%d]%s", pPlayer->entindex(), pPlayer->GetPlayerName() ),
  2206. CFmtStr( "#ENTNAME[%d]%s", entindex(), GetPlayerName() ) );
  2207. break;
  2208. }
  2209. }
  2210. }
  2211. }
  2212. }
  2213. }
  2214. // if we died from killing ourself, check if we should lose a weapon in progressive
  2215. DecrementProgressiveWeaponFromSuicide();
  2216. //Only count the drop if it was not friendly fire
  2217. DropWeapons(true, !friendlyFire );
  2218. m_iNumFollowers = 0;
  2219. // Just in case the progress bar is on screen, kill it.
  2220. SetProgressBarTime( 0 );
  2221. m_bIsDefusing = false;
  2222. m_bIsGrabbingHostage = false;
  2223. m_bHasNightVision = false;
  2224. m_bNightVisionOn = false;
  2225. // [dwenger] Added for fun-fact support
  2226. m_bPickedUpDefuser = false;
  2227. m_bDefusedWithPickedUpKit = false;
  2228. m_bPickedUpWeapon = false;
  2229. m_bAttemptedDefusal = false;
  2230. m_nPreferredGrenadeDrop = 0;
  2231. m_bIsSpawning = false;
  2232. m_flDefusedBombWithThisTimeRemaining = 0;
  2233. m_bHasHelmet = false;
  2234. m_bHasHeavyArmor = false;
  2235. m_flFlashDuration = 0.0f;
  2236. if( FlashlightIsOn() )
  2237. FlashlightTurnOff();
  2238. // show killer in death cam mode
  2239. if( IsValidObserverTarget( info.GetAttacker() ) )
  2240. {
  2241. SetObserverTarget( info.GetAttacker() );
  2242. }
  2243. else
  2244. {
  2245. ResetObserverMode();
  2246. }
  2247. //update damage info with our accumulated physics force
  2248. CTakeDamageInfo subinfo = info;
  2249. subinfo.SetDamageForce( m_vecTotalBulletForce );
  2250. //Adrian: Select a death pose to extrapolate the ragdoll's velocity.
  2251. SelectDeathPose( info );
  2252. // See if there's a ragdoll magnet that should influence our force.
  2253. CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this );
  2254. if( pMagnet )
  2255. {
  2256. m_vecTotalBulletForce += pMagnet->GetForceVector( this );
  2257. }
  2258. // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
  2259. // because we still want to transmit to the clients in our PVS.
  2260. CreateRagdollEntity();
  2261. State_Transition( STATE_DEATH_ANIM ); // Transition into the dying state.
  2262. BaseClass::Event_Killed( subinfo );
  2263. if ( IsAssassinationTarget() )
  2264. {
  2265. CPlayerTrophy *pLoot = (CPlayerTrophy*)CreateEntityByName( "prop_physics" );
  2266. Vector vecDir = RandomVector( 0.0f, 25.0f );
  2267. pLoot->SetAbsOrigin( GetAbsOrigin() + vecDir + Vector( 0.f, 0.f, 35.f ) );
  2268. pLoot->KeyValue( "model", g_pszLootModelName );
  2269. pLoot->SetAbsVelocity( vecDir );
  2270. pLoot->SetLocalAngularVelocity( QAngle( RandomFloat( 50, 100 ), RandomFloat( 50, 100 ), RandomFloat( 50, 100 ) ) );
  2271. pLoot->Spawn();
  2272. pLoot->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE );
  2273. pLoot->SetTouch( &CPlayerTrophy::OnTouchLoot );
  2274. }
  2275. // [pfreese] If this kill ended the round, award the MVP to someone on the
  2276. // winning team.
  2277. // TODO - move this code somewhere else more MVP related
  2278. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  2279. {
  2280. // Lose all awarded grenades on death
  2281. m_bGunGameTRModeHasHEGrenade = false;
  2282. m_bGunGameTRModeHasFlashbang = false;
  2283. m_bGunGameTRModeHasMolotov = false;
  2284. m_bGunGameTRModeHasIncendiary = false;
  2285. }
  2286. if ( CSGameRules()->IsPlayingGunGame() )
  2287. {
  2288. RecordRebuyStructLastRound();
  2289. if ( pAttacker != this || !IsAbleToInstantRespawn() )
  2290. {
  2291. // Re-evaluate end-of-gun-game when a player is killed
  2292. if ( CSGameRules()->CheckWinConditions() )
  2293. {
  2294. m_bMadeFinalGunGameProgressiveKill = false;
  2295. // int nEntity2 = pAttackerPlayer ? pAttackerPlayer->entindex() : this->entindex();
  2296. // CSGameRules()->StartSlomoDeathCam( this->entindex(), nEntity2 );
  2297. }
  2298. }
  2299. }
  2300. else
  2301. {
  2302. if ( CSGameRules()->CheckWinConditions() )
  2303. {
  2304. // int nEntity2 = pAttackerPlayer ? pAttackerPlayer->entindex() : this->entindex();
  2305. // CSGameRules()->StartSlomoDeathCam( this->entindex(), nEntity2 );
  2306. }
  2307. }
  2308. SendLastKillerDamageToClient( pAttackerPlayer );
  2309. OutputDamageGiven();
  2310. OutputDamageTaken();
  2311. if ( m_bPunishedForTK )
  2312. {
  2313. m_bPunishedForTK = false;
  2314. HintMessage( "#Hint_cannot_play_because_tk", true, true );
  2315. }
  2316. if ( CSGameRules()->ShouldRecordMatchStats() )
  2317. {
  2318. m_iMatchStats_LiveTime.GetForModify( CSGameRules()->GetRoundsPlayed() ) += CSGameRules()->GetRoundElapsedTime();
  2319. // Keep track in QMM data
  2320. if ( m_uiAccountId && CSGameRules() )
  2321. {
  2322. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  2323. {
  2324. pQMM->m_iMatchStats_LiveTime[ CSGameRules()->GetRoundsPlayed() ] = m_iMatchStats_LiveTime.Get( CSGameRules()->GetRoundsPlayed() );
  2325. }
  2326. }
  2327. }
  2328. #if CS_CONTROLLABLE_BOTS_ENABLED
  2329. if ( IsControllingBot() ) // Should this be here, or at the top?
  2330. {
  2331. ReleaseControlOfBot();
  2332. }
  2333. #endif
  2334. if ( pAttackerPlayer &&
  2335. !CSGameRules()->IsWarmupPeriod() &&
  2336. CSGameRules()->IsPlayingCooperativeGametype() )
  2337. {
  2338. int nTeamCheck = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  2339. if ( CSGameRules()->IsPlayingCoopMission() )
  2340. nTeamCheck = TEAM_CT;
  2341. if ( pAttacker->GetTeamNumber() == nTeamCheck )
  2342. {
  2343. CWeaponCSBase* pSpecialWeapon = dynamic_cast< CWeaponCSBase * >( info.GetWeapon() ); // this can be NULL if the kill is by HE/molly/impact/etc. (inflictor is non-NULL and points to grenade then)
  2344. if ( CSGameRules()->CheckGotGuardianModeSpecialKill( pSpecialWeapon ) )
  2345. {
  2346. CSingleUserRecipientFilter filter( pAttackerPlayer );
  2347. EmitSound( filter, pAttackerPlayer->entindex(), "GunGameWeapon.ImpendingLevelUp" );
  2348. }
  2349. }
  2350. if ( GetTeamNumber() == nTeamCheck )
  2351. {
  2352. CBroadcastRecipientFilter filtermsg;
  2353. filtermsg.RemoveAllRecipients();
  2354. //play a sound here for all players on the player team to indicate that someone died
  2355. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  2356. {
  2357. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  2358. if ( pPlayer && pPlayer->GetTeamNumber() == nTeamCheck )
  2359. {
  2360. if ( pPlayer != this )
  2361. filtermsg.AddRecipient( pPlayer );
  2362. CSingleUserRecipientFilter filter( pPlayer );
  2363. pPlayer->EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.Demoted" );
  2364. }
  2365. }
  2366. UTIL_ClientPrintFilter( filtermsg, HUD_PRINTCENTER, "#SFUI_Notice_Guardian_PlayerHasDied", GetPlayerName() );
  2367. }
  2368. }
  2369. }
  2370. bool CCSPlayer::IsCloseToActiveBomb( void )
  2371. {
  2372. float bombCheckDistSq = AchievementConsts::KillEnemyNearBomb_MaxDistance * AchievementConsts::KillEnemyNearBomb_MaxDistance;
  2373. for ( int i=0; i < g_PlantedC4s.Count(); i++ )
  2374. {
  2375. CPlantedC4 *pC4 = g_PlantedC4s[i];
  2376. if ( pC4 && pC4->IsBombActive() )
  2377. {
  2378. Vector bombPos = pC4->GetAbsOrigin();
  2379. Vector distToBomb = this->GetAbsOrigin() - bombPos;
  2380. if ( distToBomb.LengthSqr() < bombCheckDistSq )
  2381. {
  2382. return true;
  2383. }
  2384. }
  2385. }
  2386. return false;
  2387. }
  2388. bool CCSPlayer::IsCloseToHostage( void )
  2389. {
  2390. float hostageCheckDistSq = AchievementConsts::KillEnemyNearHostage_MaxDistance * AchievementConsts::KillEnemyNearHostage_MaxDistance;
  2391. for ( int i=0; i < g_Hostages.Count(); i++ )
  2392. {
  2393. CHostage *pHostage = g_Hostages[i];
  2394. if ( pHostage && pHostage->IsValid() )
  2395. {
  2396. Vector hostagePos = pHostage->GetAbsOrigin();
  2397. Vector distToHostage = this->GetAbsOrigin() - hostagePos;
  2398. if ( distToHostage.LengthSqr() < hostageCheckDistSq )
  2399. {
  2400. return true;
  2401. }
  2402. }
  2403. }
  2404. return false;
  2405. }
  2406. bool CCSPlayer::IsObjectiveKill( CCSPlayer* pCSVictim )
  2407. {
  2408. // check all cases where this kill is 'objective' Based
  2409. // Killing someone close to a hostage
  2410. if ( ( pCSVictim && pCSVictim->IsCloseToHostage() ) ||
  2411. this->IsCloseToHostage() )
  2412. return true;
  2413. switch ( GetTeamNumber() )
  2414. {
  2415. case TEAM_TERRORIST:
  2416. // Terrorist kills CT in a bomb plant zone after a bomb is planted
  2417. if ( ( pCSVictim && pCSVictim->IsCloseToActiveBomb() )
  2418. || this->IsCloseToActiveBomb() )
  2419. return true;
  2420. // Terrorist kills hostage rescuer
  2421. if ( pCSVictim && ( pCSVictim->GetNumFollowers() > 0 ) )
  2422. return true;
  2423. break;
  2424. case TEAM_CT:
  2425. // killing someone WHILE guiding hostages
  2426. if ( this->GetNumFollowers() > 0 )
  2427. return true;
  2428. break;
  2429. }
  2430. return false;
  2431. }
  2432. // [menglish, tj] Update and check any one-off achievements based on the kill
  2433. // Notify that I've killed some entity. (called from Victim's Event_Killed ).
  2434. // (dkorus: This function appears to be misnamed, it also called for self kills/suicides )
  2435. void CCSPlayer::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )
  2436. {
  2437. BaseClass::Event_KilledOther(pVictim, info );
  2438. if ( !CSGameRules() )
  2439. return;
  2440. CCSPlayer *pCSVictim = ToCSPlayer( pVictim );
  2441. CCSPlayer *pCSAttacker = ToCSPlayer( info.GetAttacker() );
  2442. if ( pCSVictim == pCSAttacker )
  2443. {
  2444. // Bail if this was a suicide
  2445. return;
  2446. }
  2447. if ( IsOtherSameTeam( pVictim->GetTeamNumber() ) && !IsOtherEnemy( pCSVictim ) && pVictim != pCSAttacker )
  2448. {
  2449. CSGameRules()->ScorePlayerTeamKill( pCSAttacker );
  2450. UpdateTeamLeaderPlaySound( pCSAttacker->GetTeamNumber() );
  2451. }
  2452. else // on a different team from the attacker
  2453. {
  2454. // score kill
  2455. if ( pCSAttacker && pCSVictim &&
  2456. (pVictim->GetTeamNumber() == TEAM_CT || pVictim->GetTeamNumber() == TEAM_TERRORIST ) ) // this makes sure the victim is not a hostage before awarding a score
  2457. {
  2458. if ( pCSAttacker->IsObjectiveKill( pCSVictim ) )
  2459. CSGameRules()->ScorePlayerObjectiveKill( pCSAttacker );
  2460. else
  2461. CSGameRules()->ScorePlayerKill( pCSAttacker );
  2462. }
  2463. else if ( pCSAttacker && pVictim && StringHasPrefixCaseSensitive( pVictim->GetClassname(), "chicken" ) )
  2464. {
  2465. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase * >( info.GetWeapon() );
  2466. pCSAttacker->AddDeathmatchKillScore( 1, pWeapon ? pWeapon->GetCSWeaponID() : WEAPON_NONE,
  2467. pWeapon ? pWeapon->GetDefaultLoadoutSlot() : LOADOUT_POSITION_INVALID );
  2468. if ( IGameEvent *event = CSGameRules()->CreateWeaponKillGameEvent( "other_death", info ) )
  2469. {
  2470. event->SetInt("otherid", pVictim->entindex() );
  2471. event->SetString("othertype", "chicken" );
  2472. gameeventmanager->FireEvent( event );
  2473. }
  2474. m_NumChickensKilledThisSpawn++;
  2475. }
  2476. // Killed an enemy
  2477. if ( CSGameRules()->IsPlayingGunGame() && pCSVictim && pCSAttacker )
  2478. {
  2479. bool bBonus = false;
  2480. if ( !IsControllingBot() )
  2481. {
  2482. m_iNumGunGameKillsWithCurrentWeapon++;
  2483. }
  2484. IGameEvent * event = gameeventmanager->CreateEvent( "gg_killed_enemy" );
  2485. if ( event )
  2486. {
  2487. event->SetInt("victimid", pCSVictim->GetPlayerInfo()->GetUserID() );
  2488. event->SetInt("attackerid", pCSAttacker->GetPlayerInfo()->GetUserID() );
  2489. if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION )
  2490. {
  2491. event->SetInt( "dominated", 1 );
  2492. }
  2493. else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE )
  2494. {
  2495. event->SetInt( "revenge", 1 );
  2496. }
  2497. if ( CSGameRules()->IsPlayingGunGameDeathmatch() && pCSAttacker->GetActiveCSWeapon() &&
  2498. CSGameRules()->IsDMBonusActive() && ( CSGameRules()->GetDMBonusWeaponLoadoutSlot() == pCSAttacker->GetActiveCSWeapon()->GetDefaultLoadoutSlot() ) )
  2499. bBonus = true;
  2500. event->SetInt( "bonus", bBonus );
  2501. gameeventmanager->FireEvent( event );
  2502. }
  2503. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  2504. {
  2505. // don't award if controlling a bot or in the warmup round
  2506. if ( !IsControllingBot() && !CSGameRules()->IsWarmupPeriod() )
  2507. {
  2508. // Don't allow kill points for upgrades after a round has ended - (with some slack)
  2509. // also don't upgrade if we hit the last round before half-time
  2510. if ( CSGameRules()->GetRoundRestartTime() + 0.5f < gpGlobals->curtime && !CSGameRules()->IsLastRoundBeforeHalfTime() )
  2511. {
  2512. // Bump up the count of how many kills this player has in the current round
  2513. m_iNumGunGameTRKillPoints++;
  2514. m_iNumGunGameTRBombTotalPoints++;
  2515. ConVarRef mp_ggtr_bomb_pts_for_upgrade( "mp_ggtr_bomb_pts_for_upgrade" );
  2516. if ( m_iNumGunGameTRKillPoints == mp_ggtr_bomb_pts_for_upgrade.GetInt() || CSGameRules()->GetGunGameTRBonusGrenade( this ) > 0 )
  2517. {
  2518. // Play client sound for impending weapon upgrade...
  2519. SendGunGameWeaponUpgradeAlert();
  2520. }
  2521. }
  2522. }
  2523. // Test for end of round for gun game TR (ensure that the weapon that made the kill is the one that matches the last weapon in the progression )
  2524. CWeaponCSBase* pWeapon = dynamic_cast<CWeaponCSBase *>( info.GetWeapon() );
  2525. CEconItemView* pItem = pWeapon ? pWeapon->GetEconItemView() : NULL;
  2526. int wID = WEAPON_NONE;
  2527. if ( pItem != NULL )
  2528. {
  2529. wID = pItem->GetItemIndex();
  2530. }
  2531. else
  2532. {
  2533. const CCSWeaponInfo* pDamageWeaponInfo = GetWeaponInfoFromDamageInfo( info );
  2534. wID = pDamageWeaponInfo ? pDamageWeaponInfo->m_weaponId : WEAPON_NONE;
  2535. }
  2536. int curwID = CSGameRules()->GetCurrentGunGameWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  2537. if ( wID == curwID && CSGameRules()->IsFinalGunGameProgressiveWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2538. {
  2539. // Determine if current # kills with this weapon >= # kills necessary to level up the weapon
  2540. if ( m_iNumGunGameKillsWithCurrentWeapon >= CSGameRules()->GetGunGameNumKillsRequiredForWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2541. {
  2542. // Made the proper number of kills with the final weapon so record this fact
  2543. m_bMadeFinalGunGameProgressiveKill = true;
  2544. }
  2545. }
  2546. }
  2547. bool bKilledLeader = false;
  2548. //bool bUpgradedWeapon = false;
  2549. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  2550. {
  2551. if ( pCSVictim != pCSAttacker )
  2552. {
  2553. // Test for end of round
  2554. if ( CSGameRules()->IsFinalGunGameProgressiveWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2555. {
  2556. // Determine if current # kills with this weapon == # kills necessary to level up the weapon
  2557. if ( m_iNumGunGameKillsWithCurrentWeapon == CSGameRules()->GetGunGameNumKillsRequiredForWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2558. {
  2559. // Made the proper number of kills with the final weapon so record this fact
  2560. m_bMadeFinalGunGameProgressiveKill = true;
  2561. }
  2562. }
  2563. else
  2564. {
  2565. int nOtherTeam = pCSVictim->GetTeamNumber();
  2566. bool bIsCurrentLeader = entindex() == GetGlobalTeam( GetTeamNumber() )->GetGGLeader( GetTeamNumber() );
  2567. bKilledLeader = pCSVictim->entindex() == GetGlobalTeam( nOtherTeam )->GetGGLeader( nOtherTeam );
  2568. // send e message that you killed the enemy leader
  2569. if ( bKilledLeader && !bIsCurrentLeader )
  2570. ClientPrint( this, HUD_PRINTCENTER, "#Player_Killed_Enemy_Leader" );
  2571. bool bWithKnife = false;
  2572. if ( pCSAttacker == this )
  2573. {
  2574. CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >( GetActiveWeapon() );
  2575. if ( pAttackerWeapon && pAttackerWeapon->IsA( WEAPON_KNIFE ) )
  2576. bWithKnife = true;
  2577. }
  2578. // Determine if current # kills with this weapon == # kills necessary to level up the weapon
  2579. if ( bWithKnife || (!bIsCurrentLeader && bKilledLeader) || m_iNumGunGameKillsWithCurrentWeapon == CSGameRules()->GetGunGameNumKillsRequiredForWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2580. {
  2581. // Reset kill count with respect to new weapon
  2582. m_iNumGunGameKillsWithCurrentWeapon = 0;
  2583. m_iNumRoundKills = 0;
  2584. m_iNumRoundKillsHeadshots = 0;
  2585. m_iNumRoundTKs = 0;
  2586. CSingleUserRecipientFilter filter( this );
  2587. //bUpgradedWeapon = true;
  2588. // emit the level up sound here because emily wants them
  2589. // to overlap with the leader acquisition sound
  2590. EmitSound( filter, entindex(), "UI.ArmsRace.LevelUp" );
  2591. // Single Player Progressive Gun Game, so give the next weapon
  2592. GiveNextProgressiveGunGameWeapon();
  2593. // if ( CSGameRules()->IsPlayingGunGameProgressive() )
  2594. // {
  2595. // int nRequiredKills = CSGameRules()->GetGunGameNumKillsRequiredForWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  2596. // if ( m_iNumGunGameKillsWithCurrentWeapon >= nRequiredKills || m_iNumRoundKills >= nRequiredKills )
  2597. // {
  2598. // // if we hit the max for this weapon and
  2599. //
  2600. // }
  2601. // }
  2602. // Alert everyone that this player has the final ggp weapon
  2603. if ( CSGameRules()->IsFinalGunGameProgressiveWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  2604. {
  2605. IGameEvent * eventGGFinalWeap = gameeventmanager->CreateEvent( "gg_final_weapon_achieved" );
  2606. // Change the UI to reflect gun-game last weapon level reached
  2607. //ConVarRef sf_ui_tint( "sf_ui_tint" );
  2608. //sf_ui_tint.SetValue( g_KnifeLevelTint );
  2609. if ( eventGGFinalWeap )
  2610. {
  2611. //CCSPlayer *pCSAchiever = ( CCSPlayer* )( info.GetAttacker() );
  2612. eventGGFinalWeap->SetInt("playerid", pCSAttacker ? pCSAttacker->GetPlayerInfo()->GetUserID() : -1 );
  2613. gameeventmanager->FireEvent( eventGGFinalWeap );
  2614. CRecipientFilter filterKnifeLevelNotification;
  2615. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2616. {
  2617. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  2618. if ( pPlayer && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) )
  2619. {
  2620. filterKnifeLevelNotification.AddRecipient( pPlayer );
  2621. }
  2622. }
  2623. filterKnifeLevelNotification.MakeReliable();
  2624. CFmtStr fmtEntName;
  2625. if ( pCSAttacker )
  2626. fmtEntName.AppendFormat( "#ENTNAME[%d]%s", pCSAttacker->entindex(), pCSAttacker->GetPlayerName() );
  2627. UTIL_ClientPrintFilter( filterKnifeLevelNotification, HUD_PRINTTALK, "#SFUI_Notice_Knife_Level", fmtEntName.Access() );
  2628. }
  2629. }
  2630. }
  2631. }
  2632. }
  2633. }
  2634. if ( this == pCSAttacker )
  2635. {
  2636. // force a sort right now
  2637. GetGlobalTeam( GetTeamNumber() )->DetermineGGLeaderAndSort();
  2638. bool bPlayedLeaderSound = UpdateTeamLeaderPlaySound( GetTeamNumber() );
  2639. if ( !bPlayedLeaderSound )
  2640. {
  2641. int nConsecutiveKills = CCS_GameStats.FindPlayerStats( pCSVictim ).statsKills.iNumKilledByUnanswered[entindex()];
  2642. CSingleUserRecipientFilter filter( this );
  2643. // check if we got bonus points first and play that sound
  2644. if ( 0/*bKilledLeader*/ )
  2645. {
  2646. EmitSound( filter, entindex(), "UI.DeathMatchBonusKill" );
  2647. }
  2648. // else if ( bUpgradedWeapon )
  2649. // {
  2650. // EmitSound( filter, entindex(), "UI.ArmsRace.LevelUp" );
  2651. // }
  2652. else if ( bBonus )
  2653. {
  2654. EmitSound( filter, entindex(), "UI.DeathMatchBonusKill" );
  2655. }
  2656. else if ( CSGameRules()->IsPlayingGunGameProgressive() == false )
  2657. {
  2658. if ( nConsecutiveKills < 2 )
  2659. {
  2660. //EmitSound( filter, entindex(), "Music.Kill_01" );
  2661. }
  2662. else if ( nConsecutiveKills < 3 )
  2663. {
  2664. //EmitSound( filter, entindex(), "Music.Kill_02" );
  2665. }
  2666. else if ( nConsecutiveKills < 4 )
  2667. {
  2668. //EmitSound( filter, entindex(), "Music.Kill_03" );
  2669. }
  2670. }
  2671. }
  2672. }
  2673. }
  2674. if ( pCSVictim && ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION || IsPlayerDominated( pCSVictim->entindex() ) ) )
  2675. {
  2676. m_hDominateEffectPlayer = pCSVictim;
  2677. m_flDominateEffectDelayTime = gpGlobals->curtime + 1.0f;
  2678. }
  2679. }
  2680. }
  2681. void CCSPlayer::GiveCurrentProgressiveGunGameWeapon( void )
  2682. {
  2683. int currentWeaponID = CSGameRules()->GetCurrentGunGameWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  2684. if ( currentWeaponID != -1 )
  2685. {
  2686. // Drop the current pistol
  2687. CBaseCombatWeapon *pWeapon;
  2688. pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2689. DestroyWeapon( pWeapon );
  2690. // Drop the current rifle
  2691. pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  2692. DestroyWeapon( pWeapon );
  2693. // Assign the weapon
  2694. GiveWeaponFromID( currentWeaponID );
  2695. }
  2696. // Tell game rules to recalculate the current highest gun game index
  2697. CSGameRules()->CalculateMaxGunGameProgressiveWeaponIndex();
  2698. }
  2699. void CCSPlayer::IncrementGunGameProgressiveWeapon( int nNumLevelsToIncrease )
  2700. {
  2701. int newWeaponIndex = m_iGunGameProgressiveWeaponIndex + nNumLevelsToIncrease - 1;
  2702. if ( newWeaponIndex >= CSGameRules()->GetNumProgressiveGunGameWeapons( GetTeamNumber() ) - 1 )
  2703. {
  2704. // Clamp weapon index to 2nd to last index
  2705. newWeaponIndex = CSGameRules()->GetNumProgressiveGunGameWeapons( GetTeamNumber() ) - 2;
  2706. }
  2707. if ( newWeaponIndex >= m_iGunGameProgressiveWeaponIndex )
  2708. {
  2709. // Bump up the player's weapon level to the new weapon
  2710. m_iGunGameProgressiveWeaponIndex = newWeaponIndex;
  2711. GiveNextProgressiveGunGameWeapon();
  2712. }
  2713. }
  2714. void CCSPlayer::GiveNextProgressiveGunGameWeapon( void )
  2715. {
  2716. int nextWeaponID = CSGameRules()->GetNextGunGameWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  2717. if ( nextWeaponID != -1 )
  2718. {
  2719. // Assign the next weapon to use
  2720. m_iGunGameProgressiveWeaponIndex++;
  2721. // Drop the current pistol
  2722. CBaseCombatWeapon *pWeapon;
  2723. pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  2724. DestroyWeapon( pWeapon );
  2725. // Drop the current rifle
  2726. pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  2727. DestroyWeapon( pWeapon );
  2728. if ( nextWeaponID == WEAPON_KNIFE || nextWeaponID == WEAPON_KNIFE_GG )
  2729. {
  2730. // Drop the knife so that when we re-give it it will be primary
  2731. pWeapon = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  2732. DestroyWeapon( pWeapon );
  2733. }
  2734. // Assign the new weapon
  2735. GiveWeaponFromID( nextWeaponID );
  2736. // Send a game event for leveling up
  2737. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  2738. {
  2739. IGameEvent *event = gameeventmanager->CreateEvent( "ggtr_player_levelup" );
  2740. if ( event )
  2741. {
  2742. const char* szName = WeaponIdAsString( static_cast<CSWeaponID>(nextWeaponID ) );
  2743. event->SetInt( "userid", GetUserID() );
  2744. event->SetInt( "weaponrank", m_iGunGameProgressiveWeaponIndex );
  2745. event->SetString( "weaponname", szName );
  2746. gameeventmanager->FireEvent( event );
  2747. }
  2748. }
  2749. else if ( CSGameRules()->IsPlayingGunGameProgressive() )
  2750. {
  2751. IGameEvent *event = gameeventmanager->CreateEvent( "ggprogressive_player_levelup" );
  2752. if ( event )
  2753. {
  2754. const char* szName = WeaponIdAsString( static_cast<CSWeaponID>(nextWeaponID ) );
  2755. event->SetInt( "userid", GetUserID() );
  2756. event->SetInt( "weaponrank", m_iGunGameProgressiveWeaponIndex );
  2757. event->SetString( "weaponname", szName );
  2758. gameeventmanager->FireEvent( event );
  2759. }
  2760. }
  2761. UpdateLeader();
  2762. // Tell game rules to recalculate the current highest gun game index
  2763. CSGameRules()->CalculateMaxGunGameProgressiveWeaponIndex();
  2764. }
  2765. }
  2766. void CCSPlayer::SubtractProgressiveWeaponIndex( void )
  2767. {
  2768. int previousWeaponID = CSGameRules()->GetPreviousGunGameWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() );
  2769. if ( previousWeaponID != -1 )
  2770. {
  2771. // Assign the previous weapon to use
  2772. m_iGunGameProgressiveWeaponIndex--;
  2773. // clear the number of kills with the current weapon
  2774. m_iNumGunGameKillsWithCurrentWeapon = 0;
  2775. // Destroy the gold knife if we are on that level so we get the regular knife when we spawn
  2776. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_KNIFE );
  2777. if ( pWeapon && pWeapon->GetWeaponID() == WEAPON_KNIFE_GG )
  2778. {
  2779. DestroyWeapon( pWeapon );
  2780. }
  2781. }
  2782. }
  2783. void CCSPlayer::GiveWeaponFromID ( int nWeaponID )
  2784. {
  2785. const char *pchClassName = NULL;
  2786. const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinition( nWeaponID );
  2787. if ( pDef && pDef->GetDefinitionIndex() != 0 )
  2788. {
  2789. pchClassName = pDef->GetDefinitionName();
  2790. }
  2791. else
  2792. {
  2793. pchClassName = WeaponIdAsString( (CSWeaponID)nWeaponID );
  2794. }
  2795. if ( !pchClassName )
  2796. return;
  2797. GiveNamedItem( pchClassName );
  2798. }
  2799. void CCSPlayer::DeathSound( const CTakeDamageInfo &info )
  2800. {
  2801. if( m_LastHitGroup == HITGROUP_HEAD )
  2802. {
  2803. EmitSound( "Player.DeathHeadShot" );
  2804. }
  2805. else
  2806. {
  2807. EmitSound( "Player.Death" );
  2808. }
  2809. }
  2810. //-----------------------------------------------------------------------------
  2811. // Purpose:
  2812. //-----------------------------------------------------------------------------
  2813. void CCSPlayer::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
  2814. {
  2815. if ( !cs_enable_player_physics_box.GetBool() )
  2816. {
  2817. VPhysicsDestroyObject();
  2818. return;
  2819. }
  2820. BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );
  2821. if ( sv_turbophysics.GetBool() )
  2822. return;
  2823. // Setup the HL2 specific callback.
  2824. GetPhysicsController()->SetEventHandler( &playerCallback );
  2825. }
  2826. void CCSPlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
  2827. {
  2828. if ( !CanMove() )
  2829. return;
  2830. BaseClass::VPhysicsShadowUpdate( pPhysics );
  2831. }
  2832. bool CCSPlayer::HasShield() const
  2833. {
  2834. #ifdef CS_SHIELD_ENABLED
  2835. return m_bHasShield;
  2836. #else
  2837. return false;
  2838. #endif
  2839. }
  2840. bool CCSPlayer::IsShieldDrawn() const
  2841. {
  2842. #ifdef CS_SHIELD_ENABLED
  2843. return m_bShieldDrawn;
  2844. #else
  2845. return false;
  2846. #endif
  2847. }
  2848. void CCSPlayer::SprayPaint( CCSUsrMsg_PlayerDecalDigitalSignature const &msg )
  2849. {
  2850. if ( !m_uiAccountId ) return;
  2851. if ( !CSGameRules() ) return;
  2852. if ( !msg.has_data() ) return;
  2853. if ( !msg.data().trace_id() ) return;
  2854. if ( !msg.data().endpos().size() )
  2855. {
  2856. //
  2857. // This is user's initial request for traces to be performed
  2858. //
  2859. if ( gpGlobals->curtime < m_flNextDecalTime ) return;
  2860. PushAwayDecalPaintingTime( 0.2f ); // prevent trace spamming
  2861. if ( int( msg.data().equipslot() ) < 0 ) return;
  2862. if ( msg.data().equipslot() >= Q_ARRAYSIZE( m_unEquippedPlayerSprayIDs ) ) return;
  2863. if ( !m_unEquippedPlayerSprayIDs[ msg.data().equipslot() ] ) return;
  2864. // relax the server-side check, clients should just have a spray equipped and client message will
  2865. // be authoritative about spraying
  2866. // if ( m_unEquippedPlayerSprayIDs[ msg.data().equipslot() ] != msg.data().tx_defidx() ) return;
  2867. if ( fabs( gpGlobals->curtime - msg.data().creationtime() ) > 10 ) return;
  2868. //
  2869. // Perform all the traces to validate spray application
  2870. //
  2871. trace_t tr;
  2872. Vector forward, right;
  2873. if ( char const *szError = IsAbleToApplySpray( &tr, &forward, &right ) )
  2874. {
  2875. ClientPrint( this, HUD_PRINTCENTER, szError );
  2876. return;
  2877. }
  2878. // line hit something, so paint a decal
  2879. PushAwayDecalPaintingTime( PLAYERDECALS_COOLDOWN_SECONDS );
  2880. //
  2881. // Ask the user to create a digital signature for this decal before it can be applied in the world
  2882. //
  2883. CCSGameRules::ServerPlayerDecalData_t data;
  2884. data.m_unAccountID = m_uiAccountId;
  2885. data.m_nTraceID = msg.data().trace_id();
  2886. data.m_vecOrigin = tr.endpos;
  2887. data.m_vecRight = right;
  2888. data.m_vecNormal = tr.plane.normal;
  2889. data.m_nEquipSlot = msg.data().equipslot();
  2890. data.m_nPlayer = msg.data().tx_defidx();
  2891. data.m_nEntity = tr.m_pEnt->entindex();
  2892. data.m_nHitbox = tr.hitbox;
  2893. data.m_nTintID = msg.data().tint_id();
  2894. data.m_flCreationTime = gpGlobals->curtime;
  2895. data.m_rtGcTime = msg.data().rtime();
  2896. // Always pretend the player was standing directly in front of the surface they hit.
  2897. // Matches code in QcCreatePreviewDecal, update that if you touch this.
  2898. data.m_vecStart = tr.endpos + tr.plane.normal;
  2899. CSGameRules()->m_arrServerPlayerDecalData.AddToTail( data );
  2900. CSingleUserRecipientFilter filter( this );
  2901. CCSUsrMsg_PlayerDecalDigitalSignature askuser;
  2902. data.CopyToMsg( askuser );
  2903. SendUserMessage( filter, CS_UM_PlayerDecalDigitalSignature, askuser );
  2904. CBroadcastRecipientFilter sprayCanFilter;
  2905. EmitSound( sprayCanFilter, entindex(), "SprayCan.Shake" ); // start playing the spray sound when the server confirms the trace
  2906. // it may take multiple seconds for the GC digital signature to come through from the client, so we'll
  2907. // play another spray sound when we actually apply the spray decal image
  2908. }
  2909. else
  2910. {
  2911. // We should have this user's request in the vector
  2912. CCSGameRules::ServerPlayerDecalData_t data;
  2913. data.m_unAccountID = m_uiAccountId;
  2914. data.InitFromMsg( msg );
  2915. {
  2916. int idx = CSGameRules()->m_arrServerPlayerDecalData.Find( data );
  2917. if ( idx < 0 )
  2918. return;
  2919. CSGameRules()->m_arrServerPlayerDecalData.FastRemove( idx );
  2920. }
  2921. // Verify the signature of the user's spray paint
  2922. #ifdef _DEBUG
  2923. {
  2924. float flendpos[3] = { msg.data().endpos( 0 ), msg.data().endpos( 1 ), msg.data().endpos( 2 ) };
  2925. float flstartpos[3] = { msg.data().startpos( 0 ), msg.data().startpos( 1 ), msg.data().startpos( 2 ) };
  2926. float flright[3] = { msg.data().right( 0 ), msg.data().right( 1 ), msg.data().right( 2 ) };
  2927. float flnormal[3] = { msg.data().normal( 0 ), msg.data().normal( 1 ), msg.data().normal( 2 ) };
  2928. DevMsg( "Server signature #%u e(%08X,%08X,%08X) s(%08X,%08X,%08X) r(%08X,%08X,%08X) n(%08X,%08X,%08X)\n", data.m_nTraceID,
  2929. *reinterpret_cast< uint32 * >( &flendpos[0] ), *reinterpret_cast< uint32 * >( &flendpos[1] ), *reinterpret_cast< uint32 * >( &flendpos[2] ),
  2930. *reinterpret_cast< uint32 * >( &flstartpos[0] ), *reinterpret_cast< uint32 * >( &flstartpos[1] ), *reinterpret_cast< uint32 * >( &flstartpos[2] ),
  2931. *reinterpret_cast< uint32 * >( &flright[0] ), *reinterpret_cast< uint32 * >( &flright[1] ), *reinterpret_cast< uint32 * >( &flright[2] ),
  2932. *reinterpret_cast< uint32 * >( &flnormal[0] ), *reinterpret_cast< uint32 * >( &flnormal[1] ), *reinterpret_cast< uint32 * >( &flnormal[2] )
  2933. );
  2934. }
  2935. #endif
  2936. if ( !BValidateClientPlayerDecalSignature( msg.data() ) )
  2937. return;
  2938. // Follow through and apply the decal
  2939. CBroadcastRecipientFilter sprayCanFilter;
  2940. EmitSound( sprayCanFilter, entindex(), "SprayCan.Paint" );
  2941. extern void FE_PlayerDecal( CCSGameRules::ServerPlayerDecalData_t const &data, std::string const &signature );
  2942. FE_PlayerDecal( data, msg.data().signature() );
  2943. }
  2944. // Expire any digital signature requests that are too old
  2945. CUtlVector< CCSGameRules::ServerPlayerDecalData_t > &arrData = CSGameRules()->m_arrServerPlayerDecalData;
  2946. FOR_EACH_VEC( arrData, i )
  2947. { // everything that is 5+ minutes old will never come back
  2948. if ( arrData[ i ].m_flCreationTime + 5 * 60 < gpGlobals->curtime )
  2949. arrData.FastRemove( i-- );
  2950. }
  2951. //
  2952. // old-style via TempEnts system
  2953. // UTIL_PlayerDecalTrace( &tr, right, ( ( entindex() & 0x7F ) << 24 ) | uint32( unEquippedPlayerSprayID ) );
  2954. // must be removed!
  2955. }
  2956. void CCSGameRules::ServerPlayerDecalData_t::CopyToMsg( CCSUsrMsg_PlayerDecalDigitalSignature &msg ) const
  2957. {
  2958. PlayerDecalDigitalSignature &ddata = *msg.mutable_data();
  2959. ddata.set_accountid( m_unAccountID );
  2960. ddata.set_trace_id( m_nTraceID );
  2961. ddata.set_rtime( m_rtGcTime );
  2962. for ( int i = 0; i < 3; ++ i ) ddata.add_endpos( m_vecOrigin[i] );
  2963. for ( int i = 0; i < 3; ++ i ) ddata.add_startpos( m_vecStart[i] );
  2964. for ( int i = 0; i < 3; ++ i ) ddata.add_right( m_vecRight[i] );
  2965. for ( int i = 0; i < 3; ++ i ) ddata.add_normal( m_vecNormal[i] );
  2966. ddata.set_equipslot( m_nEquipSlot );
  2967. ddata.set_tx_defidx( m_nPlayer );
  2968. ddata.set_entindex( m_nEntity );
  2969. ddata.set_hitbox( m_nHitbox );
  2970. ddata.set_tint_id( m_nTintID );
  2971. ddata.set_creationtime( m_flCreationTime );
  2972. }
  2973. void CCSGameRules::ServerPlayerDecalData_t::InitFromMsg( CCSUsrMsg_PlayerDecalDigitalSignature const &msg )
  2974. {
  2975. PlayerDecalDigitalSignature const &ddata = msg.data();
  2976. m_unAccountID = ddata.accountid();
  2977. m_nTraceID = ddata.trace_id();
  2978. m_rtGcTime = ddata.rtime();
  2979. if ( ddata.endpos_size() == 3 ) m_vecOrigin.Init( ddata.endpos( 0 ), ddata.endpos( 1 ), ddata.endpos( 2 ) );
  2980. if ( ddata.startpos_size() == 3 ) m_vecStart.Init( ddata.startpos( 0 ), ddata.startpos( 1 ), ddata.startpos( 2 ) );
  2981. if ( ddata.right_size() == 3 ) m_vecRight.Init( ddata.right( 0 ), ddata.right( 1 ), ddata.right( 2 ) );
  2982. if ( ddata.normal_size() == 3 ) m_vecNormal.Init( ddata.normal( 0 ), ddata.normal( 1 ), ddata.normal( 2 ) );
  2983. m_nEquipSlot = ddata.equipslot();
  2984. m_nPlayer = ddata.tx_defidx();
  2985. m_nEntity = ddata.entindex();
  2986. m_nHitbox = ddata.hitbox();
  2987. m_nTintID = ddata.tint_id();
  2988. m_flCreationTime = ddata.creationtime();
  2989. }
  2990. void CCSPlayer::ImpulseCommands()
  2991. {
  2992. int iImpulse = ( int ) m_nImpulse;
  2993. switch ( iImpulse )
  2994. {
  2995. #if !CSTRIKE_BETA_BUILD || defined ( _DEBUG )
  2996. case 111: // CS:GO has 4 sprays
  2997. case 112:
  2998. case 113:
  2999. case 114:
  3000. // If this player doesn't have a spraycan equipped then swallow the spray request (this probably doesn't work)
  3001. if ( sv_cheats->GetBool() )
  3002. {
  3003. CCSUsrMsg_PlayerDecalDigitalSignature msg;
  3004. msg.mutable_data()->set_equipslot( iImpulse - 111 );
  3005. SprayPaint( msg );
  3006. }
  3007. break;
  3008. #endif
  3009. }
  3010. // Fall through to generic implementation (and it resets the impulse state)
  3011. BaseClass::ImpulseCommands();
  3012. }
  3013. void CCSPlayer::CheatImpulseCommands( int iImpulse )
  3014. {
  3015. switch( iImpulse )
  3016. {
  3017. case 101:
  3018. {
  3019. if( sv_cheats->GetBool() )
  3020. {
  3021. extern int gEvilImpulse101;
  3022. gEvilImpulse101 = true;
  3023. InitializeAccount( CSGameRules()->GetMaxMoney() );
  3024. for ( int i = 0; i < MAX_WEAPONS; ++i )
  3025. {
  3026. CBaseCombatWeapon *pWeapon = GetWeapon( i );
  3027. if ( pWeapon )
  3028. {
  3029. pWeapon->GiveReserveAmmo( AMMO_POSITION_PRIMARY, 999 );
  3030. pWeapon->GiveReserveAmmo( AMMO_POSITION_SECONDARY, 999 );
  3031. }
  3032. }
  3033. gEvilImpulse101 = false;
  3034. }
  3035. }
  3036. break;
  3037. default:
  3038. {
  3039. BaseClass::CheatImpulseCommands( iImpulse );
  3040. }
  3041. }
  3042. }
  3043. void CCSPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  3044. {
  3045. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  3046. int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
  3047. PointCameraSetupVisibility( this, area, pvs, pvssize );
  3048. }
  3049. bool CCSPlayer::ShouldCheckOcclusion( CBasePlayer *pOtherPlayer )
  3050. {
  3051. if ( BaseClass::ShouldCheckOcclusion( pOtherPlayer ) )
  3052. return true;
  3053. int nMyTeam = GetTeamNumber(), nOtherTeam = pOtherPlayer->GetTeamNumber();
  3054. if ( nMyTeam <= TEAM_SPECTATOR || nOtherTeam <= TEAM_SPECTATOR )
  3055. return false; // no need to check occlusion for spectators (or for active player looking at spectators), unless we really want to reduce network traffic a little bit
  3056. Assert( nMyTeam == TEAM_CT || nMyTeam == TEAM_TERRORIST );
  3057. Assert( nOtherTeam == TEAM_CT || nOtherTeam == TEAM_TERRORIST );
  3058. if ( nMyTeam == nOtherTeam )
  3059. return false; // no need to check occlusion between teammates
  3060. if ( !IsAlive() || !pOtherPlayer->IsAlive() )
  3061. return false; // no need to check occlusion when one of the players is dead
  3062. Assert (! static_cast< CCSPlayer* >( pOtherPlayer )->m_isCurrentGunGameTeamLeader ); // we should not even try to check occlusion for a gun game team leader; CServerGameEnts::CheckTransmit() shouldn't even call us here
  3063. return true;
  3064. }
  3065. bool CCSPlayer::IsValidObserverTarget(CBaseEntity * target )
  3066. {
  3067. if ( target == NULL )
  3068. return false;
  3069. if ( !target->IsPlayer() )
  3070. {
  3071. // [jason] If the target is planted C4, we allow that to be observed as well
  3072. CPlantedC4* pPlantedC4 = dynamic_cast< CPlantedC4* >(target );
  3073. if ( pPlantedC4 )
  3074. return true;
  3075. // allow spectating grenades
  3076. CBaseCSGrenadeProjectile* pGrenade = dynamic_cast< CBaseCSGrenadeProjectile* >( target );
  3077. if ( pGrenade )
  3078. return true;
  3079. return false;
  3080. }
  3081. // fall through to the base checks
  3082. return BaseClass::IsValidObserverTarget( target );
  3083. }
  3084. CBaseEntity* CCSPlayer::FindNextObserverTarget( bool bReverse )
  3085. {
  3086. CBaseEntity* pTarget = BaseClass::FindNextObserverTarget( bReverse );
  3087. // [jason] If we have no valid targets left (eg. last teammate dies in competitive mode )
  3088. // then try to place the camera near any planted bomb
  3089. if ( !pTarget )
  3090. {
  3091. if ( g_PlantedC4s.Count() > 0 )
  3092. {
  3093. // Immediately change the observer target, so we can handle SetObserverMode appropriately
  3094. SetObserverTarget( g_PlantedC4s[0] );
  3095. // [mbooth] free roaming spectator is useful for testing
  3096. if ( !spec_allow_roaming.GetBool() || GetObserverMode() != OBS_MODE_ROAMING )
  3097. {
  3098. // Allow the camera to pivot
  3099. SetObserverMode( OBS_MODE_CHASE );
  3100. }
  3101. return g_PlantedC4s[0];
  3102. }
  3103. }
  3104. return pTarget;
  3105. }
  3106. void CCSPlayer::UpdateAddonBits()
  3107. {
  3108. SNPROF( "CCSPlayer::UpdateAddonBits" );
  3109. int iNewBits = 0;
  3110. //it's ok to show the active weapon as a holstered weapon if it's not yet visible (still deploying)
  3111. CBaseCombatWeapon *pActiveWeapon = GetActiveWeapon();
  3112. bool bActiveWeaponIsVisible = true;
  3113. if ( pActiveWeapon && pActiveWeapon->GetWeaponWorldModel() )
  3114. {
  3115. bActiveWeaponIsVisible = !pActiveWeapon->GetWeaponWorldModel()->IsEffectActive( EF_NODRAW );
  3116. }
  3117. int nFlashbang = GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_FLASHBANG ) );
  3118. if ( dynamic_cast< CFlashbang* >( GetActiveWeapon() ) && bActiveWeaponIsVisible )
  3119. {
  3120. --nFlashbang;
  3121. }
  3122. if ( nFlashbang >= 1 )
  3123. iNewBits |= ADDON_FLASHBANG_1;
  3124. if ( nFlashbang >= 2 )
  3125. iNewBits |= ADDON_FLASHBANG_2;
  3126. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_HEGRENADE ) ) &&
  3127. ( !dynamic_cast< CHEGrenade* >( GetActiveWeapon() ) || !bActiveWeaponIsVisible ) )
  3128. {
  3129. iNewBits |= ADDON_HE_GRENADE;
  3130. }
  3131. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_SMOKEGRENADE ) ) &&
  3132. ( !dynamic_cast< CSmokeGrenade* >( GetActiveWeapon() ) || !bActiveWeaponIsVisible ) )
  3133. {
  3134. iNewBits |= ADDON_SMOKE_GRENADE;
  3135. }
  3136. // [mlowrance] Molotov purposely left out
  3137. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_DECOY ) ) &&
  3138. ( !dynamic_cast< CDecoyGrenade* >( GetActiveWeapon() ) || !bActiveWeaponIsVisible ) )
  3139. {
  3140. iNewBits |= ADDON_DECOY;
  3141. }
  3142. if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_TAGRENADE ) ) &&
  3143. ( !dynamic_cast< CSensorGrenade* >( GetActiveWeapon() ) || !bActiveWeaponIsVisible ) )
  3144. {
  3145. iNewBits |= ADDON_TAGRENADE;
  3146. }
  3147. if ( HasC4() && ( !dynamic_cast< CC4* >( GetActiveWeapon() ) || !bActiveWeaponIsVisible ) )
  3148. iNewBits |= ADDON_C4;
  3149. if ( HasDefuser() )
  3150. iNewBits |= ADDON_DEFUSEKIT;
  3151. CWeaponCSBase *weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  3152. if ( weapon && ( weapon != GetActiveWeapon() || !bActiveWeaponIsVisible ) )
  3153. {
  3154. iNewBits |= ADDON_PRIMARY;
  3155. m_iPrimaryAddon = weapon->GetCSWeaponID();
  3156. }
  3157. else
  3158. {
  3159. m_iPrimaryAddon = WEAPON_NONE;
  3160. }
  3161. weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  3162. if ( weapon && ( weapon != GetActiveWeapon() || !bActiveWeaponIsVisible ) )
  3163. {
  3164. iNewBits |= ADDON_PISTOL;
  3165. if ( weapon->GetCSWeaponID() == WEAPON_ELITE )
  3166. {
  3167. iNewBits |= ADDON_PISTOL2;
  3168. }
  3169. m_iSecondaryAddon = weapon->GetCSWeaponID();
  3170. }
  3171. else if ( weapon && weapon->GetCSWeaponID() == WEAPON_ELITE )
  3172. {
  3173. // The active weapon is weapon_elite. Set ADDON_PISTOL2 without ADDON_PISTOL, so we know
  3174. // to display the empty holster.
  3175. iNewBits |= ADDON_PISTOL2;
  3176. m_iSecondaryAddon = weapon->GetCSWeaponID();
  3177. }
  3178. else
  3179. {
  3180. m_iSecondaryAddon = WEAPON_NONE;
  3181. }
  3182. CWeaponCSBase *knife = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_KNIFE ) );
  3183. if ( knife && ( knife != GetActiveWeapon() || !bActiveWeaponIsVisible ) )
  3184. {
  3185. iNewBits |= ADDON_KNIFE;
  3186. }
  3187. m_iAddonBits = iNewBits;
  3188. }
  3189. void CCSPlayer::AppendSpottedEntityUpdateMessage( int entindex, bool bSpotted,
  3190. CCSUsrMsg_ProcessSpottedEntityUpdate::SpottedEntityUpdate *pMsg )
  3191. {
  3192. CBaseEntity * pEntity = UTIL_EntityByIndex( entindex );
  3193. if ( pEntity )
  3194. {
  3195. pMsg->set_entity_idx( entindex );
  3196. // Entity may not yet exist on client so we need to pass the class ID
  3197. pMsg->set_class_id( pEntity->GetServerClass()->m_ClassID );
  3198. // generic entity data
  3199. // write out position
  3200. pMsg->set_origin_x( pEntity->GetAbsOrigin().x/4 );
  3201. pMsg->set_origin_y( pEntity->GetAbsOrigin().y/4 );
  3202. pMsg->set_origin_z( pEntity->GetAbsOrigin().z/4 );
  3203. pMsg->set_angle_y( AngleNormalize( pEntity->GetAbsAngles().y ) );
  3204. // Clients are are unaware of the defuser class, so we need to flag defuse entities manually
  3205. pMsg->set_defuser( FClassnameIs( pEntity, "item_defuser" ) || FClassnameIs( pEntity, "item_cutters" ) );
  3206. // class specific data first
  3207. CCSPlayer * pPlayer = ToCSPlayer( UTIL_PlayerByIndex( entindex ) );
  3208. if ( pPlayer )
  3209. {
  3210. pMsg->set_player_has_defuser( pPlayer->HasDefuser() ); // has defuser
  3211. pMsg->set_player_has_c4( pPlayer->HasC4() ); // has bomb
  3212. }
  3213. }
  3214. }
  3215. void CCSPlayer::ProcessSpottedEntityUpdate()
  3216. {
  3217. if ( gpGlobals->curtime < m_fNextRadarUpdateTime )
  3218. return;
  3219. m_fNextRadarUpdateTime = gpGlobals->curtime + SPOTTED_ENTITY_UPDATE_INTERVAL + RandomFloat( 0.f, SPOTTED_ENTITY_UPDATE_INTERVAL );
  3220. // Determine which entities are outside of PVS and test their spot state
  3221. GatherNonPVSSpottedEntitiesFunctor NonPVSSpottable( this );
  3222. ForEachEntity( NonPVSSpottable );
  3223. const CBitVec<MAX_EDICTS> & entities = NonPVSSpottable.GetSpotted( );
  3224. int nIndex = entities.FindNextSetBit( 0 );
  3225. CSingleUserRecipientFilter user( this );
  3226. CCSUsrMsg_ProcessSpottedEntityUpdate msg;
  3227. msg.set_new_update( true ); // Start of a new update frame. Signals the client to reset its spotting data.
  3228. int nMessageEntityCount = 0; // The number of entities updated in this message
  3229. while ( nIndex > -1 )
  3230. {
  3231. if ( this->entindex() != nIndex )
  3232. {
  3233. if ( nMessageEntityCount >= SPOTTED_ENTITY_COUNT_MESSAGE_MAX )
  3234. {
  3235. // We do not have enough space for this entity. Start a new message.
  3236. SendUserMessage( user, CS_UM_ProcessSpottedEntityUpdate, msg );
  3237. msg.Clear();
  3238. msg.set_new_update( false ); // Start of a partial update. Clients do not need to clear their spotting data.
  3239. nMessageEntityCount = 0;
  3240. continue;
  3241. }
  3242. AppendSpottedEntityUpdateMessage( nIndex, true, msg.add_entity_updates() );
  3243. nMessageEntityCount++;
  3244. }
  3245. nIndex = entities.FindNextSetBit( nIndex + 1 );
  3246. }
  3247. SendUserMessage( user, CS_UM_ProcessSpottedEntityUpdate, msg );
  3248. }
  3249. void CCSPlayer::UpdateMouseoverHints()
  3250. {
  3251. return;
  3252. // these traces and checks existed to pop up hints telling the player what to do when they saw a friend or enemy
  3253. // all of these hints are now handled by the game instructor and have time-outs, success limits, etc and this is no longer needed
  3254. /*
  3255. if ( IsBlind() || IsObserver() )
  3256. return;
  3257. // Exit out if hint has already been displayed or is not applicable
  3258. if ( !CSGameRules()->IsHostageRescueMap() || m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR )
  3259. {
  3260. return;
  3261. }
  3262. Vector forward, up;
  3263. EyeVectors( &forward, NULL, &up );
  3264. trace_t tr;
  3265. // Search for objects in a sphere (tests for entities that are not solid, yet still useable )
  3266. Vector searchStart = EyePosition();
  3267. Vector searchEnd = searchStart + forward * 2048;
  3268. int useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_VISIBLE_AND_NPCS;
  3269. UTIL_TraceLine( searchStart, searchEnd, useableContents, this, COLLISION_GROUP_NONE, &tr );
  3270. if ( tr.fraction != 1.0f )
  3271. {
  3272. if (tr.DidHitNonWorldEntity() && tr.m_pEnt )
  3273. {
  3274. CBaseEntity *pObject = tr.m_pEnt;
  3275. switch ( pObject->Classify() )
  3276. {
  3277. case CLASS_PLAYER:
  3278. {
  3279. const float grenadeBloat = 1.2f; // Be conservative in estimating what a player can distinguish
  3280. if ( !TheBots->IsLineBlockedBySmoke( EyePosition(), pObject->EyePosition(), grenadeBloat ) )
  3281. {
  3282. if ( g_pGameRules->PlayerRelationship( this, pObject ) == GR_TEAMMATE )
  3283. {
  3284. if ( !(m_iDisplayHistoryBits & DHF_FRIEND_SEEN ) )
  3285. {
  3286. m_iDisplayHistoryBits |= DHF_FRIEND_SEEN;
  3287. HintMessage( "#Hint_spotted_a_friend", true );
  3288. }
  3289. }
  3290. else
  3291. {
  3292. if ( !(m_iDisplayHistoryBits & DHF_ENEMY_SEEN ) )
  3293. {
  3294. m_iDisplayHistoryBits |= DHF_ENEMY_SEEN;
  3295. HintMessage( "#Hint_spotted_an_enemy", true );
  3296. }
  3297. }
  3298. }
  3299. }
  3300. break;
  3301. case CLASS_PLAYER_ALLY:
  3302. switch ( GetTeamNumber() )
  3303. {
  3304. case TEAM_CT:
  3305. if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR ) && tr.fraction > 0.1f )
  3306. {
  3307. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  3308. HintMessage( "#Hint_rescue_the_hostages", true );
  3309. }
  3310. else if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_NEAR ) && tr.fraction <= 0.1f )
  3311. {
  3312. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  3313. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_NEAR;
  3314. }
  3315. break;
  3316. case TEAM_TERRORIST:
  3317. if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR ) )
  3318. {
  3319. m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
  3320. HintMessage( "#Hint_prevent_hostage_rescue", true );
  3321. }
  3322. break;
  3323. }
  3324. break;
  3325. }
  3326. }
  3327. }
  3328. */
  3329. }
  3330. void CCSPlayer::PostThink()
  3331. {
  3332. BaseClass::PostThink();
  3333. // if we're spawning, clear it
  3334. if ( m_bIsSpawning )
  3335. m_bIsSpawning = false;
  3336. if ( IsTaunting() )
  3337. {
  3338. if ( gpGlobals->curtime >= m_flTauntEndTime )
  3339. {
  3340. StopTaunting();
  3341. }
  3342. else if ( IsThirdPersonTaunt() && ( GetGroundEntity() == NULL && GetWaterLevel() == WL_NotInWater ) )
  3343. {
  3344. StopTaunting();
  3345. }
  3346. }
  3347. if ( IsLookingAtWeapon() )
  3348. {
  3349. if ( gpGlobals->curtime >= m_flLookWeaponEndTime )
  3350. {
  3351. StopLookingAtWeapon();
  3352. }
  3353. }
  3354. // failsafe to show active world model if it fails to unhide by the time deploy is complete
  3355. CWeaponCSBase *pCSWeapon = GetActiveCSWeapon();
  3356. if ( pCSWeapon && pCSWeapon->GetActivity() != pCSWeapon->GetDeployActivity() )
  3357. {
  3358. pCSWeapon->ShowWeaponWorldModel( true );
  3359. }
  3360. UpdateAddonBits();
  3361. ProcessSpottedEntityUpdate();
  3362. //UpdateTeamMoney();
  3363. if ( !(m_iDisplayHistoryBits & DHF_ROUND_STARTED ) && CanPlayerBuy(false ) )
  3364. {
  3365. m_iDisplayHistoryBits |= DHF_ROUND_STARTED;
  3366. }
  3367. if ( m_flNextMouseoverUpdate < gpGlobals->curtime )
  3368. {
  3369. m_flNextMouseoverUpdate = gpGlobals->curtime + 0.2f;
  3370. if ( m_bShowHints )
  3371. {
  3372. UpdateMouseoverHints();
  3373. }
  3374. }
  3375. #if !defined( CSTRIKE15 )
  3376. if ( GetActiveWeapon() && !(m_iDisplayHistoryBits & DHF_AMMO_EXHAUSTED ) )
  3377. {
  3378. // The "out of ammo" prompt shouldn't display in GunGame or training, because there are no buy-zones. <-- why doesn't this just check for buyzones in the map? -mtw
  3379. if ( !CSGameRules()->IsPlayingGunGame() && !CSGameRules()->IsPlayingTraining() )
  3380. {
  3381. CBaseCombatWeapon *pWeapon = GetActiveWeapon();
  3382. if ( !pWeapon->HasAnyAmmo() && !(pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE ) )
  3383. {
  3384. m_iDisplayHistoryBits |= DHF_AMMO_EXHAUSTED;
  3385. HintMessage( "#Hint_out_of_ammo", false );
  3386. }
  3387. }
  3388. }
  3389. #endif
  3390. if ( !m_bWasInBuyZone && IsInBuyZone() && IsAlive() )
  3391. {
  3392. // we entered a buyzone
  3393. m_bWasInBuyZone = true;
  3394. IGameEvent * event = gameeventmanager->CreateEvent( "enter_buyzone" );
  3395. if ( event )
  3396. {
  3397. event->SetInt( "userid", GetUserID() );
  3398. event->SetBool( "canbuy", CanPlayerBuy( false ) );
  3399. gameeventmanager->FireEvent( event );
  3400. }
  3401. }
  3402. else if ( m_bWasInBuyZone && !IsInBuyZone() && IsAlive() )
  3403. {
  3404. // we exited a buy zone
  3405. m_bWasInBuyZone = false;
  3406. IGameEvent * event = gameeventmanager->CreateEvent( "exit_buyzone" );
  3407. if ( event )
  3408. {
  3409. event->SetInt( "userid", GetUserID() );
  3410. event->SetBool( "canbuy", CanPlayerBuy( false ) );
  3411. gameeventmanager->FireEvent( event );
  3412. }
  3413. }
  3414. if ( !m_bWasInBombZoneTrigger && m_bInBombZoneTrigger )
  3415. {
  3416. // we entered a bomb zone
  3417. m_bWasInBombZoneTrigger = true;
  3418. IGameEvent * event = gameeventmanager->CreateEvent( "enter_bombzone" );
  3419. if ( event )
  3420. {
  3421. event->SetInt( "userid", GetUserID() );
  3422. event->SetBool( "hasbomb", HasC4() );
  3423. event->SetBool( "isplanted", CSGameRules()->m_bBombPlanted );
  3424. gameeventmanager->FireEvent( event );
  3425. }
  3426. }
  3427. else if ( m_bWasInBombZoneTrigger && !m_bInBombZoneTrigger )
  3428. {
  3429. // we exited a bomb zone
  3430. m_bWasInBombZoneTrigger = false;
  3431. IGameEvent * event = gameeventmanager->CreateEvent( "exit_bombzone" );
  3432. if ( event )
  3433. {
  3434. event->SetInt( "userid", GetUserID() );
  3435. event->SetBool( "hasbomb", HasC4() );
  3436. event->SetBool( "isplanted", CSGameRules()->m_bBombPlanted );
  3437. gameeventmanager->FireEvent( event );
  3438. }
  3439. }
  3440. if ( !m_bWasInHostageRescueZone && m_bInHostageRescueZone )
  3441. {
  3442. // we entered a hostage rescue zone
  3443. m_bWasInHostageRescueZone = true;
  3444. IGameEvent * event = gameeventmanager->CreateEvent( "enter_rescue_zone" );
  3445. if ( event )
  3446. {
  3447. event->SetInt( "userid", GetUserID() );
  3448. gameeventmanager->FireEvent( event );
  3449. }
  3450. }
  3451. else if ( m_bWasInHostageRescueZone && !m_bInHostageRescueZone )
  3452. {
  3453. // we exited a hostage rescue zone
  3454. m_bWasInHostageRescueZone = false;
  3455. IGameEvent * event = gameeventmanager->CreateEvent( "exit_rescue_zone" );
  3456. if ( event )
  3457. {
  3458. event->SetInt( "userid", GetUserID() );
  3459. gameeventmanager->FireEvent( event );
  3460. }
  3461. }
  3462. #if 0
  3463. if ( m_iNumFollowers > 0 && IsAlive() )
  3464. {
  3465. ShortestPathCost cost = ShortestPathCost();
  3466. float dist = NavAreaTravelDistance( this->GetLastKnownArea(),
  3467. TheNavMesh->GetNearestNavArea( TheCSBots()->GetZone(0)->m_center ),
  3468. cost );
  3469. if( dist < cs_hostage_near_rescue_music_distance.GetFloat() )
  3470. {
  3471. CBroadcastRecipientFilter filter;
  3472. PlayMusicSelection( filter, "Music.HostageNearRescue" );
  3473. //DevMsg("***DISTANCE TO RESCUE: %f: FOLLOWERS: %i\n", dist, m_iNumFollowers );
  3474. }
  3475. }
  3476. #endif
  3477. // Store the eye angles pitch so the client can compute its animation state correctly.
  3478. QAngle eyeAngles = EyeAngles();
  3479. Vector &angEyeAngles = m_angEyeAngles.GetForModify();
  3480. angEyeAngles.x = eyeAngles.x;
  3481. angEyeAngles.y = eyeAngles.y;
  3482. angEyeAngles.z = eyeAngles.z;
  3483. m_flThirdpersonRecoil = GetAimPunchAngle()[PITCH];
  3484. m_bUseNewAnimstate ? m_PlayerAnimStateCSGO->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ) : m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  3485. // check if we need to apply a deafness DSP effect.
  3486. if ((m_applyDeafnessTime != 0.0f ) && (m_applyDeafnessTime <= gpGlobals->curtime ))
  3487. {
  3488. ApplyDeafnessEffect();
  3489. }
  3490. if ( IsPlayerUnderwater() && GetWaterLevel() < WL_Eyes )
  3491. {
  3492. StopSound( "Player.AmbientUnderWater" );
  3493. SetPlayerUnderwater( false );
  3494. }
  3495. if( !m_bUseNewAnimstate && IsAlive() && m_cycleLatchTimer.IsElapsed() )
  3496. {
  3497. m_cycleLatchTimer.Start( CycleLatchInterval );
  3498. // 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
  3499. m_cycleLatch.GetForModify() = 16 * GetCycle();// 4 point fixed
  3500. }
  3501. // if player is not blind, set flash duration to default
  3502. if ( m_flFlashDuration > 0.000001f && !IsBlind() )
  3503. {
  3504. m_flFlashDuration = 0.0f;
  3505. }
  3506. // inactive player drops the bomb after a certain duration (afk)
  3507. if ( !m_bHasMovedSinceSpawn && CSGameRules()->GetRoundElapsedTime() > sv_spawn_afk_bomb_drop_time.GetFloat()
  3508. && !CSGameRules()->IsPlayingCoopMission() )
  3509. {
  3510. // Drop the C4
  3511. CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
  3512. if ( pC4 )
  3513. {
  3514. SetBombDroppedTime( gpGlobals->curtime );
  3515. CSWeaponDrop( pC4, false, false );
  3516. //odd that the AFK player 'says' they have dropped the bomb... but it's better than nothing
  3517. Radio( "SpottedLooseBomb", "#Cstrike_TitlesTXT_Game_afk_bomb_drop" );
  3518. }
  3519. }
  3520. if ( m_bNeedToUpdateCoinFromInventory )
  3521. {
  3522. m_bNeedToUpdateCoinFromInventory = false;
  3523. bool bHasCoin = false;
  3524. if ( CEconItemView const *pItemViewCoin = Inventory()->GetItemInLoadout( 0, LOADOUT_POSITION_FLAIR0 ) )
  3525. {
  3526. if ( const GameItemDefinition_t *pDefItem = pItemViewCoin->GetItemDefinition() )
  3527. {
  3528. bHasCoin = true;
  3529. SetRank( MEDAL_CATEGORY_SEASON_COIN, MedalRank_t( pDefItem->GetDefinitionIndex() ) );
  3530. }
  3531. }
  3532. if ( !bHasCoin )
  3533. {
  3534. SetRank( MEDAL_CATEGORY_SEASON_COIN, MedalRank_t( 0 ) );
  3535. }
  3536. }
  3537. if ( m_bNeedToUpdateMusicFromInventory )
  3538. {
  3539. m_bNeedToUpdateMusicFromInventory = false;
  3540. bool bHasMusic = false;
  3541. if ( CEconItemView const *pItemViewMusic = Inventory()->GetItemInLoadout( 0, LOADOUT_POSITION_MUSICKIT ) )
  3542. {
  3543. static const CEconItemAttributeDefinition *pAttr_MusicID = GetItemSchema()->GetAttributeDefinitionByName( "music id" );
  3544. uint32 unMusicID;
  3545. if ( pItemViewMusic && pItemViewMusic->IsValid() && pItemViewMusic->FindAttribute( pAttr_MusicID, &unMusicID ) )
  3546. {
  3547. SetMusicID( unMusicID );
  3548. bHasMusic = true;
  3549. }
  3550. }
  3551. if ( !bHasMusic )
  3552. {
  3553. SetMusicID( CSMUSIC_NOPACK );
  3554. }
  3555. }
  3556. if ( m_bNeedToUpdatePlayerSprayFromInventory )
  3557. {
  3558. m_bNeedToUpdatePlayerSprayFromInventory = false;
  3559. COMPILE_TIME_ASSERT( ARRAYSIZE( m_unEquippedPlayerSprayIDs ) == ( LOADOUT_POSITION_SPRAY0 /*LOADOUT_POSITION_SPRAY3*/ + 1 - LOADOUT_POSITION_SPRAY0 ) );
  3560. for ( int iSprayLoadoutSlot = LOADOUT_POSITION_SPRAY0; iSprayLoadoutSlot <= LOADOUT_POSITION_SPRAY0 /*LOADOUT_POSITION_SPRAY3*/; ++ iSprayLoadoutSlot )
  3561. {
  3562. bool bHasEquippedPlayerSpray = false;
  3563. if ( CEconItemView const *pItemViewEquipped = Inventory()->GetItemInLoadout( 0, iSprayLoadoutSlot ) )
  3564. {
  3565. if ( pItemViewEquipped && pItemViewEquipped->IsValid() )
  3566. {
  3567. if ( uint32 unStickerKitID = pItemViewEquipped->GetStickerAttributeBySlotIndexInt( 0, k_EStickerAttribute_ID, 0 ) )
  3568. {
  3569. m_unEquippedPlayerSprayIDs[ iSprayLoadoutSlot - LOADOUT_POSITION_SPRAY0 ] = unStickerKitID;
  3570. bHasEquippedPlayerSpray = true;
  3571. }
  3572. }
  3573. }
  3574. if ( !bHasEquippedPlayerSpray )
  3575. {
  3576. m_unEquippedPlayerSprayIDs[ iSprayLoadoutSlot - LOADOUT_POSITION_SPRAY0 ] = 0;
  3577. }
  3578. }
  3579. }
  3580. if ( m_bNeedToUpdatePersonaDataPublicFromInventory )
  3581. {
  3582. m_bNeedToUpdatePersonaDataPublicFromInventory = false;
  3583. if ( CCSPlayerInventory *pInv = Inventory() )
  3584. {
  3585. if ( GCSDK::CGCClientSharedObjectCache *pSOC = pInv->GetSOC() )
  3586. {
  3587. if ( GCSDK::CGCClientSharedObjectTypeCache *pCacheType = pSOC->FindTypeCache( CEconPersonaDataPublic::k_nTypeID ) )
  3588. {
  3589. if ( pCacheType->GetCount() == 1 )
  3590. {
  3591. CEconPersonaDataPublic *pCacheData = ( CEconPersonaDataPublic * ) pCacheType->GetObject( 0 );
  3592. delete m_pPersonaDataPublic;
  3593. if ( pCacheData )
  3594. {
  3595. m_pPersonaDataPublic = new CEconPersonaDataPublic;
  3596. m_pPersonaDataPublic->Obj().CopyFrom( pCacheData->Obj() );
  3597. }
  3598. }
  3599. }
  3600. }
  3601. }
  3602. }
  3603. if ( m_flFlinchStack < 1.0 )
  3604. {
  3605. m_flFlinchStack = Approach( 1.0, m_flFlinchStack, gpGlobals->frametime * 0.35 );//0.65
  3606. //Msg( "m_flFlinchStack = %f\n", m_flFlinchStack );
  3607. }
  3608. m_flFlinchStack = clamp( m_flFlinchStack, 0.1, 1.0 );
  3609. int nTeamCheck = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  3610. //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  3611. if ( CSGameRules()->IsPlayingCoopGuardian() || CSGameRules()->IsPlayingCoopMission() )
  3612. {
  3613. float flDistance = 0;
  3614. float flDistMin = (float)mp_guardian_player_dist_min.GetInt();
  3615. float flDistMax = (float)mp_guardian_player_dist_max.GetInt();
  3616. if ( CSGameRules()->IsPlayingCoopGuardian() && TheCSBots() && TheNavMesh
  3617. && GetTeamNumber() == nTeamCheck && IsAlive() )
  3618. {
  3619. if ( CSGameRules()->IsHostageRescueMap() )
  3620. {
  3621. Vector vecStartSpot = Vector( 0, 0, 0 );
  3622. for ( int iHostage = 0; iHostage < g_Hostages.Count(); iHostage++ )
  3623. {
  3624. CHostage *pHostage = g_Hostages[iHostage];
  3625. if ( pHostage )
  3626. {
  3627. vecStartSpot = ( vecStartSpot + pHostage->GetAbsOrigin() ) / ( iHostage + 1 );
  3628. }
  3629. }
  3630. flDistance = ( GetAbsOrigin() - vecStartSpot ).Length();
  3631. }
  3632. else if ( TheCSBots()->GetTerroristTargetSite() >= 0 )
  3633. {
  3634. const CCSBotManager::Zone *zone = TheCSBots()->GetZone( mp_guardian_target_site.GetInt() );
  3635. if ( zone && zone->m_area[0] )
  3636. flDistance = ( GetAbsOrigin() - zone->m_area[0]->GetCenter() ).Length();
  3637. }
  3638. }
  3639. bool bCanHurt = false;
  3640. int nDamageToGive = 20;
  3641. if ( CSGameRules()->IsPlayingCoopGuardian() )
  3642. bCanHurt = true;
  3643. if ( IsAlive() && flDistMin > 0 )
  3644. m_flGuardianTooFarDistFrac = MAX( 0, ( flDistance - flDistMin ) / ( flDistMax - flDistMin ) );
  3645. else
  3646. m_flGuardianTooFarDistFrac = 0;
  3647. if ( flDistance > flDistMax )
  3648. {
  3649. if ( bCanHurt && m_flNextGuardianTooFarHurtTime <= gpGlobals->curtime )
  3650. {
  3651. Vector vecDamagePos = GetAbsOrigin();
  3652. CWorld *pWorld = GetWorldEntity();
  3653. if ( !pWorld )
  3654. return;
  3655. CTakeDamageInfo info( pWorld, pWorld, nDamageToGive, DMG_GENERIC );
  3656. info.SetDamagePosition( vecDamagePos );
  3657. info.SetDamageForce( vec3_origin );
  3658. TakeDamage( info );
  3659. m_flNextGuardianTooFarHurtTime = gpGlobals->curtime + 0.75;
  3660. }
  3661. }
  3662. }
  3663. if ( m_flDetectedByEnemySensorTime > 0.0f && (gpGlobals->curtime - m_flDetectedByEnemySensorTime) > 5.0f )
  3664. {
  3665. m_flDetectedByEnemySensorTime = 0.0f;
  3666. }
  3667. }
  3668. void CCSPlayer::PushawayThink()
  3669. {
  3670. // Push physics props out of our way.
  3671. PerformObstaclePushaway( this );
  3672. SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );
  3673. }
  3674. void CCSPlayer::SetModel( const char *szModelName )
  3675. {
  3676. m_bUseNewAnimstate = ( Q_stristr( szModelName, "custom_player" ) != 0 );
  3677. if ( m_bUseNewAnimstate )
  3678. {
  3679. if ( m_bUseNewAnimstate && m_PlayerAnimStateCSGO )
  3680. m_PlayerAnimStateCSGO->Reset();
  3681. }
  3682. BaseClass::SetModel( szModelName );
  3683. }
  3684. //-----------------------------------------------------------------------------
  3685. // Purpose: Returns whether or not we can switch to the given weapon.
  3686. // Input : pWeapon -
  3687. //-----------------------------------------------------------------------------
  3688. bool CCSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
  3689. {
  3690. if ( !pWeapon->CanDeploy() )
  3691. return false;
  3692. if ( GetActiveWeapon() )
  3693. {
  3694. if ( !GetActiveWeapon()->CanHolster() )
  3695. return false;
  3696. }
  3697. return true;
  3698. }
  3699. void CCSPlayer::OnSwitchWeapons( CBaseCombatWeapon* pBaseWeapon )
  3700. {
  3701. if ( pBaseWeapon )
  3702. {
  3703. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  3704. if ( pWeapon )
  3705. {
  3706. IGameEvent * event = gameeventmanager->CreateEvent( "item_equip" );
  3707. if( event )
  3708. {
  3709. const char *weaponName = pWeapon->GetClassname();
  3710. if ( IsWeaponClassname( weaponName ) )
  3711. {
  3712. weaponName += WEAPON_CLASSNAME_PREFIX_LENGTH;
  3713. }
  3714. event->SetInt( "userid", GetUserID() );
  3715. event->SetString( "item", weaponName );
  3716. event->SetBool( "canzoom", pWeapon->HasZoom() );
  3717. event->SetBool( "hassilencer", pWeapon->HasSilencer() );
  3718. event->SetBool( "issilenced", pWeapon->IsSilenced() );
  3719. event->SetBool( "hastracers", (pWeapon->GetCSWpnData().GetTracerFrequency() > 0 ) ? true : false );
  3720. int nType = pWeapon->GetWeaponType();
  3721. event->SetInt( "weptype", (nType == WEAPONTYPE_UNKNOWN ) ? -1 : nType );
  3722. /*
  3723. WEAPONTYPE_UNKNOWN = -1
  3724. //
  3725. WEAPONTYPE_KNIFE=0,
  3726. WEAPONTYPE_PISTOL,
  3727. WEAPONTYPE_SUBMACHINEGUN,
  3728. WEAPONTYPE_RIFLE,
  3729. WEAPONTYPE_SHOTGUN,
  3730. WEAPONTYPE_SNIPER_RIFLE,
  3731. WEAPONTYPE_MACHINEGUN,
  3732. WEAPONTYPE_C4,
  3733. WEAPONTYPE_GRENADE,
  3734. */
  3735. bool bIsPainted = false;
  3736. CEconItemView *pItem = pWeapon->GetEconItemView();
  3737. if (pItem)
  3738. {
  3739. const CPaintKit *pPaintKit = pItem->GetCustomPaintKit();
  3740. if ( pPaintKit && (pPaintKit->nID > 0 ) )
  3741. {
  3742. bIsPainted = true;
  3743. }
  3744. }
  3745. event->SetBool( "ispainted", bIsPainted );
  3746. gameeventmanager->FireEvent( event );
  3747. }
  3748. CSWeaponType weaponType = pWeapon->GetWeaponType();
  3749. CSWeaponID weaponID = static_cast<CSWeaponID>( pWeapon->GetCSWeaponID() );
  3750. if ( weaponType == WEAPONTYPE_GRENADE )
  3751. { // When switching to grenade remember the preferred grenade
  3752. m_nPreferredGrenadeDrop = weaponID;
  3753. }
  3754. if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
  3755. {
  3756. if ( m_WeaponTypesHeld.Find( weaponID ) == -1 )
  3757. {
  3758. // Add this weapon to the list of weapons used by the player
  3759. m_WeaponTypesHeld.AddToTail( weaponID );
  3760. }
  3761. }
  3762. MDLCACHE_CRITICAL_SECTION();
  3763. // Add a deploy event to let the 3rd person animation system know to update to the current weapon and optionally play a deploy animation if it exists.
  3764. if ( (gpGlobals->curtime - pBaseWeapon->m_flLastTimeInAir) < 0.1f )
  3765. {
  3766. // if the weapon was flying through the air VERY recently, assume we 'caught' it and play a catch anim
  3767. DoAnimationEvent( PLAYERANIMEVENT_CATCH_WEAPON );
  3768. }
  3769. else
  3770. {
  3771. DoAnimationEvent( PLAYERANIMEVENT_DEPLOY );
  3772. }
  3773. }
  3774. }
  3775. }
  3776. bool CCSPlayer::ShouldDoLargeFlinch( const CTakeDamageInfo& info, int nHitGroup )
  3777. {
  3778. if ( FBitSet( GetFlags(), FL_DUCKING ) )
  3779. return false;
  3780. //CWeaponCSBase *pWeapon = dynamic_cast<CWeaponCSBase*>(info.GetWeapon());
  3781. //if ( pWeapon && pWeapon->GetWeaponType() == WEAPONTYPE_SUBMACHINEGUN )
  3782. // return true;
  3783. if ( nHitGroup == HITGROUP_LEFTLEG )
  3784. return false;
  3785. if ( nHitGroup == HITGROUP_RIGHTLEG )
  3786. return false;
  3787. return true;
  3788. }
  3789. bool CCSPlayer::IsArmored( int nHitGroup )
  3790. {
  3791. bool bApplyArmor = false;
  3792. if ( ArmorValue() > 0 )
  3793. {
  3794. switch ( nHitGroup )
  3795. {
  3796. case HITGROUP_GENERIC:
  3797. case HITGROUP_CHEST:
  3798. case HITGROUP_STOMACH:
  3799. case HITGROUP_LEFTARM:
  3800. case HITGROUP_RIGHTARM:
  3801. bApplyArmor = true;
  3802. break;
  3803. case HITGROUP_HEAD:
  3804. if ( m_bHasHelmet )
  3805. {
  3806. bApplyArmor = true;
  3807. }
  3808. break;
  3809. default:
  3810. break;
  3811. }
  3812. }
  3813. return bApplyArmor;
  3814. }
  3815. void CCSPlayer::Pain( CCSPlayer* pAttacker, bool bHasArmour, int nDmgTypeBits )
  3816. {
  3817. if ( nDmgTypeBits & DMG_BURN )
  3818. {
  3819. if ( bHasArmour == false )
  3820. {
  3821. EmitSound( "Player.BurnDamage" );
  3822. }
  3823. else
  3824. {
  3825. EmitSound( "Player.BurnDamageKevlar" );
  3826. }
  3827. return;
  3828. }
  3829. if ( nDmgTypeBits & DMG_CLUB )
  3830. {
  3831. if ( bHasArmour == false )
  3832. {
  3833. EmitSound( "Flesh.BulletImpact" );
  3834. }
  3835. else
  3836. {
  3837. EmitSound( "Player.DamageKevlar" );
  3838. }
  3839. return;
  3840. }
  3841. switch (m_LastHitGroup )
  3842. {
  3843. case HITGROUP_HEAD: {
  3844. //When hit in the head we play a sound for the player who made the headshot to give them
  3845. //feedback. This plays even at a very long range. Other players receive another sound
  3846. //that doesn't carry as far.
  3847. CRecipientFilter filter;
  3848. for (int i = 1; i <= gpGlobals->maxClients; ++i)
  3849. {
  3850. CBasePlayer* pPlayer = UTIL_PlayerByIndex(i);
  3851. if (!pPlayer || pPlayer == pAttacker)
  3852. {
  3853. //exclude the player who made the shot.
  3854. continue;
  3855. }
  3856. filter.AddRecipient(pPlayer);
  3857. }
  3858. EmitSound_t params;
  3859. params.m_pSoundName = m_bHasHelmet ? "Player.DamageHelmet" : "Player.DamageHeadShot";
  3860. params.m_flSoundTime = 0.0f;
  3861. params.m_pflSoundDuration = nullptr;
  3862. params.m_bWarnOnDirectWaveReference = true;
  3863. EmitSound(filter, entindex(), params);
  3864. if (pAttacker != nullptr)
  3865. {
  3866. //The player who made the shot gets this 'feedback' version of the sound.
  3867. CRecipientFilter attacker_filter;
  3868. attacker_filter.AddRecipient(pAttacker);
  3869. EmitSound_t attacker_params;
  3870. attacker_params.m_pSoundName = m_bHasHelmet ? "Player.DamageHelmetFeedback" : "Player.DamageHeadShotFeedback";
  3871. attacker_params.m_flSoundTime = 0.0f;
  3872. attacker_params.m_pflSoundDuration = nullptr;
  3873. attacker_params.m_bWarnOnDirectWaveReference = true;
  3874. EmitSound(attacker_filter, entindex(), attacker_params);
  3875. }
  3876. break;
  3877. }
  3878. default:
  3879. if ( bHasArmour == false )
  3880. {
  3881. EmitSound( "Flesh.BulletImpact" );
  3882. }
  3883. else
  3884. {
  3885. EmitSound( "Player.DamageKevlar" );
  3886. }
  3887. break;
  3888. }
  3889. }
  3890. ConVar mp_tagging_scale( "mp_tagging_scale", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Scalar for player tagging modifier when hit. Lower values for greater tagging." );
  3891. class CBombShieldTraceEnum : public IEntityEnumerator
  3892. {
  3893. public:
  3894. CBombShieldTraceEnum( Ray_t *pRay ) : m_pRay(pRay), m_bHitBombBlocker(false)
  3895. {
  3896. }
  3897. virtual bool EnumEntity( IHandleEntity *pHandleEntity )
  3898. {
  3899. Assert( pHandleEntity );
  3900. trace_t tr;
  3901. enginetrace->ClipRayToEntity( *m_pRay, MASK_ALL, pHandleEntity, &tr );
  3902. if (( tr.fraction < 1.0f ) || (tr.startsolid) || (tr.allsolid))
  3903. {
  3904. if ( !V_strcmp( tr.surface.name, "TOOLS/TOOLSBLOCKBOMB" ) )
  3905. {
  3906. m_bHitBombBlocker = true;
  3907. return false;
  3908. }
  3909. }
  3910. return true;
  3911. }
  3912. bool HitBombBlocker( void ) { return m_bHitBombBlocker; }
  3913. private:
  3914. Ray_t *m_pRay;
  3915. bool m_bHitBombBlocker;
  3916. };
  3917. static CUtlVector< CCSPlayer::ITakeDamageListener * > s_arrTakeDamageListeners;
  3918. CCSPlayer::ITakeDamageListener::ITakeDamageListener()
  3919. {
  3920. s_arrTakeDamageListeners.AddToTail( this );
  3921. }
  3922. CCSPlayer::ITakeDamageListener::~ITakeDamageListener()
  3923. {
  3924. Verify( s_arrTakeDamageListeners.FindAndRemove( this ) );
  3925. }
  3926. int CCSPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  3927. {
  3928. CTakeDamageInfo info = inputInfo;
  3929. FOR_EACH_VEC_BACK( s_arrTakeDamageListeners, idxTDL )
  3930. {
  3931. s_arrTakeDamageListeners[idxTDL]->OnTakeDamageListenerCallback( this, info );
  3932. }
  3933. if ( m_bGunGameImmunity )
  3934. {
  3935. // No damage if immune
  3936. return 0;
  3937. }
  3938. CBaseEntity *pInflictor = info.GetInflictor();
  3939. if ( !pInflictor )
  3940. return 0;
  3941. if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
  3942. return 0;
  3943. //if this is C4 bomb damage, make sure it didn't pass through any bomb blockers to reach this player.
  3944. CPlantedC4 *pInflictorC4 = dynamic_cast< CPlantedC4 * >( pInflictor );
  3945. if ( pInflictorC4 )
  3946. {
  3947. Ray_t ray;
  3948. ray.Init( pInflictorC4->GetAbsOrigin(), GetAbsOrigin() );
  3949. CBombShieldTraceEnum bombShieldTrace( &ray );
  3950. enginetrace->EnumerateEntities( ray, true, &bombShieldTrace );
  3951. if ( bombShieldTrace.HitBombBlocker() )
  3952. {
  3953. return 0;
  3954. }
  3955. }
  3956. // Because explosions and fire damage don't do raytracing, but rather lookup entities in volume,
  3957. // we need to set damage hitgroup to generic to make sure previous bullet damage hitgroup is not
  3958. // carried over. Only bullets do raytracing so force it here!
  3959. if ( ( info.GetDamageType() & DMG_BULLET ) == 0 )
  3960. m_LastHitGroup = HITGROUP_GENERIC;
  3961. float flArmorBonus = 0.5f;
  3962. float flArmorRatio = 0.5f;
  3963. float flDamage = info.GetDamage();
  3964. bool bFriendlyFireEnabled = CSGameRules()->IsFriendlyFireOn();
  3965. m_LastDamageType = info.GetDamageType();
  3966. CSGameRules()->PlayerTookDamage(this, info );
  3967. CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker() );
  3968. // determine some useful info about the source of this damage
  3969. bool bDamageIsFromTeammate = pAttacker && ( pAttacker != this ) && IsOtherSameTeam( pAttacker->GetTeamNumber() ) && !IsOtherEnemy( pAttacker );
  3970. if ( (!bFriendlyFireEnabled && bDamageIsFromTeammate) || ( bDamageIsFromTeammate && CSGameRules()->IsFreezePeriod() ) )
  3971. {
  3972. // when FF is off and that damage is from a teammate (not yourself ) never do damage
  3973. // this FF setting should be consistent and the behavior should match player expectations (no middle ground ) [mtw]
  3974. return 0;
  3975. }
  3976. bool bDamageIsFromSelf = (pAttacker == this );
  3977. bool bDamageIsFromGunfire = !!(info.GetDamageType() & DMG_BULLET ); // check the damage type [mtw]
  3978. bool bDamageIsFromGrenade = pInflictor && !!(info.GetDamageType() & DMG_BLAST ) && dynamic_cast< CHEGrenadeProjectile* >( pInflictor ) != NULL;
  3979. bool bDamageIsFromFire = !!(info.GetDamageType() & DMG_BURN ); // check the damage type [mtw]
  3980. bool bDamageIsFromOpponent = pAttacker != NULL && IsOtherEnemy( pAttacker );
  3981. // Check "Goose Chase" achievement
  3982. if ( m_bIsDefusing && ( m_gooseChaseStep == GC_NONE ) && bDamageIsFromOpponent && pAttacker )
  3983. {
  3984. CTeam *pAttackerTeam = GetGlobalTeam( pAttacker->GetTeamNumber() );
  3985. if ( pAttackerTeam )
  3986. {
  3987. // count enemies
  3988. int livingEnemies = 0;
  3989. for ( int iPlayer=0; iPlayer < pAttackerTeam->GetNumPlayers(); iPlayer++ )
  3990. {
  3991. CCSPlayer *pPlayer = ToCSPlayer( pAttackerTeam->GetPlayer( iPlayer ) );
  3992. Assert( pPlayer );
  3993. if ( !pPlayer )
  3994. continue;
  3995. Assert( pPlayer->GetTeamNumber() == pAttackerTeam->GetTeamNumber() );
  3996. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  3997. {
  3998. livingEnemies++;
  3999. }
  4000. }
  4001. //Must be last enemy alive;
  4002. if (livingEnemies == 1 )
  4003. {
  4004. m_gooseChaseStep = GC_SHOT_DURING_DEFUSE;
  4005. m_pGooseChaseDistractingPlayer = pAttacker;
  4006. }
  4007. }
  4008. }
  4009. // score penalty for damage to teammates, regardless of whether it is actually applied
  4010. if ( bDamageIsFromTeammate || bDamageIsFromSelf )
  4011. {
  4012. CSGameRules()->ScoreFriendlyFire( pAttacker, flDamage );
  4013. }
  4014. // warn about team attacks
  4015. // ignoring the FF check so both players are notified that they hit their teammate [mtw]
  4016. // don't do this when a player is hurt by a molotov [mtw]
  4017. if ( bDamageIsFromTeammate && !bDamageIsFromFire && pAttacker )
  4018. {
  4019. if ( !(pAttacker->m_iDisplayHistoryBits & DHF_FRIEND_INJURED ) )
  4020. {
  4021. ClientPrint( pAttacker, HUD_PRINTCENTER, "#Hint_try_not_to_injure_teammates" );
  4022. pAttacker->m_iDisplayHistoryBits |= DHF_FRIEND_INJURED;
  4023. }
  4024. // [Jason] Change the constant time interval to be a convar instead (was 1.0f before )
  4025. if ( (pAttacker->m_flLastAttackedTeammate + CS_WarnFriendlyDamageInterval.GetInt() ) < gpGlobals->curtime )
  4026. {
  4027. pAttacker->m_flLastAttackedTeammate = gpGlobals->curtime;
  4028. // tell the rest of this player's team
  4029. for ( int i=1; i<=gpGlobals->maxClients; ++i )
  4030. {
  4031. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  4032. if ( pPlayer && IsOtherSameTeam( pPlayer->GetTeamNumber() ) && !IsOtherEnemy( pPlayer->entindex() ) )
  4033. {
  4034. ClientPrint( pPlayer, HUD_PRINTTALK, "#Cstrike_TitlesTXT_Game_teammate_attack", CFmtStr( "#ENTNAME[%d]%s", pAttacker->entindex(), pAttacker->GetPlayerName() ) );
  4035. }
  4036. }
  4037. }
  4038. }
  4039. float fFriendlyFireDamageReductionRatio = 1.0f;
  4040. // if a player damages them self with a grenade, scale by the convar
  4041. if ( bDamageIsFromSelf && bDamageIsFromGrenade )
  4042. {
  4043. fFriendlyFireDamageReductionRatio = ff_damage_reduction_grenade_self.GetFloat();
  4044. }
  4045. else if ( bDamageIsFromTeammate )
  4046. {
  4047. // reduce all other FF damage per convar settings
  4048. if ( bDamageIsFromGunfire )
  4049. {
  4050. fFriendlyFireDamageReductionRatio = ff_damage_reduction_bullets.GetFloat();
  4051. }
  4052. else if ( bDamageIsFromGrenade )
  4053. {
  4054. fFriendlyFireDamageReductionRatio = ff_damage_reduction_grenade.GetFloat();
  4055. }
  4056. else
  4057. {
  4058. fFriendlyFireDamageReductionRatio = ff_damage_reduction_other.GetFloat();
  4059. }
  4060. if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() )
  4061. fFriendlyFireDamageReductionRatio = 0;
  4062. }
  4063. flDamage *= fFriendlyFireDamageReductionRatio;
  4064. float fHeavyArmorDamageReductionRatio = 0.85f;
  4065. if ( CSGameRules() && CSGameRules()->IsPlayingCooperativeGametype() && IsBot() )
  4066. fHeavyArmorDamageReductionRatio = 0.35f;
  4067. if ( HasHeavyArmor() )
  4068. flDamage *= fHeavyArmorDamageReductionRatio;
  4069. // TODO[pmf]: we should be able to replace all this below with pWeapon = info.GetWeapon()
  4070. const CCSWeaponInfo* pFlinchInfoSource = NULL;
  4071. CCSPlayer *pInflictorPlayer = ToCSPlayer( info.GetInflictor() );
  4072. CWeaponCSBase *pInflictorWeapon = NULL;
  4073. if ( pInflictorPlayer )
  4074. {
  4075. pInflictorWeapon = pInflictorPlayer->GetActiveCSWeapon();
  4076. if ( pInflictorWeapon )
  4077. {
  4078. pFlinchInfoSource = &pInflictorWeapon->GetCSWpnData();
  4079. }
  4080. }
  4081. CBaseCSGrenadeProjectile* pGrenade = dynamic_cast< CBaseCSGrenadeProjectile* >( pInflictor );
  4082. if ( !pFlinchInfoSource )
  4083. {
  4084. if ( pGrenade )
  4085. {
  4086. pFlinchInfoSource = pGrenade->m_pWeaponInfo;
  4087. }
  4088. }
  4089. // special case for inferno (caused by molotov projectiles )
  4090. if ( !pFlinchInfoSource )
  4091. {
  4092. if ( pInflictor->ClassMatches( "inferno" ) )
  4093. {
  4094. pFlinchInfoSource = GetWeaponInfo( WEAPON_MOLOTOV );
  4095. }
  4096. }
  4097. if ( pAttacker )
  4098. {
  4099. // [paquin. forest] if this is blast damage, and we haven't opted out with a cvar,
  4100. // we need to get the armor ratio out of the inflictor
  4101. if( info.GetDamageType() & DMG_BURN )
  4102. {
  4103. // (DDK ) Ideally we'd use the info's weapon information instead of damage type, but this field appears to be unused and not available when passing this thru
  4104. pAttacker->AddBurnDamageDelt( entindex() );
  4105. }
  4106. if ( info.GetDamageType() & DMG_BLAST )
  4107. {
  4108. // [paquin] if we know this is a grenade, use it's armor ratio, otherwise
  4109. // use the he grenade armor ratio
  4110. const CCSWeaponInfo* pWeaponInfo;
  4111. if ( pGrenade && pGrenade->m_pWeaponInfo )
  4112. {
  4113. pWeaponInfo = pGrenade->m_pWeaponInfo;
  4114. }
  4115. else
  4116. {
  4117. pWeaponInfo = GetWeaponInfo( WEAPON_HEGRENADE );
  4118. }
  4119. if ( pWeaponInfo )
  4120. {
  4121. flArmorRatio *= pWeaponInfo->GetArmorRatio( pInflictorWeapon ? pInflictorWeapon->GetEconItemView() : NULL );
  4122. }
  4123. }
  4124. else
  4125. {
  4126. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfoFromDamageInfo(info);
  4127. if ( pWeaponInfo )
  4128. {
  4129. flArmorRatio *= pWeaponInfo->GetArmorRatio( pInflictorWeapon ? pInflictorWeapon->GetEconItemView() : NULL );
  4130. if ( info.GetDamageType() & DMG_BULLET && bDamageIsFromOpponent )
  4131. {
  4132. CCS_GameStats.Event_ShotHit( pAttacker, info ); // [pmf] Should this be done AFTER damage reduction?
  4133. }
  4134. }
  4135. }
  4136. }
  4137. float fDamageToHealth = flDamage;
  4138. float fDamageToArmor = 0;
  4139. float fHeavyArmorBonus = 1.0f;
  4140. if ( HasHeavyArmor() )
  4141. {
  4142. flArmorRatio *= 0.5f;
  4143. flArmorBonus = 0.33f;
  4144. fHeavyArmorBonus = 0.33f;
  4145. }
  4146. // Deal with Armour
  4147. bool bDamageTypeAppliesToArmor = ( info.GetDamageType() == DMG_GENERIC ) ||
  4148. ( info.GetDamageType() & (DMG_BULLET | DMG_BLAST | DMG_CLUB | DMG_SLASH) );
  4149. if ( bDamageTypeAppliesToArmor && ArmorValue() && IsArmored( m_LastHitGroup ) )
  4150. {
  4151. fDamageToHealth = flDamage * flArmorRatio;
  4152. fDamageToArmor = (flDamage - fDamageToHealth ) * (flArmorBonus * fHeavyArmorBonus);
  4153. int armorValue = ArmorValue();
  4154. // Does this use more armor than we have?
  4155. if (fDamageToArmor > armorValue )
  4156. {
  4157. fDamageToHealth = flDamage - armorValue / flArmorBonus;
  4158. fDamageToArmor = armorValue;
  4159. armorValue = 0;
  4160. }
  4161. else
  4162. {
  4163. if ( fDamageToArmor < 0 )
  4164. fDamageToArmor = 1;
  4165. armorValue -= fDamageToArmor;
  4166. }
  4167. m_lastDamageArmor = (int )fDamageToArmor;
  4168. SetArmorValue(armorValue );
  4169. // [tj] Handle headshot-surviving achievement
  4170. if ( ( m_LastHitGroup == HITGROUP_HEAD ) && bDamageIsFromGunfire )
  4171. {
  4172. if ( flDamage > GetHealth() && fDamageToHealth < GetHealth() )
  4173. {
  4174. m_bSurvivedHeadshotDueToHelmet = true;
  4175. }
  4176. }
  4177. flDamage = fDamageToHealth;
  4178. info.SetDamage( flDamage );
  4179. if ( ArmorValue() <= 0.0 )
  4180. {
  4181. m_bHasHelmet = false;
  4182. m_bHasHeavyArmor = false;
  4183. }
  4184. if( !(info.GetDamageType() & DMG_FALL ) && !(info.GetDamageType() & DMG_BURN ) && !(info.GetDamageType() & DMG_BLAST ) )
  4185. {
  4186. Pain( pAttacker, true /*has armor*/, info.GetDamageType() );
  4187. }
  4188. }
  4189. else
  4190. {
  4191. m_lastDamageArmor = 0;
  4192. if( !(info.GetDamageType() & DMG_FALL ) )
  4193. Pain( pAttacker, false /*no armor*/, info.GetDamageType() );
  4194. }
  4195. CEconItemView *pItem = NULL;
  4196. if ( pInflictorWeapon != NULL || ( pGrenade && fDamageToHealth > 0 ) )
  4197. {
  4198. if ( !pGrenade )
  4199. pItem = pInflictorWeapon->GetEconItemView();
  4200. // The word "flinch" actually means "tagging" here which reduces your movement velocity
  4201. int nKnifeSpeed = 250;
  4202. float fFlinchModifier = 1.0;// ShouldDoLargeFlinch( info, m_LastHitGroup ) ? pFlinchInfoSource->GetFlinchVelocityModifierLarge( pInflictorWeapon->GetEconItemView() ) : pFlinchInfoSource->GetFlinchVelocityModifierSmall( pInflictorWeapon->GetEconItemView() );
  4203. float flFlinchModLarge = pFlinchInfoSource->GetFlinchVelocityModifierLarge( pItem );
  4204. float flFlinchModSmall = pFlinchInfoSource->GetFlinchVelocityModifierSmall( pItem );
  4205. // if grenade, scale by the damage
  4206. if ( pGrenade )
  4207. {
  4208. float flScale = 1.0f - ( fDamageToHealth / 40.0f );
  4209. flFlinchModLarge += ( flScale * 1.05 );
  4210. flFlinchModLarge = MIN( 1.5f, flFlinchModLarge );
  4211. }
  4212. // apply the minimum large flinch amount on the first hit and on subsequent hits,
  4213. // apply a portion of the small amount - less as we apply more
  4214. m_flFlinchStack = MIN( m_flFlinchStack, MIN( flFlinchModLarge, flFlinchModLarge - ( 1.0 - m_flFlinchStack ) * flFlinchModSmall ) );
  4215. // don't modify m_flFlinchStack, keep it raw because it will decay in Think
  4216. fFlinchModifier = m_flFlinchStack;
  4217. //Msg( "%s: m_flFlinchStack = %f\n", GetPlayerName(), m_flFlinchStack );
  4218. // scale this by a global convar value
  4219. fFlinchModifier *= mp_tagging_scale.GetFloat();
  4220. float flHeavyArmorFlinchBonus = m_bHasHeavyArmor ? CS_PLAYER_HEAVYARMOR_FLINCH_MODIFIER : 1;
  4221. fFlinchModifier *= flHeavyArmorFlinchBonus;
  4222. //float flExosuitFlinchBonus = m_bHasExosuit ? CS_PLAYER_EXOSUIT_FLINCH_MODIFIER : 1;
  4223. //fFlinchModifier *= flExosuitFlinchBonus;
  4224. // get the player's current max speed based on their weapon
  4225. float flWepMaxSpeed = GetActiveCSWeapon() ? GetActiveCSWeapon()->GetMaxSpeed() : nKnifeSpeed;
  4226. // this is the value we use to scale the above fFlinchModifier -
  4227. // knives receive less, AKs receive a bit more, etc
  4228. float flLocalWepScaler = MAX( 0.15, ( flWepMaxSpeed - 120 ) / ( nKnifeSpeed - 120 ) ) * 0.8f;
  4229. flLocalWepScaler += 0.08;
  4230. fFlinchModifier = ( fFlinchModifier * flLocalWepScaler );
  4231. // the held weapon also determines what the tagging cap should be
  4232. // since it's accumulative, we want to be able to cap it so we don't keep getting more
  4233. // tagged the more someone shoots us
  4234. float flRatio = (MIN( 1.0, ( ( flWepMaxSpeed - 80 ) / ( nKnifeSpeed - 80 ) ) ) * 1.2f) - 0.08f;
  4235. //float flClampMin = MAX( 0.2, flRatio - ( 0.65 * ( 1 + ( 1.0 - flRatio ) ) ) );
  4236. float flClampMin = MAX( 0.2, (flRatio / 4) );
  4237. float flClampMax = ( flFlinchModLarge > 0.65 ) ? flFlinchModLarge : 0.65;
  4238. // do the clamp
  4239. fFlinchModifier = clamp( fFlinchModifier, flClampMin, flClampMax );
  4240. // reduce stamina slightly
  4241. m_flStamina = clamp( m_flStamina + ( 8 * ( 1.0 - fFlinchModifier ) ), 0.0f, sv_staminamax.GetFloat() );
  4242. // lerp between no flinch (all damage reduced to 0) to specified flinch (full damage)
  4243. SetFlinchVelocityModifier( Lerp( fFriendlyFireDamageReductionRatio, 1.0f, fFlinchModifier ) );
  4244. //Msg( "%s: flClampMin = %f, m_flFlinchStack = %f, fFlinchModifier = %f\n", GetPlayerName(), flClampMin, m_flFlinchStack, fFlinchModifier );
  4245. }
  4246. // keep track of amount of damage last sustained
  4247. m_lastDamageAmount = flDamage;
  4248. // round damage to integer
  4249. m_lastDamageHealth = (int )flDamage;
  4250. info.SetDamage( m_lastDamageHealth );
  4251. #if REPORT_PLAYER_DAMAGE
  4252. // damage output spew
  4253. char dmgtype[64];
  4254. CTakeDamageInfo::DebugGetDamageTypeString( info.GetDamageType(), dmgtype, sizeof(dmgtype ) );
  4255. if ( info.GetDamageType() & DMG_HEADSHOT )
  4256. Q_strncat(dmgtype, "HEADSHOT", sizeof(dmgtype ) );
  4257. char outputString[256];
  4258. Q_snprintf( outputString, sizeof(outputString ), "%f: Player %s incoming %f damage from %s, type %s; applied %d health and %d armor\n",
  4259. gpGlobals->curtime, GetPlayerName(),
  4260. inputInfo.GetDamage(), info.GetInflictor()->GetDebugName(), dmgtype,
  4261. m_lastDamageHealth, m_lastDamageArmor );
  4262. Msg(outputString );
  4263. #endif
  4264. if ( info.GetDamage() <= 0 )
  4265. return 0;
  4266. CSingleUserAndReplayRecipientFilter user( this );
  4267. user.MakeReliable();
  4268. CCSUsrMsg_Damage msg;
  4269. msg.set_amount( (int )info.GetDamage() );
  4270. msg.set_victim_entindex( entindex() );
  4271. const Vector& inflictor = info.GetInflictor()->WorldSpaceCenter();
  4272. msg.mutable_inflictor_world_pos()->set_x( inflictor.x );
  4273. msg.mutable_inflictor_world_pos()->set_y( inflictor.y );
  4274. msg.mutable_inflictor_world_pos()->set_z( inflictor.z );
  4275. SendUserMessage( user, CS_UM_Damage, msg );
  4276. // Do special explosion damage effect
  4277. if ( info.GetDamageType() & DMG_BLAST )
  4278. {
  4279. OnDamagedByExplosion( info );
  4280. }
  4281. if ( info.GetDamageType() & DMG_BURN )
  4282. {
  4283. m_fMolotovDamageTime = gpGlobals->curtime;
  4284. }
  4285. if( m_lowHealthGoalTime == 0.0f && GetHealth() - info.GetDamage() <= AchievementConsts::StillAlive_MaxHealthLeft )
  4286. {
  4287. // we're really low on health...
  4288. // set an achievement timer in case we can stay alive with this much health for a while
  4289. m_lowHealthGoalTime = gpGlobals->curtime + 30.0f;
  4290. }
  4291. // [menglish] Achievement award for kill stealing i.e. killing an enemy who was very damaged from other players <--- "LOL" -mtw
  4292. // [Forrest] Moved this check before RecordDamageTaken so that the damage currently being dealt by this player
  4293. // won't disqualify them from getting the achievement.
  4294. if(GetHealth() - info.GetDamage() <= 0 && GetHealth() <= AchievementConsts::KillLowDamage_MaxHealthLeft )
  4295. {
  4296. bool onlyDamage = true;
  4297. if( pAttacker && IsOtherEnemy( pAttacker ) )
  4298. {
  4299. //Verify that the killer has not done damage to this player beforehand
  4300. FOR_EACH_LL( m_DamageList, i )
  4301. {
  4302. if ( m_DamageList[i]->GetPlayerRecipientPtr() == this && m_DamageList[i]->GetPlayerDamagerPtr() == pAttacker )
  4303. {
  4304. onlyDamage = false;
  4305. break;
  4306. }
  4307. }
  4308. if ( onlyDamage )
  4309. {
  4310. pAttacker->AwardAchievement(CSKillLowDamage );
  4311. }
  4312. }
  4313. }
  4314. //
  4315. // this is the actual damage applied to the player and not the raw damage that was output from the weapon
  4316. int nHealthRemoved = (GetHealth() < info.GetDamage()) ? GetHealth() : info.GetDamage();
  4317. if ( pAttacker )
  4318. {
  4319. // Record for the shooter
  4320. pAttacker->RecordDamage( pAttacker, this, info.GetDamage(), nHealthRemoved );
  4321. if ( CSGameRules()->ShouldRecordMatchStats() )
  4322. {
  4323. pAttacker->m_iMatchStats_Damage.GetForModify( CSGameRules()->GetRoundsPlayed() ) += nHealthRemoved; // record in MatchStats'
  4324. // Keep track in QMM data
  4325. if ( pAttacker->m_uiAccountId && CSGameRules() )
  4326. {
  4327. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pAttacker->m_uiAccountId ) )
  4328. {
  4329. pQMM->m_iMatchStats_Damage[ CSGameRules()->GetRoundsPlayed() ] = pAttacker->m_iMatchStats_Damage.Get( CSGameRules()->GetRoundsPlayed() );
  4330. }
  4331. }
  4332. // utility damage
  4333. if ( bDamageIsFromGrenade || ( pInflictor->ClassMatches( "inferno" ) ) )
  4334. {
  4335. pAttacker->m_iMatchStats_UtilityDamage.GetForModify( CSGameRules( )->GetRoundsPlayed( ) ) += nHealthRemoved; // record in MatchStats'
  4336. // Keep track in QMM data
  4337. if ( pAttacker->m_uiAccountId && CSGameRules( ) )
  4338. {
  4339. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules( )->QueuedMatchmakingPlayersDataFind( pAttacker->m_uiAccountId ) )
  4340. {
  4341. pQMM->m_iMatchStats_UtilityDamage[ CSGameRules( )->GetRoundsPlayed( ) ] = pAttacker->m_iMatchStats_UtilityDamage.Get( CSGameRules( )->GetRoundsPlayed( ) );
  4342. }
  4343. }
  4344. }
  4345. }
  4346. // And for the victim (don't double-record if it is the same person)
  4347. if ( pAttacker != this )
  4348. {
  4349. RecordDamage( pAttacker, this, info.GetDamage(), nHealthRemoved );
  4350. }
  4351. // Track total number of health points removed
  4352. if ( bDamageIsFromOpponent && ( nHealthRemoved > 0 ) &&
  4353. !this->IsBot() && !CSGameRules()->IsWarmupPeriod() )
  4354. {
  4355. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( GetHumanPlayerAccountID() ) )
  4356. {
  4357. pQMM->m_numHealthPointsRemovedTotal += nHealthRemoved;
  4358. }
  4359. }
  4360. if ( bDamageIsFromOpponent && ( nHealthRemoved > 0 ) &&
  4361. !pAttacker->IsBot() && !CSGameRules()->IsWarmupPeriod() )
  4362. {
  4363. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pAttacker->GetHumanPlayerAccountID() ) )
  4364. {
  4365. pQMM->m_numHealthPointsDealtTotal += nHealthRemoved;
  4366. }
  4367. }
  4368. // if a player is dealing damage to a bot without a pistol, then they lose pistols only bonus
  4369. if ( bDamageIsFromOpponent && ( nHealthRemoved > 0 ) &&
  4370. IsBot() && CSGameRules()->IsPlayingCoopMission() )
  4371. {
  4372. if ( CWeaponCSBase *pWeaponDamage = dynamic_cast< CWeaponCSBase * >( info.GetWeapon() ) )
  4373. switch ( pWeaponDamage->GetWeaponType() )
  4374. {
  4375. case WEAPONTYPE_SUBMACHINEGUN:
  4376. case WEAPONTYPE_RIFLE:
  4377. case WEAPONTYPE_SHOTGUN:
  4378. case WEAPONTYPE_SNIPER_RIFLE:
  4379. case WEAPONTYPE_MACHINEGUN:
  4380. CSGameRules()->m_coopBonusPistolsOnly = false;
  4381. break;
  4382. }
  4383. }
  4384. if ( bDamageIsFromOpponent )
  4385. {
  4386. CSGameRules()->ScorePlayerDamage( pAttacker, info.GetDamage() );
  4387. }
  4388. else if ( bDamageIsFromTeammate )
  4389. {
  4390. // we need to check to see how much damage our attacker has done to teammates during this round and warm or kick as needed
  4391. int nDamageGivenThisRound = 0;
  4392. //CDamageRecord *pDamageList = pAttacker->GetDamageGivenList();
  4393. FOR_EACH_LL( pAttacker->GetDamageList(), i )
  4394. {
  4395. if ( !pAttacker->GetDamageList()[i] )
  4396. continue;
  4397. if ( pAttacker->GetDamageList()[i]->GetPlayerDamagerPtr() != pAttacker )
  4398. continue;
  4399. CCSPlayer *pDamageGivenListPlayer = pAttacker->GetDamageList()[i]->GetPlayerRecipientPtr();
  4400. if ( !pDamageGivenListPlayer )
  4401. continue;
  4402. int nGivenTeam = pDamageGivenListPlayer->GetTeamNumber();
  4403. if( ( pDamageGivenListPlayer != pAttacker ) && pAttacker->IsOtherSameTeam( nGivenTeam ) && !IsOtherEnemy( pDamageGivenListPlayer ) )
  4404. {
  4405. nDamageGivenThisRound += pAttacker->GetDamageList()[i]->GetActualHealthRemoved();
  4406. }
  4407. }
  4408. bool bIsPlayingOffline = CSGameRules() && CSGameRules()->IsPlayingOffline();
  4409. if ( mp_autokick.GetBool() && !bIsPlayingOffline )
  4410. {
  4411. if ( mp_spawnprotectiontime.GetInt() > 0 && CSGameRules() && CSGameRules()->GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() )
  4412. {
  4413. if ( nDamageGivenThisRound > mp_td_spawndmgthreshold.GetInt() )
  4414. {
  4415. // if we've already warned, but haven't warned this round, kick them or if we already have given a warning previously for doing too much in general
  4416. if ( (pAttacker->m_bTDGaveProtectionWarning && !pAttacker->m_bTDGaveProtectionWarningThisRound) || (pAttacker->m_nTeamDamageGivenForMatch + nDamageGivenThisRound) > mp_td_dmgtowarn.GetInt() )
  4417. {
  4418. if ( sv_kick_ban_duration.GetInt() > 0 )
  4419. {
  4420. CSGameRules()->SendKickBanToGC( pAttacker, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_THSpawn );
  4421. // don't roll the kick command into this, it will fail on a lan, where kickid will go through
  4422. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pAttacker->GetUserID() ) );
  4423. }
  4424. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For doing too much team damage\n", pAttacker->GetUserID(), bIsPlayingOffline ? 0 : 1 ) );
  4425. }
  4426. else if ( !pAttacker->m_bTDGaveProtectionWarningThisRound )
  4427. {
  4428. pAttacker->m_bTDGaveProtectionWarningThisRound = true;
  4429. pAttacker->m_bTDGaveProtectionWarning = true;
  4430. // give a warning
  4431. ClientPrint( pAttacker, HUD_PRINTTALK, "#Cstrike_TitlesTXT_Hint_warning_team_damage_start" );
  4432. }
  4433. }
  4434. }
  4435. if ( (pAttacker->m_nTeamDamageGivenForMatch + nDamageGivenThisRound) > mp_td_dmgtowarn.GetInt() )
  4436. {
  4437. if ( (m_flLastTHWarningTime + 5.0f) < gpGlobals->curtime )
  4438. {
  4439. m_flLastTHWarningTime = gpGlobals->curtime;
  4440. ClientPrint( pAttacker, HUD_PRINTTALK, "#Cstrike_TitlesTXT_Hint_warning_team_damage" );
  4441. }
  4442. if ( (pAttacker->m_nTeamDamageGivenForMatch + nDamageGivenThisRound) > mp_td_dmgtokick.GetInt() )
  4443. {
  4444. if ( sv_kick_ban_duration.GetInt() > 0 )
  4445. {
  4446. CSGameRules()->SendKickBanToGC( pAttacker, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_THLimit );
  4447. // don't roll the kick command into this, it will fail on a lan, where kickid will go through
  4448. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pAttacker->GetUserID() ) );
  4449. }
  4450. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For doing too much team damage\n", pAttacker->GetUserID(), bIsPlayingOffline ? 0 : 1 ) );
  4451. }
  4452. }
  4453. //Msg( "nDamageGiven from %s is %d, m_nTeamDamageGivenForMatch is %d\n", pAttacker->GetPlayerName(), nDamageGivenThisRound, pAttacker->m_nTeamDamageGivenForMatch );
  4454. }
  4455. }
  4456. }
  4457. else
  4458. {
  4459. RecordDamage( NULL, this, info.GetDamage(), nHealthRemoved ); //damaged by a null player - likely the world
  4460. }
  4461. m_vecTotalBulletForce += info.GetDamageForce();
  4462. gamestats->Event_PlayerDamage( this, info );
  4463. return CBaseCombatCharacter::OnTakeDamage( info );
  4464. }
  4465. //MIKETODO: this probably should let the shield model catch the trace attacks.
  4466. bool CCSPlayer::IsHittingShield( const Vector &vecDirection, trace_t *ptr )
  4467. {
  4468. if ( HasShield() == false )
  4469. return false;
  4470. if ( IsShieldDrawn() == false )
  4471. return false;
  4472. float flDot;
  4473. Vector vForward;
  4474. Vector2D vec2LOS = vecDirection.AsVector2D();
  4475. AngleVectors( GetLocalAngles(), &vForward );
  4476. Vector2DNormalize( vForward.AsVector2D() );
  4477. Vector2DNormalize( vec2LOS );
  4478. flDot = DotProduct2D ( vec2LOS , vForward.AsVector2D() );
  4479. if ( flDot < -0.87f )
  4480. return true;
  4481. return false;
  4482. }
  4483. void CCSPlayer::AwardAchievement( int iAchievement, int iCount )
  4484. {
  4485. if ( IsControllingBot() )
  4486. {
  4487. // Players controlling bots cannot earn achievements
  4488. return;
  4489. }
  4490. BaseClass::AwardAchievement( iAchievement, iCount );
  4491. }
  4492. void CCSPlayer::ClearGunGameImmunity( void )
  4493. {
  4494. // Fired a shot so no longer immune
  4495. m_bGunGameImmunity = false;
  4496. m_fImmuneToGunGameDamageTime = 0.0f;
  4497. }
  4498. ConVar mp_flinch_punch_scale( "mp_flinch_punch_scale", "3", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Scalar for first person view punch when getting hit." );
  4499. //ConVar aimpunch_fix( "aimpunch_fix", "0" );
  4500. void CCSPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  4501. {
  4502. bool bShouldBleed = true;
  4503. bool bShouldSpark = false;
  4504. bool bHitShield = IsHittingShield( vecDir, ptr );
  4505. CBasePlayer *pAttacker = (CBasePlayer* )ToBasePlayer( info.GetAttacker() );
  4506. // show blood for firendly fire only if FF is on
  4507. if ( pAttacker && IsOtherSameTeam( pAttacker->GetTeamNumber() ) && !IsOtherEnemy( pAttacker->entindex() ) )
  4508. bShouldBleed = CSGameRules()->IsFriendlyFireOn();
  4509. if ( m_takedamage != DAMAGE_YES )
  4510. return;
  4511. m_LastHitGroup = ptr->hitgroup;
  4512. m_nForceBone = ptr->physicsbone; //Save this bone for ragdoll
  4513. float flDamage = info.GetDamage();
  4514. QAngle punchAngle;
  4515. float flAng;
  4516. bool hitByBullet = false;
  4517. bool hitByGrenadeProjectile = false;
  4518. bool bHeadShot = false;
  4519. float flBodyDamageScale = (GetTeamNumber() == TEAM_CT) ? mp_damage_scale_ct_body.GetFloat() : mp_damage_scale_t_body.GetFloat();
  4520. float flHeadDamageScale = (GetTeamNumber() == TEAM_CT) ? mp_damage_scale_ct_head.GetFloat() : mp_damage_scale_t_head.GetFloat();
  4521. // heavy armor reduces headshot damage by have of what it is, so it does x2 instead of x4
  4522. if ( HasHeavyArmor() )
  4523. flHeadDamageScale = flHeadDamageScale * 0.5;
  4524. if( m_bGunGameImmunity )
  4525. {
  4526. bShouldBleed = false;
  4527. }
  4528. else if( bHitShield )
  4529. {
  4530. flDamage = 0;
  4531. bShouldBleed = false;
  4532. bShouldSpark = true;
  4533. }
  4534. else if( info.GetDamageType() & DMG_SHOCK )
  4535. {
  4536. bShouldBleed = false;
  4537. }
  4538. else if( info.GetDamageType() & DMG_BLAST )
  4539. {
  4540. if ( ArmorValue() > 0 )
  4541. bShouldBleed = false;
  4542. if ( bShouldBleed == true )
  4543. {
  4544. // punch view if we have no armor
  4545. punchAngle = GetRawAimPunchAngle();
  4546. punchAngle.x = mp_flinch_punch_scale.GetFloat() * flDamage * -0.1;
  4547. if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -4 )
  4548. punchAngle.x = mp_flinch_punch_scale.GetFloat() * -4;
  4549. SetAimPunchAngle( punchAngle );
  4550. }
  4551. }
  4552. else
  4553. {
  4554. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfoFromDamageInfo( info );
  4555. CWeaponCSBase *pWeapon = dynamic_cast<CWeaponCSBase*>(info.GetWeapon());
  4556. CEconItemView *pItem = NULL;
  4557. if ( pWeapon )
  4558. pItem = pWeapon->GetEconItemView();
  4559. if ( pWeaponInfo )
  4560. {
  4561. hitByBullet = IsGunWeapon( pWeaponInfo->GetWeaponType( pItem ) );
  4562. hitByGrenadeProjectile = ( ( pWeaponInfo->GetWeaponType( pItem ) == WEAPONTYPE_GRENADE ) && ( info.GetDamageType() & DMG_CLUB ) != 0 );
  4563. }
  4564. switch ( ptr->hitgroup )
  4565. {
  4566. case HITGROUP_GENERIC:
  4567. break;
  4568. case HITGROUP_HEAD:
  4569. if ( m_bHasHelmet && !hitByGrenadeProjectile )
  4570. {
  4571. // bShouldBleed = false;
  4572. bShouldSpark = true;
  4573. }
  4574. flDamage *= 4;
  4575. flDamage *= flHeadDamageScale;
  4576. if ( !m_bHasHelmet )
  4577. {
  4578. punchAngle = GetRawAimPunchAngle();
  4579. punchAngle.x += mp_flinch_punch_scale.GetFloat() * flDamage * -0.5;
  4580. if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -12 )
  4581. punchAngle.x = mp_flinch_punch_scale.GetFloat() * -12;
  4582. punchAngle.z = mp_flinch_punch_scale.GetFloat() * flDamage * random->RandomFloat(-1,1 );
  4583. if ( punchAngle.z < mp_flinch_punch_scale.GetFloat() * -9 )
  4584. punchAngle.z = mp_flinch_punch_scale.GetFloat() * -9;
  4585. else if ( punchAngle.z > mp_flinch_punch_scale.GetFloat() * 9 )
  4586. punchAngle.z = mp_flinch_punch_scale.GetFloat() * 9;
  4587. SetAimPunchAngle( punchAngle );
  4588. }
  4589. bHeadShot = true;
  4590. break;
  4591. case HITGROUP_CHEST:
  4592. flDamage *= 1.0;
  4593. flDamage *= flBodyDamageScale;
  4594. if ( ArmorValue() <= 0 )
  4595. flAng = -0.1;
  4596. else
  4597. flAng = -0.005;
  4598. punchAngle = GetRawAimPunchAngle();
  4599. punchAngle.x += mp_flinch_punch_scale.GetFloat() * flDamage * flAng;
  4600. if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -4 )
  4601. punchAngle.x = mp_flinch_punch_scale.GetFloat() * -4;
  4602. SetAimPunchAngle( punchAngle );
  4603. break;
  4604. case HITGROUP_STOMACH:
  4605. flDamage *= 1.25;
  4606. flDamage *= flBodyDamageScale;
  4607. if ( ArmorValue() <= 0 )
  4608. flAng = -0.1;
  4609. else
  4610. flAng = -0.005;
  4611. punchAngle = GetRawAimPunchAngle();
  4612. punchAngle.x += mp_flinch_punch_scale.GetFloat() * flDamage * flAng;
  4613. if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -4 )
  4614. punchAngle.x = mp_flinch_punch_scale.GetFloat() * -4;
  4615. SetAimPunchAngle( punchAngle );
  4616. break;
  4617. case HITGROUP_LEFTARM:
  4618. case HITGROUP_RIGHTARM:
  4619. flDamage *= 1.0;
  4620. flDamage *= flBodyDamageScale;
  4621. //
  4622. // punchAngle = GetRawAimPunchAngle();
  4623. // punchAngle.x = mp_flinch_punch_scale.GetFloat() * flDamage * -0.005;
  4624. //
  4625. // if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -2 )
  4626. // punchAngle.x = mp_flinch_punch_scale.GetFloat() * -2;
  4627. //
  4628. // SetAimPunchAngle( punchAngle );
  4629. //
  4630. break;
  4631. case HITGROUP_LEFTLEG:
  4632. case HITGROUP_RIGHTLEG:
  4633. flDamage *= 0.75;
  4634. flDamage *= flBodyDamageScale;
  4635. //
  4636. // punchAngle = GetRawAimPunchAngle();
  4637. // punchAngle.x = mp_flinch_punch_scale.GetFloat() * flDamage * -0.005;
  4638. //
  4639. // if ( punchAngle.x < mp_flinch_punch_scale.GetFloat() * -1 )
  4640. // punchAngle.x = mp_flinch_punch_scale.GetFloat() * -1;
  4641. //
  4642. // SetAimPunchAngle( punchAngle );
  4643. //
  4644. break;
  4645. default:
  4646. break;
  4647. }
  4648. }
  4649. // Since this code only runs on the server, make sure it shows the tempents it creates.
  4650. CDisablePredictionFiltering disabler;
  4651. if ( bShouldBleed )
  4652. {
  4653. // This does smaller splotches on the guy and splats blood on the world.
  4654. TraceBleed( flDamage, vecDir, ptr, info.GetDamageType() );
  4655. CEffectData data;
  4656. data.m_vOrigin = ptr->endpos;
  4657. data.m_vNormal = vecDir * -1;
  4658. data.m_nEntIndex = ptr->m_pEnt ? ptr->m_pEnt->entindex() : 0;
  4659. data.m_flMagnitude = flDamage;
  4660. // reduce blood effect if target has armor
  4661. if ( ArmorValue() > 0 )
  4662. data.m_flMagnitude *= 0.5f;
  4663. // reduce blood effect if target is hit in the helmet
  4664. if ( ptr->hitgroup == HITGROUP_HEAD && bShouldSpark )
  4665. data.m_flMagnitude = 1;
  4666. DispatchEffect( "csblood", data );
  4667. }
  4668. if ( ( ptr->hitgroup == HITGROUP_HEAD/* || bHitShield*/ ) && bShouldSpark ) // they hit a helmet
  4669. {
  4670. // show metal spark effect
  4671. //g_pEffects->Sparks( ptr->endpos, 1, 1, &ptr->plane.normal );
  4672. QAngle angle;
  4673. VectorAngles( ptr->plane.normal, angle );
  4674. DispatchParticleEffect( "impact_helmet_headshot", ptr->endpos, angle );
  4675. }
  4676. if ( !bHitShield )
  4677. {
  4678. CTakeDamageInfo subInfo = info;
  4679. subInfo.SetDamage( flDamage );
  4680. float impulseMultiplier = 1.0f;
  4681. if ( hitByBullet )
  4682. {
  4683. impulseMultiplier = phys_playerscale.GetFloat();
  4684. if ( bHeadShot )
  4685. {
  4686. subInfo.AddDamageType( DMG_HEADSHOT );
  4687. impulseMultiplier *= phys_headshotscale.GetFloat();
  4688. }
  4689. }
  4690. if ( hitByGrenadeProjectile )
  4691. {
  4692. impulseMultiplier = 0.0f;
  4693. }
  4694. subInfo.SetDamageForce( info.GetDamageForce() * impulseMultiplier );
  4695. AddMultiDamage( subInfo, this );
  4696. }
  4697. }
  4698. void CCSPlayer::Reset( bool resetScore )
  4699. {
  4700. if( resetScore )
  4701. {
  4702. RemoveNemesisRelationships();
  4703. ResetFragCount();
  4704. ResetAssistsCount();
  4705. ResetDeathCount();
  4706. ClearScore();
  4707. ClearContributionScore();
  4708. // when score is reset, make sure
  4709. m_numRoundsSurvived = m_maxNumRoundsSurvived = 0;
  4710. m_longestLife = -1.0f;
  4711. m_iTotalCashSpent = 0;
  4712. m_iCashSpentThisRound = 0;
  4713. m_nTeamDamageGivenForMatch = 0;
  4714. m_bTDGaveProtectionWarning = false;
  4715. m_bTDGaveProtectionWarningThisRound = false;
  4716. m_nEndMatchNextMapVote = -1;
  4717. m_iEnemyKills = 0;
  4718. m_iEnemyKillHeadshots = 0;
  4719. m_iEnemy3Ks = 0;
  4720. m_iEnemy4Ks = 0;
  4721. m_iEnemy5Ks = 0;
  4722. m_iEnemyKillsAgg = 0;
  4723. m_numFirstKills = 0;
  4724. m_numClutchKills = 0;
  4725. m_numPistolKills = 0;
  4726. m_numSniperKills = 0;
  4727. m_iRoundsWon = 0;
  4728. m_bLastKillUsedUniqueWeaponMatch = 0;
  4729. m_uniqueKillWeaponsMatch.RemoveAll();
  4730. }
  4731. ResetDamageCounters();
  4732. ResetAccount(); // remove all player cash
  4733. //remove any weapons they bought before the round started
  4734. RemoveAllItems( true );
  4735. //RemoveShield();
  4736. if ( !resetScore )
  4737. InitializeAccount(); // don't track initial amount as earned
  4738. else
  4739. InitializeAccount( CSGameRules()->GetStartMoney() );
  4740. // setting this to the current time prevents late-joining players from getting prioritized for receiving the defuser/bomb
  4741. m_fLastGivenDefuserTime = gpGlobals->curtime;
  4742. m_fLastGivenBombTime = gpGlobals->curtime;
  4743. }
  4744. //-----------------------------------------------------------------------------
  4745. // Purpose: Displays a hint message to the player
  4746. // Input : *pMessage -
  4747. // bDisplayIfDead -
  4748. // bOverrideClientSettings -
  4749. //-----------------------------------------------------------------------------
  4750. void CCSPlayer::HintMessage( const char *pMessage, bool bDisplayIfDead, bool bOverrideClientSettings )
  4751. {
  4752. if ( !bDisplayIfDead && !IsAlive() || !IsNetClient() || !m_pHintMessageQueue )
  4753. return;
  4754. if ( bOverrideClientSettings || m_bShowHints )
  4755. m_pHintMessageQueue->AddMessage( pMessage );
  4756. }
  4757. void CCSPlayer::ResetAccount()
  4758. {
  4759. m_iAccount = 0;
  4760. m_iAccountMoneyEarnedForNextRound = 0;
  4761. }
  4762. void CCSPlayer::InitializeAccount( int amount )
  4763. {
  4764. if ( ( amount == -1 ) && m_uiAccountId )
  4765. {
  4766. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  4767. {
  4768. amount = pQMM->m_cash;
  4769. }
  4770. }
  4771. m_iAccount = ( amount == -1 ) ? CSGameRules()->GetStartMoney() : amount;
  4772. m_iAccountMoneyEarnedForNextRound = 0;
  4773. int MaxAmount = CSGameRules()->GetMaxMoney();
  4774. Assert( m_iAccount >= 0 && m_iAccount <= MaxAmount );
  4775. m_iAccount = clamp<int, int, int>( m_iAccount, 0, MaxAmount );
  4776. if ( CSGameRules()->ShouldRecordMatchStats() )
  4777. {
  4778. m_iMatchStats_CashEarned.GetForModify( CSGameRules()->GetTotalRoundsPlayed() ) = m_iAccount;
  4779. }
  4780. // Keep track in QMM data
  4781. if ( m_uiAccountId && CSGameRules() )
  4782. {
  4783. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  4784. {
  4785. pQMM->m_cash = m_iAccount;
  4786. if ( CSGameRules()->ShouldRecordMatchStats() )
  4787. {
  4788. pQMM->m_iMatchStats_CashEarned[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_CashEarned.Get( CSGameRules()->GetTotalRoundsPlayed() );
  4789. }
  4790. }
  4791. }
  4792. }
  4793. bool CCSPlayer::AreAccountAwardsEnabled( PlayerCashAward::Type reason ) const
  4794. {
  4795. // no awards in the warmup period
  4796. if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() )
  4797. return false;
  4798. // cash awards for individual actions must be enabled for this game mode
  4799. if ( !mp_playercashawards.GetBool() )
  4800. return false;
  4801. return true;
  4802. }
  4803. void CCSPlayer::AddAccountAward( PlayerCashAward::Type reason )
  4804. {
  4805. Assert( reason != PlayerCashAward::NONE );
  4806. AddAccountAward(reason, CSGameRules()->PlayerCashAwardValue ( reason ) );
  4807. }
  4808. void CCSPlayer::AddAccountAward( PlayerCashAward::Type reason, int amount, const CWeaponCSBase *pWeapon )
  4809. {
  4810. if ( !AreAccountAwardsEnabled( reason ) )
  4811. return;
  4812. if ( amount == 0 )
  4813. return;
  4814. const char* awardReasonToken = NULL;
  4815. const char* sign_string = "+$";
  4816. const char* szWeaponName = NULL;
  4817. extern ConVar cash_player_killed_enemy_default;
  4818. extern ConVar cash_player_killed_enemy_factor;
  4819. int currentround = CSGameRules()->GetTotalRoundsPlayed();
  4820. switch ( reason )
  4821. {
  4822. case PlayerCashAward::KILL_TEAMMATE:
  4823. awardReasonToken = "#Player_Cash_Award_Kill_Teammate";
  4824. sign_string = "-$";
  4825. break;
  4826. case PlayerCashAward::KILLED_ENEMY:
  4827. awardReasonToken = "#Player_Cash_Award_Killed_Enemy_Generic";
  4828. // if award amount is non-default, use the verbose message.
  4829. if ( pWeapon && ( amount != cash_player_killed_enemy_default.GetInt() ))
  4830. {
  4831. szWeaponName = pWeapon->GetEconItemView()->GetItemDefinition()->GetItemBaseName();
  4832. awardReasonToken = "#Player_Cash_Award_Killed_Enemy";
  4833. }
  4834. // scale amount by kill award factor convar.
  4835. amount = RoundFloatToInt( amount * cash_player_killed_enemy_factor.GetFloat() );
  4836. // Match stats
  4837. if ( CSGameRules()->ShouldRecordMatchStats() )
  4838. {
  4839. m_iMatchStats_KillReward.GetForModify( currentround ) += amount;
  4840. // Keep track of Match stats in QMM data
  4841. if ( m_uiAccountId && CSGameRules() )
  4842. {
  4843. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  4844. {
  4845. pQMM->m_iMatchStats_KillReward[ currentround ] = m_iMatchStats_EquipmentValue.Get( currentround );
  4846. }
  4847. }
  4848. }
  4849. break;
  4850. case PlayerCashAward::BOMB_PLANTED:
  4851. awardReasonToken = "#Player_Cash_Award_Bomb_Planted";
  4852. break;
  4853. case PlayerCashAward::BOMB_DEFUSED:
  4854. awardReasonToken = "#Player_Cash_Award_Bomb_Defused";
  4855. break;
  4856. case PlayerCashAward::RESCUED_HOSTAGE:
  4857. awardReasonToken = "#Player_Cash_Award_Rescued_Hostage";
  4858. break;
  4859. case PlayerCashAward::INTERACT_WITH_HOSTAGE:
  4860. awardReasonToken = "#Player_Cash_Award_Interact_Hostage";
  4861. break;
  4862. case PlayerCashAward::RESPAWN:
  4863. awardReasonToken = "#Player_Cash_Award_Respawn";
  4864. break;
  4865. case PlayerCashAward::GET_KILLED:
  4866. awardReasonToken = "#Player_Cash_Award_Get_Killed";
  4867. break;
  4868. case PlayerCashAward::DAMAGE_HOSTAGE:
  4869. awardReasonToken = "#Player_Cash_Award_Damage_Hostage";
  4870. sign_string = "-$";
  4871. break;
  4872. case PlayerCashAward::KILL_HOSTAGE:
  4873. awardReasonToken = "#Player_Cash_Award_Kill_Hostage";
  4874. sign_string = "-$";
  4875. break;
  4876. default:
  4877. break;
  4878. }
  4879. char strAmount[64];
  4880. Q_snprintf( strAmount, sizeof( strAmount ), "%s%d", sign_string, abs( amount ));
  4881. // don't message 0 or negative values in coop
  4882. if ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() && amount <= 0 )
  4883. {
  4884. }
  4885. else
  4886. {
  4887. ClientPrint( this, HUD_PRINTTALK, awardReasonToken, strAmount, szWeaponName );
  4888. }
  4889. AddAccount( amount, true, false );
  4890. if ( dev_reportmoneychanges.GetBool() )
  4891. {
  4892. CCSBot *pBot = ToCSBot( m_hControlledBot.Get() );
  4893. if ( pBot )
  4894. Msg( "%s earned %d (for %s) (while being controlled by %s) (total money: %d)\n", pBot->GetPlayerName(), amount, awardReasonToken, GetPlayerName(), GetAccountBalance() );
  4895. else
  4896. Msg( "%s earned %d (for %s) (total money: %d)\n", GetPlayerName(), amount, awardReasonToken, GetAccountBalance() );
  4897. }
  4898. }
  4899. void CCSPlayer::AddAccountFromTeam( int amount, bool bTrackChange, TeamCashAward::Type reason )
  4900. {
  4901. // no awards in the warmup period
  4902. if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() )
  4903. return;
  4904. AddAccount( amount, bTrackChange, false, NULL );
  4905. if( IsControllingBot() )
  4906. {
  4907. // make sure we award team bonus to the actual player controlling the bot
  4908. m_PreControlData.m_iAccount = clamp( m_PreControlData.m_iAccount + amount, 0, CSGameRules()->GetMaxMoney() );
  4909. if ( CSGameRules() && CSGameRules()->IsPlayingClassic() &&
  4910. !CSGameRules()->IsWarmupPeriod() && !CSGameRules()->IsFreezePeriod() &&
  4911. ( amount > 0 ) )
  4912. { // Any money earned during the round cannot be used until next round starts.
  4913. m_PreControlData.m_iAccountMoneyEarnedForNextRound = clamp( ( int ) m_PreControlData.m_iAccountMoneyEarnedForNextRound + amount, 0, CSGameRules()->GetMaxMoney() );
  4914. }
  4915. if ( CSGameRules()->ShouldRecordMatchStats() )
  4916. {
  4917. m_iMatchStats_CashEarned.GetForModify( CSGameRules()->GetTotalRoundsPlayed() ) = m_PreControlData.m_iAccount;
  4918. }
  4919. // Keep track in QMM data
  4920. if ( m_uiAccountId && CSGameRules() )
  4921. {
  4922. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  4923. {
  4924. pQMM->m_cash = m_PreControlData.m_iAccount;
  4925. if ( CSGameRules()->ShouldRecordMatchStats() )
  4926. {
  4927. pQMM->m_iMatchStats_CashEarned[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_CashEarned.Get( CSGameRules()->GetTotalRoundsPlayed() );
  4928. }
  4929. }
  4930. }
  4931. if ( dev_reportmoneychanges.GetBool() )
  4932. {
  4933. CCSBot *pBot = ToCSBot( m_hControlledBot.Get() );
  4934. Msg( "%s earned %d (while controlling %s), Adding to Pre-Bot Control Data Account (total cached money: %d)\n", GetPlayerName(), amount, pBot ? pBot->GetPlayerName() : "NULL", m_PreControlData.m_iAccount );
  4935. }
  4936. }
  4937. }
  4938. void CCSPlayer::AddAccount( int amount, bool bTrackChange, bool bItemBought, const char *pItemName )
  4939. {
  4940. // we don't want to award a bot money that is being controlled by a player because the player is currently storing the bots money
  4941. if ( IsBot() && HasControlledByPlayer() )
  4942. return;
  4943. int iAccountStarting = m_iAccount;
  4944. m_iAccount += amount;
  4945. if ( CSGameRules() && CSGameRules()->IsPlayingClassic() &&
  4946. !CSGameRules()->IsWarmupPeriod() && !CSGameRules()->IsFreezePeriod() &&
  4947. ( amount > 0 ) )
  4948. { // Any money earned during the round cannot be used until next round starts.
  4949. m_iAccountMoneyEarnedForNextRound = clamp( (int) m_iAccountMoneyEarnedForNextRound + amount, 0, CSGameRules()->GetMaxMoney() );
  4950. }
  4951. if ( dev_reportmoneychanges.GetBool() && amount < 0 && bItemBought )
  4952. {
  4953. CCSBot *pBot = ToCSBot( m_hControlledBot.Get() );
  4954. if ( pBot )
  4955. Msg( "%s spent %d on a %s (while being controlled by %s) (total left: %d)\n", pBot->GetPlayerName(), amount, pItemName, GetPlayerName(), m_iAccount.Get() );
  4956. else
  4957. Msg( "%s spent %d on a %s (total left: %d)\n", GetPlayerName(), amount, pItemName, m_iAccount.Get() );
  4958. }
  4959. if ( bTrackChange )
  4960. {
  4961. // note: if we lost money, but didn't make a purchase, we don't log it as money spent or earned, it's a penalty
  4962. if ( amount > 0 )
  4963. {
  4964. CCS_GameStats.Event_MoneyEarned( this, amount );
  4965. }
  4966. else if ( amount < 0 && bItemBought )
  4967. {
  4968. CCS_GameStats.Event_MoneySpent( this, -amount, pItemName );
  4969. m_iTotalCashSpent += -amount;
  4970. m_iCashSpentThisRound += -amount;
  4971. }
  4972. }
  4973. m_iAccount = clamp( (int)m_iAccount, 0, CSGameRules()->GetMaxMoney() );
  4974. CSingleUserRecipientFilter user( this );
  4975. CCSUsrMsg_AdjustMoney msg;
  4976. msg.set_amount( amount );
  4977. SendUserMessage( user, CS_UM_AdjustMoney, msg );
  4978. if ( CSGameRules()->ShouldRecordMatchStats() )
  4979. {
  4980. m_iMatchStats_CashEarned.GetForModify( CSGameRules()->GetTotalRoundsPlayed() ) = m_iAccount;
  4981. }
  4982. // Keep track in QMM data
  4983. if ( m_uiAccountId && CSGameRules() && !IsControllingBot() )
  4984. {
  4985. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  4986. {
  4987. pQMM->m_cash = m_iAccount;
  4988. if ( CSGameRules()->ShouldRecordMatchStats() )
  4989. {
  4990. pQMM->m_iMatchStats_CashEarned[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_CashEarned.Get( CSGameRules()->GetTotalRoundsPlayed() );
  4991. }
  4992. }
  4993. }
  4994. if ( mp_logmoney.GetBool() && amount && ( m_iAccount != iAccountStarting ) )
  4995. {
  4996. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" money change %d%s%d = $%d%s%s%s%s%s\n",
  4997. GetPlayerName(),
  4998. entindex(),
  4999. GetNetworkIDString(),
  5000. GetTeam()->GetName(),
  5001. iAccountStarting, ( amount > 0 ) ? "+" : "", amount, m_iAccount.Get(),
  5002. bTrackChange ? " (tracked)" : "",
  5003. bItemBought ? " (purchase" : "",
  5004. ( pItemName && *pItemName ) ? ": " : "",
  5005. ( pItemName && *pItemName ) ? pItemName : "",
  5006. bItemBought ? ")" : ""
  5007. );
  5008. }
  5009. }
  5010. int CCSPlayer::AddDeathmatchKillScore( int nScore, CSWeaponID wepID, int iSlot, bool bIsAssist, const char* szVictim )
  5011. {
  5012. if ( !CSGameRules() || !CSGameRules()->IsPlayingGunGameDeathmatch() )
  5013. return 0;
  5014. if ( nScore <= 0 )
  5015. return 0;
  5016. int nBonus = 0;
  5017. if ( CSGameRules()->IsDMBonusActive() && CSGameRules()->GetDMBonusWeaponLoadoutSlot() == iSlot && !bIsAssist )
  5018. nBonus = ( (float)( mp_dm_bonus_percent.GetInt() ) / 100.0f ) * nScore;
  5019. const char* awardReasonToken = NULL;
  5020. // handle econ weapons
  5021. const char* szWeaponName = "unknown";
  5022. CEconItemView *pItem = Inventory()->GetItemInLoadout( GetTeamNumber(), iSlot );
  5023. if ( pItem )
  5024. {
  5025. szWeaponName = pItem->GetItemDefinition()->GetItemBaseName();
  5026. }
  5027. if ( bIsAssist )
  5028. {
  5029. if ( nScore == 1 )
  5030. awardReasonToken = "#Player_Point_Award_Assist_Enemy";
  5031. else
  5032. awardReasonToken = "#Player_Point_Award_Assist_Enemy_Plural";
  5033. szWeaponName = szVictim;
  5034. }
  5035. else
  5036. {
  5037. if ( nScore == 1 )
  5038. awardReasonToken = "#Player_Point_Award_Killed_Enemy";
  5039. else
  5040. awardReasonToken = "#Player_Point_Award_Killed_Enemy_Plural";
  5041. }
  5042. char strnScore[64];
  5043. if ( nBonus > 0 )
  5044. Q_snprintf( strnScore, sizeof( strnScore ), "%d (+%d)", abs( nScore ), abs( nBonus ) );
  5045. else
  5046. Q_snprintf( strnScore, sizeof( strnScore ), "%d", abs( nScore ));
  5047. ClientPrint( this, HUD_PRINTTALK, awardReasonToken, strnScore, szWeaponName );
  5048. AddContributionScore( nScore + nBonus );
  5049. // return if not the current leader
  5050. bool bCurrentLeader = true;
  5051. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  5052. {
  5053. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  5054. if ( pPlayer && pPlayer != this && GetScore() <= pPlayer->GetScore() )
  5055. {
  5056. bCurrentLeader = false;
  5057. break;
  5058. }
  5059. }
  5060. if ( bCurrentLeader )
  5061. {
  5062. //if we made it this far, we are the current leader
  5063. IGameEvent *event = gameeventmanager->CreateEvent( "gg_leader" );
  5064. if ( event )
  5065. {
  5066. event->SetInt( "playerid", GetUserID() );
  5067. gameeventmanager->FireEvent( event );
  5068. }
  5069. }
  5070. return nScore + nBonus;
  5071. }
  5072. void CCSPlayer::MarkAsNotReceivingMoneyNextRound( bool bAllowMoneyNextRound /*=false*/ )
  5073. {
  5074. m_receivesMoneyNextRound = bAllowMoneyNextRound;
  5075. // Keep track in QMM data
  5076. if ( m_uiAccountId && CSGameRules() )
  5077. {
  5078. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  5079. {
  5080. pQMM->m_bReceiveNoMoneyNextRound = !bAllowMoneyNextRound;
  5081. }
  5082. }
  5083. }
  5084. void CCSPlayer::ProcessSuicideAsKillReward()
  5085. {
  5086. // Don't give any rewards during warmup period or freezetime aka buytime
  5087. // If any human disconnects during buytime then a bot will replace their vacant spot on the team
  5088. // so enemies will have fodder to kill and get a kill reward
  5089. if ( CCSGameRules *pCSGameRules = CSGameRules() )
  5090. {
  5091. if ( pCSGameRules->IsWarmupPeriod() )
  5092. return;
  5093. if ( pCSGameRules->IsFreezePeriod() )
  5094. return;
  5095. }
  5096. // Find alive players on the enemy team and give them maximum possible kill reward
  5097. // tie break by an enemy player with the lowest amount of money
  5098. int myteam = GetTeamNumber();
  5099. int team = myteam;
  5100. switch ( team )
  5101. {
  5102. case TEAM_TERRORIST:
  5103. team = TEAM_CT;
  5104. break;
  5105. case TEAM_CT:
  5106. team = TEAM_TERRORIST;
  5107. break;
  5108. default:
  5109. return;
  5110. }
  5111. // Best bonus enemy
  5112. CCSPlayer *pBestEnemy = NULL;
  5113. int numBestBonusMoney = 0;
  5114. // Look at alive players on the team
  5115. for ( int nAttempt = 0; ( nAttempt < 3 ) && !pBestEnemy; ++ nAttempt )
  5116. {
  5117. for ( int playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum )
  5118. {
  5119. CCSPlayer *player = ( CCSPlayer * ) UTIL_PlayerByIndex( playerNum );
  5120. if ( !player )
  5121. continue;
  5122. if ( !player->IsAlive() )
  5123. continue;
  5124. if ( player->GetTeamNumber() != team )
  5125. continue;
  5126. if ( !player->AreAccountAwardsEnabled( PlayerCashAward::KILLED_ENEMY ) )
  5127. continue;
  5128. if ( ( nAttempt < 2 ) && player->IsControllingBot() )
  5129. continue;
  5130. if ( ( nAttempt < 1 ) && player->IsBot() )
  5131. continue;
  5132. // Let's see which guns this player has?
  5133. extern ConVar cash_player_killed_enemy_default;
  5134. int numBonusMoney = cash_player_killed_enemy_default.GetInt();
  5135. int arrSlots[] = { WEAPON_SLOT_RIFLE, WEAPON_SLOT_PISTOL };
  5136. for ( int k = 0; k < Q_ARRAYSIZE( arrSlots ); ++k )
  5137. {
  5138. CBaseCombatWeapon *pWpn = player->Weapon_GetSlot( arrSlots[ k ] );
  5139. CWeaponCSBase *pWpnCsBase = dynamic_cast< CWeaponCSBase * >( pWpn );
  5140. if ( !pWpnCsBase ) continue;
  5141. int numWpnKillAward = pWpnCsBase->GetKillAward();
  5142. if ( numWpnKillAward > numBonusMoney )
  5143. numBonusMoney = numWpnKillAward;
  5144. }
  5145. // See if this is a better player to reward?
  5146. if ( ( numBonusMoney > numBestBonusMoney ) ||
  5147. ( ( numBonusMoney == numBestBonusMoney ) && ( player->GetAccountBalance() < pBestEnemy->GetAccountBalance() ) ) )
  5148. {
  5149. pBestEnemy = player;
  5150. numBestBonusMoney = numBonusMoney;
  5151. }
  5152. }
  5153. }
  5154. // Give the player kill reward
  5155. extern ConVar cash_player_killed_enemy_factor;
  5156. int numDollarsEarned = RoundFloatToInt( numBestBonusMoney * cash_player_killed_enemy_factor.GetFloat() );
  5157. if ( pBestEnemy && ( numDollarsEarned > 0 ) )
  5158. {
  5159. pBestEnemy->AddAccountAward( PlayerCashAward::KILLED_ENEMY, numBestBonusMoney );
  5160. CFmtStr fmtDollarsEarned( "%u", numDollarsEarned );
  5161. ClientPrint( pBestEnemy, HUD_PRINTTALK, "#Player_Cash_Award_ExplainSuicide_YouGotCash", CFmtStr( "#ENTNAME[%d]%s", this->entindex(), this->GetPlayerName() ), fmtDollarsEarned.Get() );
  5162. // Notify all players about what just happened?
  5163. CRecipientFilter rfSuicidingTeam, rfGettingMoneyTeam;
  5164. rfSuicidingTeam.MakeReliable(); rfGettingMoneyTeam.MakeReliable();
  5165. for ( int playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum )
  5166. {
  5167. CCSPlayer *player = ( CCSPlayer * ) UTIL_PlayerByIndex( playerNum );
  5168. if ( !player || ( player == pBestEnemy ) )
  5169. continue;
  5170. if ( player->GetTeamNumber() == myteam )
  5171. {
  5172. rfSuicidingTeam.AddRecipient( player );
  5173. }
  5174. else if ( player->GetTeamNumber() == team )
  5175. {
  5176. rfGettingMoneyTeam.AddRecipient( player );
  5177. }
  5178. }
  5179. UTIL_ClientPrintFilter( rfGettingMoneyTeam, HUD_PRINTTALK, "#Player_Cash_Award_ExplainSuicide_TeammateGotCash",
  5180. CFmtStr( "#ENTNAME[%d]%s", this->entindex(), this->GetPlayerName() ), fmtDollarsEarned.Get(),
  5181. CFmtStr( "#ENTNAME[%d]%s", pBestEnemy->entindex(), pBestEnemy->GetPlayerName() ) );
  5182. UTIL_ClientPrintFilter( rfSuicidingTeam, HUD_PRINTTALK, "#Player_Cash_Award_ExplainSuicide_EnemyGotCash",
  5183. CFmtStr( "#ENTNAME[%d]%s", this->entindex(), this->GetPlayerName() ) );
  5184. // Notify spectators
  5185. CTeamRecipientFilter teamfilter( TEAM_SPECTATOR, true );
  5186. UTIL_ClientPrintFilter( teamfilter, HUD_PRINTTALK, "#Player_Cash_Award_ExplainSuicide_Spectators",
  5187. CFmtStr( "#ENTNAME[%d]%s", this->entindex(), this->GetPlayerName() ), fmtDollarsEarned.Get(),
  5188. CFmtStr( "#ENTNAME[%d]%s", pBestEnemy->entindex(), pBestEnemy->GetPlayerName() ) );
  5189. }
  5190. }
  5191. bool CCSPlayer::DoesPlayerGetRoundStartMoney()
  5192. {
  5193. return m_receivesMoneyNextRound;
  5194. }
  5195. CCSPlayer* CCSPlayer::Instance( int iEnt )
  5196. {
  5197. return dynamic_cast< CCSPlayer* >( CBaseEntity::Instance( INDEXENT( iEnt ) ) );
  5198. }
  5199. bool CCSPlayer::ShouldPickupItemSilently( CBaseCombatCharacter *pNewOwner )
  5200. {
  5201. CCSPlayer *pNewCSOwner = dynamic_cast< CCSPlayer* >( pNewOwner );
  5202. if ( !pNewCSOwner || !CSGameRules() || CSGameRules()->IsFreezePeriod() /*|| pNewCSOwner->CanPlayerBuy( false )*/ )
  5203. return false; // turns out that item touch calls happen in between FinishMove and the trigger touch so CanPlayerBuy always returns false in this case.....
  5204. if ( pNewCSOwner->GetAbsVelocity().Length2D() < (CS_PLAYER_SPEED_RUN * CS_PLAYER_SPEED_WALK_MODIFIER) )
  5205. return true;
  5206. if ( CSGameRules() && CSGameRules()->IsPlayingCooperativeGametype() && IsBot() && (m_spawnedTime + 0.15) < gpGlobals->curtime)
  5207. return true;
  5208. return false;
  5209. }
  5210. void CCSPlayer::DropC4()
  5211. {
  5212. }
  5213. bool CCSPlayer::HasDefuser()
  5214. {
  5215. return m_bHasDefuser;
  5216. }
  5217. void CCSPlayer::RemoveDefuser()
  5218. {
  5219. m_bHasDefuser = false;
  5220. }
  5221. void CCSPlayer::GiveDefuser( bool bPickedUp /* = false */ )
  5222. {
  5223. if ( !m_bHasDefuser )
  5224. {
  5225. bool bIsSilentPickup = ShouldPickupItemSilently( this );
  5226. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  5227. if( event )
  5228. {
  5229. event->SetInt( "userid", GetUserID() );
  5230. event->SetString( "item", "defuser" );
  5231. event->SetBool( "silent", bIsSilentPickup );
  5232. gameeventmanager->FireEvent( event );
  5233. }
  5234. if ( !bIsSilentPickup )
  5235. EmitSound( "Player.PickupWeapon" );
  5236. }
  5237. m_bHasDefuser = true;
  5238. if ( !bPickedUp )
  5239. {
  5240. m_fLastGivenDefuserTime = gpGlobals->curtime;
  5241. }
  5242. // [dwenger] Added for fun-fact support
  5243. m_bPickedUpDefuser = bPickedUp;
  5244. RecalculateCurrentEquipmentValue();
  5245. }
  5246. // player blinded by a flashbang grenade
  5247. void CCSPlayer::Blind( float holdTime, float fadeTime, float startingAlpha )
  5248. {
  5249. // Don't flash a spectator.
  5250. color32 clr = {255, 255, 255, 255};
  5251. clr.a = startingAlpha;
  5252. // estimate when we can see again
  5253. float oldBlindUntilTime = m_blindUntilTime;
  5254. float oldBlindStartTime = m_blindStartTime;
  5255. m_blindUntilTime = MAX( m_blindUntilTime, gpGlobals->curtime + holdTime + 0.5f * fadeTime );
  5256. m_blindStartTime = gpGlobals->curtime;
  5257. fadeTime /= 1.4f;
  5258. if ( gpGlobals->curtime > oldBlindUntilTime )
  5259. {
  5260. // The previous flashbang is wearing off, or completely gone
  5261. m_flFlashDuration = fadeTime;
  5262. m_flFlashMaxAlpha = startingAlpha;
  5263. }
  5264. else
  5265. {
  5266. // The previous flashbang is still going strong - only extend the duration
  5267. float remainingDuration = oldBlindStartTime + m_flFlashDuration - gpGlobals->curtime;
  5268. float flNewDuration = Max( remainingDuration, fadeTime );
  5269. // The flashbang client effect runs off a network var change callback... Make sure the bits for duration get
  5270. // sent by changing it a tiny bit whenever these end up being equal.
  5271. if ( m_flFlashDuration == flNewDuration )
  5272. flNewDuration += 0.01f;
  5273. m_flFlashDuration = flNewDuration;
  5274. m_flFlashMaxAlpha = Max( m_flFlashMaxAlpha.Get(), startingAlpha );
  5275. }
  5276. if ( m_bUseNewAnimstate && m_PlayerAnimStateCSGO )
  5277. {
  5278. // Magic numbers to reduce the fade time to within 'perceptible' range.
  5279. // Players can see well enough to shoot back somewhere around 50% white plus burn-in effect.
  5280. // Varies by player and amount of panic ;)
  5281. // So this makes raised arm goes down earlier, making it a better representation of actual blindness.
  5282. float flAdjustedHold = holdTime * 0.45f;
  5283. float flAdjustedEnd = fadeTime * 0.7f;
  5284. //DevMsg( "Flashing. Time is: %f. Params: holdTime: %f, fadeTime: %f, alpha: %f\n", gpGlobals->curtime, holdTime, fadeTime, m_flFlashMaxAlpha );
  5285. m_PlayerAnimStateCSGO->m_flFlashedAmountEaseOutStart = gpGlobals->curtime + flAdjustedHold;
  5286. m_PlayerAnimStateCSGO->m_flFlashedAmountEaseOutEnd = gpGlobals->curtime + flAdjustedEnd;
  5287. // This check moves the ease-out start and end to account for a non-255 starting alpha.
  5288. // However it looks like starting alpha is ALWAYS 255, since no current code path seems to ever pass in less.
  5289. if ( m_flFlashMaxAlpha < 255 )
  5290. {
  5291. float flScaleBack = 1.0f - (( flAdjustedEnd / 255.0f ) * m_flFlashMaxAlpha);
  5292. m_PlayerAnimStateCSGO->m_flFlashedAmountEaseOutStart -= flScaleBack;
  5293. m_PlayerAnimStateCSGO->m_flFlashedAmountEaseOutEnd -= flScaleBack;
  5294. }
  5295. // when fade out time is very soon, don't pull the arm up all the way. It looks silly and robotic.
  5296. if ( flAdjustedEnd < 1.5f )
  5297. {
  5298. m_PlayerAnimStateCSGO->m_flFlashedAmountEaseOutStart -= 1.0f;
  5299. }
  5300. }
  5301. }
  5302. void CCSPlayer::Deafen( float flDistance )
  5303. {
  5304. // Spectators don't get deafened
  5305. if ( (GetObserverMode() == OBS_MODE_NONE ) || (GetObserverMode() == OBS_MODE_IN_EYE ) )
  5306. {
  5307. // dsp presets are defined in hl2/scripts/dsp_presets.txt
  5308. int effect;
  5309. if( flDistance < 100 )
  5310. {
  5311. effect = 134;
  5312. }
  5313. else if( flDistance < 500 )
  5314. {
  5315. effect = 135;
  5316. }
  5317. else if( flDistance < 1000 )
  5318. {
  5319. effect = 136;
  5320. }
  5321. else
  5322. {
  5323. // too far for us to get an effect
  5324. return;
  5325. }
  5326. CSingleUserRecipientFilter user( this );
  5327. enginesound->SetPlayerDSP( user, effect, false );
  5328. //TODO: bots can't hear sound for a while?
  5329. }
  5330. }
  5331. void CCSPlayer::GiveShield( void )
  5332. {
  5333. #ifdef CS_SHIELD_ENABLED
  5334. m_bHasShield = true;
  5335. m_bShieldDrawn = false;
  5336. if ( HasSecondaryWeapon() )
  5337. {
  5338. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  5339. pWeapon->SetModel( pWeapon->GetViewModel() );
  5340. pWeapon->Deploy();
  5341. }
  5342. CBaseViewModel *pVM = GetViewModel( 1 );
  5343. if ( pVM )
  5344. {
  5345. ShowViewModel( true );
  5346. pVM->RemoveEffects( EF_NODRAW );
  5347. pVM->SetWeaponModel( SHIELD_VIEW_MODEL, GetActiveWeapon() );
  5348. pVM->SendViewModelMatchingSequence( 1 );
  5349. }
  5350. #endif
  5351. }
  5352. void CCSPlayer::RemoveShield( void )
  5353. {
  5354. #ifdef CS_SHIELD_ENABLED
  5355. m_bHasShield = false;
  5356. CBaseViewModel *pVM = GetViewModel( 1 );
  5357. if ( pVM )
  5358. {
  5359. pVM->AddEffects( EF_NODRAW );
  5360. }
  5361. #endif
  5362. }
  5363. void CCSPlayer::RemoveAllItems( bool removeSuit )
  5364. {
  5365. //reset addon bits
  5366. m_iAddonBits = 0;
  5367. if( HasDefuser() )
  5368. {
  5369. RemoveDefuser();
  5370. }
  5371. if ( HasShield() )
  5372. {
  5373. RemoveShield();
  5374. }
  5375. m_bHasNightVision = false;
  5376. m_bNightVisionOn = false;
  5377. // [dwenger] Added for fun-fact support
  5378. m_bPickedUpDefuser = false;
  5379. m_bDefusedWithPickedUpKit = false;
  5380. m_bPickedUpWeapon = false;
  5381. m_bAttemptedDefusal = false;
  5382. m_nPreferredGrenadeDrop = 0;
  5383. if ( removeSuit )
  5384. {
  5385. m_bHasHelmet = false;
  5386. m_bHasHeavyArmor = false;
  5387. SetArmorValue( 0 );
  5388. }
  5389. BaseClass::RemoveAllItems( removeSuit );
  5390. }
  5391. void CCSPlayer::ValidateWearables( void )
  5392. {
  5393. /** Removed for partner depot **/
  5394. }
  5395. void CCSPlayer::ObserverRoundRespawn()
  5396. {
  5397. ClearFlashbangScreenFade();
  5398. // did we change our name last round?
  5399. if ( m_szNewName[0] != 0 )
  5400. {
  5401. // ... and force the name change now. After this happens, the gamerules will get
  5402. // a ClientSettingsChanged callback from the above ClientCommand, but the name
  5403. // matches what we're setting here, so it will do nothing.
  5404. ChangeName( m_szNewName );
  5405. m_szNewName[0] = 0;
  5406. }
  5407. m_unRoundStartEquipmentValue = m_unCurrentEquipmentValue = 0;
  5408. // Resets camera mode, specifically for the case where coaches are popped into 3rd to spectate a bomb
  5409. // when it's the only valid target. Limited to coach because no one ever complained about other situations
  5410. // where this happens.
  5411. if ( IsCoach() )
  5412. {
  5413. // check mp_forcecamera settings
  5414. if ( ( GetObserverMode() > OBS_MODE_FIXED ) )
  5415. {
  5416. switch ( mp_forcecamera.GetInt() )
  5417. {
  5418. case OBS_ALLOW_ALL : break; // no restrictions
  5419. case OBS_ALLOW_TEAM : SetObserverMode( OBS_MODE_IN_EYE ); break;
  5420. case OBS_ALLOW_NONE : SetObserverMode( OBS_MODE_FIXED ); break; // don't allow anything
  5421. }
  5422. }
  5423. }
  5424. }
  5425. void CCSPlayer::RoundRespawn()
  5426. {
  5427. if ( CSGameRules()->IsPlayingGunGame() )
  5428. {
  5429. bool resetScore = CSGameRules()->IsPlayingGunGameProgressive();
  5430. Reset( resetScore );
  5431. // Reinitialize some gun-game progressive variables
  5432. m_bMadeFinalGunGameProgressiveKill = false;
  5433. m_iNumGunGameKillsWithCurrentWeapon = 0;
  5434. // Ensure the player has the proper gun-game progressive weapons
  5435. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  5436. {
  5437. // Clear out weapons in progressive mode
  5438. m_iGunGameProgressiveWeaponIndex = 0;
  5439. // Reset the UI to default tint when we are no longer at last weapon level
  5440. if ( !CSGameRules()->IsFinalGunGameProgressiveWeapon( m_iGunGameProgressiveWeaponIndex, GetTeamNumber() ) )
  5441. {
  5442. ConVarRef sf_ui_tint( "sf_ui_tint" );
  5443. if ( sf_ui_tint.GetInt() == g_KnifeLevelTint )
  5444. {
  5445. if ( GetTeamNumber() == TEAM_TERRORIST )
  5446. sf_ui_tint.SetValue( g_T_Tint );
  5447. else
  5448. sf_ui_tint.SetValue( g_CT_Tint );
  5449. }
  5450. }
  5451. }
  5452. if ( !CSGameRules()->IsPlayingGunGameTRBomb() )
  5453. {
  5454. // Ensure player has the default items
  5455. GiveDefaultItems();
  5456. }
  5457. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  5458. {
  5459. // Progress weapons over rounds for TR Bomb mode
  5460. if ( m_bShouldProgressGunGameTRBombModeWeapon )
  5461. {
  5462. ResetTRBombModeWeaponProgressFlag();
  5463. if ( CSGameRules()->GetRoundsPlayed() > 0 )
  5464. IncrementGunGameProgressiveWeapon( 1 );
  5465. }
  5466. }
  5467. }
  5468. //MIKETODO: menus
  5469. //if ( m_iMenu != Menu_ChooseAppearance )
  5470. {
  5471. // remove them from any vehicle they may be in
  5472. if ( IsInAVehicle() )
  5473. {
  5474. LeaveVehicle();
  5475. }
  5476. if ( GetParent() )
  5477. SetParent( NULL );
  5478. // Put them back into the game.
  5479. StopObserverMode();
  5480. State_Transition( STATE_ACTIVE );
  5481. respawn( this, false );
  5482. m_nButtons = 0;
  5483. SetNextThink( TICK_NEVER_THINK );
  5484. ResetForceTeamThink();
  5485. }
  5486. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  5487. {
  5488. // [hpe:jason] Reset the kill points after we award the upgrade, so the UI continues to show that
  5489. // the next weapon level has been unlocked until we respawn for the next round.
  5490. m_iNumGunGameTRKillPoints = 0;
  5491. }
  5492. m_iNumRoundKills = 0;
  5493. m_iNumRoundKillsHeadshots = 0;
  5494. m_iNumRoundTKs = 0;
  5495. m_receivesMoneyNextRound = true; // reset this variable so they can receive their cash next round.
  5496. m_iAccountMoneyEarnedForNextRound = 0;
  5497. //If they didn't die, this will print out their damage info
  5498. OutputDamageGiven();
  5499. OutputDamageTaken();
  5500. ResetDamageCounters();
  5501. m_unRoundStartEquipmentValue = RecalculateCurrentEquipmentValue();
  5502. }
  5503. void CCSPlayer::CheckTKPunishment( void )
  5504. {
  5505. // teamkill punishment..
  5506. if ( (m_bJustKilledTeammate == true ) && mp_tkpunish.GetInt() )
  5507. {
  5508. m_bJustKilledTeammate = false;
  5509. m_bPunishedForTK = true;
  5510. CommitSuicide();
  5511. }
  5512. }
  5513. bool CCSPlayer::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*= 0*/ )
  5514. {
  5515. if ( IsTaunting() )
  5516. {
  5517. // Don't allow weapon switch while taunting
  5518. if ( IsThirdPersonTaunt() )
  5519. return false;
  5520. // Stop taunting for view model look at
  5521. StopTaunting();
  5522. }
  5523. if ( IsLookingAtWeapon() )
  5524. {
  5525. StopLookingAtWeapon();
  5526. }
  5527. bool bBaseClassSwitch = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
  5528. // clear any bomb-plant force-duck when switching to any weapon
  5529. if( bBaseClassSwitch )
  5530. m_bDuckOverride = false;
  5531. return bBaseClassSwitch;
  5532. }
  5533. class CCSPlayerResourcePlayer : public CCSPlayerResource { friend class CCSPlayer; };
  5534. CWeaponCSBase* CCSPlayer::GetActiveCSWeapon() const
  5535. {
  5536. CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase* >( GetActiveWeapon( ) );
  5537. /** Removed for partner depot **/
  5538. return csWeapon;
  5539. }
  5540. void CCSPlayer::LogTriggerPulls()
  5541. {
  5542. if( !(m_nButtons & IN_ATTACK ) )
  5543. {
  5544. m_triggerPulled = false;
  5545. }
  5546. else if( !m_triggerPulled )
  5547. {
  5548. // we are pulling a trigger, and we weren't already pulling it.
  5549. m_triggerPulled = true;
  5550. m_triggerPulls++;
  5551. }
  5552. }
  5553. void CCSPlayer::PreThink()
  5554. {
  5555. BaseClass::PreThink();
  5556. if ( m_bAutoReload )
  5557. {
  5558. m_bAutoReload = false;
  5559. m_nButtons |= IN_RELOAD;
  5560. }
  5561. LogTriggerPulls();
  5562. if ( m_afButtonLast != m_nButtons )
  5563. m_flLastAction = gpGlobals->curtime;
  5564. if ( g_fGameOver )
  5565. return;
  5566. // Quest update from matchmaking data
  5567. // TODO: Valve official servers only?
  5568. // TODO: Send quest id too?
  5569. CCSGameRules::CQMMPlayerData_t* pQMM = NULL;
  5570. if( m_uiAccountId && CSGameRules() )
  5571. {
  5572. pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId );
  5573. }
  5574. State_PreThink();
  5575. if ( m_pHintMessageQueue )
  5576. m_pHintMessageQueue->Update();
  5577. //Reset bullet force accumulator, only lasts one frame
  5578. m_vecTotalBulletForce = vec3_origin;
  5579. if ( mp_autokick.GetBool() && !IsBot() && !IsHLTV() && !IsAutoKickDisabled() )
  5580. {
  5581. if ( this != UTIL_GetLocalPlayerOrListenServerHost() )
  5582. {
  5583. if ( gpGlobals->curtime - m_flLastAction > CSGameRules()->GetRoundLength() * 2 )
  5584. {
  5585. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_idle_kick", CFmtStr( "#ENTNAME[%d]%s", entindex(), GetPlayerName() ) );
  5586. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d %s\n", GetUserID(), CSGameRules()->IsPlayingOffline() ? 0 : 1, "Player idle" ) );
  5587. m_flLastAction = gpGlobals->curtime;
  5588. }
  5589. }
  5590. }
  5591. if ( m_flDominateEffectDelayTime > -1 && m_flDominateEffectDelayTime <= gpGlobals->curtime && m_hDominateEffectPlayer.Get() )
  5592. {
  5593. CCSPlayer *pOtherPlayer = dynamic_cast<CCSPlayer*>( m_hDominateEffectPlayer.Get() );
  5594. if ( pOtherPlayer )
  5595. {
  5596. int victimEntIndex = pOtherPlayer->entindex();
  5597. int killerEntIndex = entindex();
  5598. if ( IsPlayerDominated( victimEntIndex ) )
  5599. {
  5600. //PlayerStats_t statsVictim = CCS_GameStats.FindPlayerStats( pOtherPlayer );
  5601. int iKills = CCS_GameStats.FindPlayerStats( pOtherPlayer ).statsKills.iNumKilledByUnanswered[killerEntIndex];
  5602. CFmtStr fmtPrintEntName( "#ENTNAME[%d]%s", pOtherPlayer->entindex(), pOtherPlayer->GetPlayerName() );
  5603. if ( CS_KILLS_FOR_DOMINATION == iKills )
  5604. {
  5605. ClientPrint( this, HUD_PRINTTALK, "#Player_You_Are_Now_Dominating", fmtPrintEntName.Access() );
  5606. }
  5607. else
  5608. {
  5609. ClientPrint( this, HUD_PRINTTALK, "#Player_You_Are_Still_Dominating", fmtPrintEntName.Access() );
  5610. }
  5611. // Play gun game domination sound
  5612. CRecipientFilter filter;
  5613. filter.AddRecipient( this );
  5614. EmitSound( filter, entindex(), "Music.GG_Dominating" );
  5615. // have this player brag to his team about dominating someone
  5616. Radio( "NiceShot"/*"OnARollBrag"*/ );
  5617. }
  5618. }
  5619. m_flDominateEffectDelayTime = -1;
  5620. m_hDominateEffectPlayer = NULL;
  5621. }
  5622. #ifndef _XBOX
  5623. ++ m_nTicksSinceLastPlaceUpdate;
  5624. // No reason to update this every tick! Once per second is good enough
  5625. if ( m_nTicksSinceLastPlaceUpdate > 30 )
  5626. {
  5627. m_nTicksSinceLastPlaceUpdate = 0;
  5628. // CS would like their players to continue to update their LastArea since it is displayed in the hud voice chat UI
  5629. // But we won't do the population tracking while dead.
  5630. // We need to check simple line of sight to make sure we don't grab nav areas from floors under us if we're slightly off
  5631. // the nav mesh up above.
  5632. const bool checkLOSToGround = true;
  5633. CNavArea *area = TheNavMesh->GetNavArea( WorldSpaceCenter(), 1000, checkLOSToGround );
  5634. if ( area && area != m_lastNavArea )
  5635. {
  5636. m_lastNavArea = area;
  5637. if ( area->GetPlace() != UNDEFINED_PLACE )
  5638. {
  5639. const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() );
  5640. if ( placeName && *placeName )
  5641. {
  5642. Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH );
  5643. }
  5644. }
  5645. }
  5646. }
  5647. #endif
  5648. }
  5649. void CCSPlayer::MoveToNextIntroCamera()
  5650. {
  5651. m_pIntroCamera = gEntList.FindEntityByClassname( m_pIntroCamera, "point_viewcontrol" );
  5652. // if m_pIntroCamera is NULL we just were at end of list, start searching from start again
  5653. if(!m_pIntroCamera )
  5654. m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "point_viewcontrol" );
  5655. // find the target
  5656. CBaseEntity *Target = NULL;
  5657. if( m_pIntroCamera )
  5658. {
  5659. Target = gEntList.FindEntityByName( NULL, STRING(m_pIntroCamera->m_target ) );
  5660. }
  5661. // if we still couldn't find a camera, goto T spawn
  5662. if(!m_pIntroCamera )
  5663. m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "info_player_terrorist" );
  5664. SetViewOffset( vec3_origin ); // no view offset
  5665. UTIL_SetSize( this, vec3_origin, vec3_origin ); // no bbox
  5666. if( !Target ) //if there are no cameras(or the camera has no target, find a spawn point and black out the screen
  5667. {
  5668. if ( m_pIntroCamera.IsValid() )
  5669. SetAbsOrigin( m_pIntroCamera->GetAbsOrigin() + VEC_VIEW );
  5670. SetAbsAngles( QAngle( 0, 0, 0 ) );
  5671. m_pIntroCamera = NULL; // never update again
  5672. return;
  5673. }
  5674. Vector vCamera = Target->GetAbsOrigin() - m_pIntroCamera->GetAbsOrigin();
  5675. Vector vIntroCamera = m_pIntroCamera->GetAbsOrigin();
  5676. VectorNormalize( vCamera );
  5677. QAngle CamAngles;
  5678. VectorAngles( vCamera, CamAngles );
  5679. SetAbsOrigin( vIntroCamera );
  5680. SetAbsAngles( CamAngles );
  5681. SnapEyeAngles( CamAngles );
  5682. m_fIntroCamTime = gpGlobals->curtime + 6;
  5683. }
  5684. class NotVIP
  5685. {
  5686. public:
  5687. bool operator()( CBasePlayer *player )
  5688. {
  5689. CCSPlayer *csPlayer = static_cast< CCSPlayer * >(player );
  5690. csPlayer->MakeVIP( false );
  5691. return true;
  5692. }
  5693. };
  5694. // Expose the VIP selection to plugins, since we don't have an official VIP mode. This
  5695. // allows plugins to access the (limited ) VIP functionality already present (scoreboard
  5696. // identification and radar color ).
  5697. CON_COMMAND( cs_make_vip, "Marks a player as the VIP" )
  5698. {
  5699. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  5700. return;
  5701. if ( args.ArgC() != 2 )
  5702. {
  5703. return;
  5704. }
  5705. CCSPlayer *player = static_cast< CCSPlayer * >(UTIL_PlayerByIndex( atoi( args[1] ) ) );
  5706. if ( !player )
  5707. {
  5708. // Invalid value clears out VIP
  5709. NotVIP notVIP;
  5710. ForEachPlayer( notVIP );
  5711. return;
  5712. }
  5713. player->MakeVIP( true );
  5714. }
  5715. void CCSPlayer::MakeVIP( bool isVIP )
  5716. {
  5717. if ( isVIP )
  5718. {
  5719. NotVIP notVIP;
  5720. ForEachPlayer( notVIP );
  5721. }
  5722. m_isVIP = isVIP;
  5723. }
  5724. bool CCSPlayer::IsVIP() const
  5725. {
  5726. return m_isVIP;
  5727. }
  5728. void CCSPlayer::DropShield( void )
  5729. {
  5730. #ifdef CS_SHIELD_ENABLED
  5731. //Drop an item_defuser
  5732. Vector vForward, vRight;
  5733. AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
  5734. RemoveShield();
  5735. CBaseAnimating *pShield = (CBaseAnimating * )CBaseEntity::Create( "item_shield", WorldSpaceCenter(), GetLocalAngles() );
  5736. pShield->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );
  5737. CBaseCombatWeapon *pActive = GetActiveWeapon();
  5738. if ( pActive )
  5739. {
  5740. pActive->Deploy();
  5741. }
  5742. #endif
  5743. }
  5744. void CCSPlayer::SetShieldDrawnState( bool bState )
  5745. {
  5746. #ifdef CS_SHIELD_ENABLED
  5747. m_bShieldDrawn = bState;
  5748. #endif
  5749. }
  5750. bool CCSPlayer::CSWeaponDrop(CBaseCombatWeapon *pWeapon, bool bDropShield, bool bThrowForward)
  5751. {
  5752. Vector vTossPos = WorldSpaceCenter();
  5753. if (bThrowForward)
  5754. {
  5755. Vector vForward;
  5756. AngleVectors(EyeAngles(), &vForward, NULL, NULL);
  5757. vTossPos = vTossPos + vForward * 100;
  5758. }
  5759. return CSWeaponDrop( pWeapon, vTossPos, bDropShield );
  5760. }
  5761. bool CCSPlayer::CSWeaponDrop( CBaseCombatWeapon *pWeapon, Vector targetPos, bool bDropShield )
  5762. {
  5763. bool bSuccess = false;
  5764. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
  5765. if ( pWeapon )
  5766. pWeapon->ShowWeaponWorldModel( false );
  5767. if ( mp_death_drop_gun.GetInt() == 0 && pCSWeapon && !pCSWeapon->IsA( WEAPON_C4 ))
  5768. {
  5769. if ( pWeapon )
  5770. UTIL_Remove( pWeapon );
  5771. UpdateAddonBits();
  5772. return true;
  5773. }
  5774. if ( HasShield() && bDropShield == true )
  5775. {
  5776. DropShield();
  5777. return true;
  5778. }
  5779. if ( pWeapon )
  5780. {
  5781. Weapon_Drop( pWeapon, &targetPos, NULL );
  5782. pWeapon->SetSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER | FSOLID_USE_TRIGGER_BOUNDS );
  5783. pWeapon->SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );
  5784. if( pCSWeapon )
  5785. {
  5786. // Track which player was owner of this weapon ( needed to track kills with enemy weapon stats )
  5787. pCSWeapon->AddPriorOwner( this );
  5788. pCSWeapon->SetModel( pCSWeapon->GetWorldDroppedModel() );
  5789. // set silencer bodygroup
  5790. if ( pCSWeapon->HasSilencer() )
  5791. {
  5792. pCSWeapon->SetBodygroup( pCSWeapon->FindBodygroupByName( "silencer" ), pCSWeapon->IsSilenced() ? 0 : 1 );
  5793. }
  5794. //Find out the index of the ammo type
  5795. int iAmmoIndex = pCSWeapon->GetPrimaryAmmoType();
  5796. //If it has an ammo type, find out how much the player has
  5797. if( iAmmoIndex != -1 )
  5798. {
  5799. // Check to make sure we don't have other weapons using this ammo type
  5800. bool bAmmoTypeInUse = false;
  5801. if ( IsAlive() && GetHealth() > 0 )
  5802. {
  5803. for ( int i=0; i<MAX_WEAPONS; ++i )
  5804. {
  5805. CBaseCombatWeapon *pOtherWeapon = GetWeapon( i );
  5806. if ( pOtherWeapon && pOtherWeapon != pWeapon && pOtherWeapon->GetPrimaryAmmoType() == iAmmoIndex )
  5807. {
  5808. bAmmoTypeInUse = true;
  5809. break;
  5810. }
  5811. }
  5812. }
  5813. if ( !bAmmoTypeInUse )
  5814. {
  5815. int iAmmoToDrop = GetAmmoCount( iAmmoIndex );
  5816. // only add 1 ammo to dropped grenades
  5817. if( pCSWeapon->GetWeaponType() == WEAPONTYPE_GRENADE )
  5818. iAmmoToDrop = 0;
  5819. // //Add this much to the dropped weapon
  5820. // pCSWeapon->SetExtraAmmoCount( iAmmoToDrop );
  5821. //Remove all ammo of this type from the player
  5822. SetAmmoCount( 0, iAmmoIndex );
  5823. }
  5824. }
  5825. //record this time as when this weapon was last dropped
  5826. pCSWeapon->m_flDroppedAtTime = gpGlobals->curtime;
  5827. }
  5828. //=========================================
  5829. // Teleport the weapon to the player's hand
  5830. //=========================================
  5831. int iBIndex = -1;
  5832. int iWeaponBoneIndex = -1;
  5833. MDLCACHE_CRITICAL_SECTION();
  5834. if ( !m_bUseNewAnimstate )
  5835. {
  5836. // now we use the weapon_bone to drop the item from. Previously we were incorrectly using the root position from the character
  5837. iBIndex = LookupBone( "ValveBiped.weapon_bone" );
  5838. iWeaponBoneIndex = pWeapon->LookupBone( "ValveBiped.weapon_bone" );
  5839. // dkorus: If we hit this assert, the model changed and we no longer have a valid "ValveBiped.weapon_bone" to use for our weapon drop position
  5840. // This code will have to change to match the new bone name
  5841. AssertMsg( iBIndex != -1, "Missing weapon bone from player! Make sure the bone exists and or that the string is updated." );
  5842. if ( iBIndex == -1 || iWeaponBoneIndex == -1 )
  5843. {
  5844. iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
  5845. iWeaponBoneIndex = 0; // use the root
  5846. }
  5847. if ( iBIndex != -1 )
  5848. {
  5849. Vector origin;
  5850. QAngle angles;
  5851. matrix3x4_t transform;
  5852. // Get the transform for the weapon bonetoworldspace in the NPC
  5853. GetBoneTransform( iBIndex, transform );
  5854. // find offset of root bone from origin in local space
  5855. // Make sure we're detached from hierarchy before doing this!!!
  5856. pWeapon->StopFollowingEntity();
  5857. MatrixAngles( transform, angles, origin );
  5858. pWeapon->SetAbsOrigin( Vector( 0, 0, 0 ) );
  5859. pWeapon->SetAbsAngles( QAngle( 0, 0, 0 ) );
  5860. pWeapon->InvalidateBoneCache();
  5861. matrix3x4_t rootLocal;
  5862. pWeapon->GetBoneTransform( iWeaponBoneIndex, rootLocal );
  5863. // invert it
  5864. matrix3x4_t rootInvLocal;
  5865. MatrixInvert( rootLocal, rootInvLocal );
  5866. matrix3x4_t weaponMatrix;
  5867. ConcatTransforms( transform, rootInvLocal, weaponMatrix );
  5868. MatrixAngles( weaponMatrix, angles, origin );
  5869. // run a hull trace to prevent throwing guns through walls or world geometry
  5870. trace_t trDropTrace;
  5871. UTIL_TraceHull( EyePosition(), origin, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), MASK_SOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trDropTrace );
  5872. if ( trDropTrace.fraction != 1.0 )
  5873. {
  5874. ////uncomment to see debug visualization
  5875. //debugoverlay->AddBoxOverlay( origin, Vector(-5,-5,-5), Vector(5,5,5), QAngle(0,0,0), 0,200,0,128, 4.0f );
  5876. //debugoverlay->AddBoxOverlay( EyePosition(), Vector(-5,-5,-5), Vector(5,5,5), QAngle(0,0,0), 200,0,0,128, 4.0f );
  5877. //debugoverlay->AddLineOverlay( EyePosition(), origin, 255,0,0, true, 4.0f );
  5878. // move the weapon drop position to a valid point between the player's eyes (assumed valid) and their right hand (assumed invalid)
  5879. origin -= (( origin - EyePosition() ) * trDropTrace.fraction);
  5880. //debugoverlay->AddBoxOverlay( origin, Vector(-5,-5,-5), Vector(5,5,5), QAngle(0,0,0), 0,0,200,128, 4.0f );
  5881. }
  5882. pWeapon->Teleport( &origin, &angles, NULL );
  5883. //Have to teleport the physics object as well
  5884. IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject();
  5885. if( pWeaponPhys )
  5886. {
  5887. Vector vPos;
  5888. QAngle vAngles;
  5889. pWeaponPhys->GetPosition( &vPos, &vAngles );
  5890. pWeaponPhys->SetPosition( vPos, vAngles, true );
  5891. AngularImpulse angImp(0,0,0 );
  5892. Vector vecAdd = (GetAbsVelocity() * 0.5f) + Vector( 0, 0, 110 );
  5893. pWeaponPhys->AddVelocity( &vecAdd, &angImp );
  5894. }
  5895. }
  5896. }
  5897. else
  5898. {
  5899. Assert( pWeapon->GetModel() );
  5900. Vector vecWeaponThrowFromPos = EyePosition();
  5901. QAngle angWeaponThrowFromAngle = EyeAngles();
  5902. int nPlayerRightHandAttachment = LookupAttachment( "weapon_hand_R" );
  5903. if ( nPlayerRightHandAttachment != -1 )
  5904. {
  5905. bool bAttachSuccess = GetAttachment( nPlayerRightHandAttachment, vecWeaponThrowFromPos );
  5906. Assert( bAttachSuccess ); bAttachSuccess;
  5907. }
  5908. else
  5909. {
  5910. DevWarning( "Warning: Can't find player's right hand attachment! [weapon_hand_R]\n" );
  5911. }
  5912. pWeapon->StopFollowingEntity();
  5913. // run a hull trace to prevent throwing guns through walls or world geometry
  5914. // Note we do a conservative trace here that blocks against more stuff than is absolutely necessary
  5915. // (we could figure out what kind of object is being thrown, and use a different trace based on that,
  5916. // but it doesn't really matter since it will just move the drop point slightly back towards your head)
  5917. trace_t trDropTrace;
  5918. UTIL_TraceHull( EyePosition(), vecWeaponThrowFromPos, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), MASK_PLAYERSOLID|CONTENTS_GRENADECLIP, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trDropTrace );
  5919. if ( trDropTrace.fraction != 1.0 )
  5920. {
  5921. //uncomment to see debug visualization
  5922. //debugoverlay->AddBoxOverlay( vecWeaponThrowFromPos, Vector(-5,-5,-5), Vector(5,5,5), QAngle(0,0,0), 0,200,0,128, 4.0f );
  5923. //debugoverlay->AddBoxOverlay( EyePosition(), Vector(-5,-5,-5), Vector(5,5,5), QAngle(0,0,0), 200,0,0,128, 4.0f );
  5924. //debugoverlay->AddLineOverlay( EyePosition(), vecWeaponThrowFromPos, 255,0,0, true, 4.0f );
  5925. // move the weapon drop position to a valid point between the player's eyes (assumed valid) and their right hand (assumed invalid)
  5926. vecWeaponThrowFromPos -= (( vecWeaponThrowFromPos - EyePosition() ) * trDropTrace.fraction);
  5927. }
  5928. //debugoverlay->AddBoxOverlay( vecWeaponThrowFromPos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 0,0,200,128, 4.0f );
  5929. pWeapon->SetAbsOrigin( vecWeaponThrowFromPos );
  5930. pWeapon->SetAbsAngles( angWeaponThrowFromAngle );
  5931. if ( pWeapon->m_hWeaponWorldModel.Get() )
  5932. pWeapon->Teleport( &vecWeaponThrowFromPos, &angWeaponThrowFromAngle, NULL );
  5933. //Have to teleport the physics object as well
  5934. IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject();
  5935. if( pWeaponPhys )
  5936. {
  5937. Vector vPos;
  5938. QAngle vAngles;
  5939. pWeaponPhys->GetPosition( &vPos, &vAngles );
  5940. pWeaponPhys->SetPosition( vPos, vAngles, true );
  5941. AngularImpulse angImp(0,0,0 );
  5942. Vector vecAdd = (GetAbsVelocity() * 0.5f) + Vector( 0, 0, 110 );
  5943. pWeaponPhys->AddVelocity( &vecAdd, &angImp );
  5944. }
  5945. }
  5946. bSuccess = true;
  5947. }
  5948. UpdateAddonBits();
  5949. return bSuccess;
  5950. }
  5951. void CCSPlayer::TransferInventory( CCSPlayer* pTargetPlayer )
  5952. {
  5953. // as part of transferring inventory, remove what WE have
  5954. SetArmorValue( 0 );
  5955. m_bHasHelmet = false;
  5956. m_bHasHeavyArmor = false;
  5957. m_bHasNightVision = false;
  5958. m_bNightVisionOn = false;
  5959. RemoveAllItems( true );
  5960. }
  5961. bool CCSPlayer::DropWeaponSlot( int nSlot, bool fromDeath )
  5962. {
  5963. bool bSuccess = false;
  5964. CWeaponCSBase *pWeapon = assert_cast< CWeaponCSBase* >( Weapon_GetSlot( nSlot ) );
  5965. if ( pWeapon )
  5966. {
  5967. bSuccess = CSWeaponDrop( pWeapon, false );
  5968. // UNDONE!
  5969. // The idea here was to add progressive permanent wear to a weapon
  5970. // This was a really inefficient way to deal with this in the first place
  5971. // Also ends up creating a new weapon which caused the weapon to unequip
  5972. /*if ( fromDeath && bSuccess )
  5973. {
  5974. CAttributeContainer *pAttributeContainer = pWeapon->GetAttributeContainer();
  5975. if ( pAttributeContainer )
  5976. {
  5977. CEconItemView *pItem = pAttributeContainer->GetItem();
  5978. if ( pItem )
  5979. {
  5980. if ( pItem->GetItemID() > 0 )
  5981. {
  5982. CSingleUserRecipientFilter filter( this );
  5983. filter.MakeReliable();
  5984. CCSUsrMsg_ItemDrop msg;
  5985. msg.set_itemid( pItem->GetItemID() );
  5986. msg.set_death( true );
  5987. SendUserMessage( filter, CS_UM_ItemDrop, msg );
  5988. }
  5989. }
  5990. }
  5991. }*/
  5992. }
  5993. return bSuccess;
  5994. }
  5995. bool CCSPlayer::HasPrimaryWeapon( void )
  5996. {
  5997. bool bSuccess = false;
  5998. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  5999. if ( pWeapon )
  6000. {
  6001. bSuccess = true;
  6002. }
  6003. return bSuccess;
  6004. }
  6005. bool CCSPlayer::HasSecondaryWeapon( void )
  6006. {
  6007. bool bSuccess = false;
  6008. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  6009. if ( pWeapon )
  6010. {
  6011. bSuccess = true;
  6012. }
  6013. return bSuccess;
  6014. }
  6015. uint32 CCSPlayer::RecalculateCurrentEquipmentValue( void )
  6016. {
  6017. m_unCurrentEquipmentValue = 0;
  6018. #if !defined( NO_STEAM_GAMECOORDINATOR )
  6019. for ( int i = 0; i < MAX_WEAPONS; ++i )
  6020. {
  6021. CWeaponCSBase* pWeapon = dynamic_cast<CWeaponCSBase*>( GetWeapon( i ) );
  6022. if ( pWeapon != NULL )
  6023. {
  6024. int nWeaponPrice = pWeapon->GetWeaponPrice();
  6025. if ( !nWeaponPrice )
  6026. {
  6027. nWeaponPrice = GetWeaponPrice( pWeapon->GetCSWeaponID() );
  6028. }
  6029. if ( pWeapon->GetWeaponType() == WEAPONTYPE_GRENADE )
  6030. { // support for multiple grenades of same type
  6031. int numAmmoItems = GetAmmoCount( pWeapon->GetPrimaryAmmoType() );
  6032. if ( numAmmoItems > 1 )
  6033. {
  6034. nWeaponPrice *= numAmmoItems;
  6035. }
  6036. }
  6037. m_unCurrentEquipmentValue += nWeaponPrice;
  6038. }
  6039. }
  6040. if ( HasDefuser() )
  6041. {
  6042. m_unCurrentEquipmentValue += GetWeaponPrice ( ITEM_DEFUSER );
  6043. }
  6044. // HACK: possessing any armor is 'kevlar', posessing any armor and a helmet is 'assault suit'.
  6045. // Similar checks like this are scattered through out the code... would be better to centralize this check.
  6046. if ( ArmorValue() > 0 )
  6047. {
  6048. // Grr... Can't use GetWeaponPrice because it subtracts out what you already have-- which is what we're trying to count.
  6049. if ( m_bHasHelmet.Get() )
  6050. {
  6051. m_unCurrentEquipmentValue += (uint16)ITEM_PRICE_ASSAULTSUIT;
  6052. }
  6053. else
  6054. {
  6055. m_unCurrentEquipmentValue += (uint16)ITEM_PRICE_KEVLAR;
  6056. }
  6057. }
  6058. #endif
  6059. return m_unCurrentEquipmentValue;
  6060. }
  6061. void CCSPlayer::UpdateFreezetimeEndEquipmentValue( void )
  6062. {
  6063. m_unFreezetimeEndEquipmentValue = m_unCurrentEquipmentValue;
  6064. #if !defined( NO_STEAM_GAMECOORDINATOR )
  6065. // Match Stats
  6066. if ( CSGameRules()->ShouldRecordMatchStats() )
  6067. {
  6068. int currentround = CSGameRules()->GetTotalRoundsPlayed();
  6069. m_iMatchStats_EquipmentValue.GetForModify( currentround ) = m_unCurrentEquipmentValue;
  6070. // add post-freeze account balance to the match stats. This is approximately what the player is saving from this round.
  6071. // we may lose items purchased post freezetime but we also avoid money earned from kills that shouldn't be considered 'saved'
  6072. m_iMatchStats_MoneySaved.GetForModify( currentround ) = GetAccountBalance();
  6073. // Keep track in QMM data
  6074. if ( m_uiAccountId && CSGameRules() )
  6075. {
  6076. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  6077. {
  6078. pQMM->m_iMatchStats_EquipmentValue[ currentround ] = m_iMatchStats_EquipmentValue.Get( currentround );
  6079. pQMM->m_iMatchStats_MoneySaved[ currentround ] = m_iMatchStats_MoneySaved.Get( currentround );
  6080. }
  6081. }
  6082. }
  6083. #endif
  6084. }
  6085. /*void CCSPlayer::UpdateAppearanceIndex( void )
  6086. {
  6087. static const char *pchRandomHeadName = "ctm_head_random";
  6088. static const CCStrike15ItemDefinition *pRandomAppearanceDef = dynamic_cast< const CCStrike15ItemDefinition* >( GetItemSchema()->GetItemDefinitionByName( pchRandomHeadName ) );
  6089. if ( IsControllingBot() && GetControlledBot() )
  6090. {
  6091. m_unAppearanceIndex = GetControlledBot()->m_unAppearanceIndex;
  6092. return;
  6093. }
  6094. const char *pAppearance = pchRandomHeadName;
  6095. const CCStrike15ItemDefinition *pDef = dynamic_cast< const CCStrike15ItemDefinition* >( GetItemSchema()->GetItemDefinitionByName( pAppearance ) );
  6096. if ( !pDef || !AreSlotsConsideredIdentical( pDef->GetLoadoutSlot( GetTeamNumber() ), LOADOUT_POSITION_APPEARANCE ) )
  6097. {
  6098. pDef = pRandomAppearanceDef;
  6099. }
  6100. if ( pDef )
  6101. {
  6102. if ( pDef == pRandomAppearanceDef )
  6103. {
  6104. CUtlVector< const CCStrike15ItemDefinition* > vecAppearances;
  6105. // Make a list of all appearances and randomly choose
  6106. const CEconItemSchema::ItemDefinitionMap_t &mapItems = GetItemSchema()->GetItemDefinitionMap();
  6107. FOR_EACH_MAP_FAST( mapItems, i )
  6108. {
  6109. const CCStrike15ItemDefinition *pItemDef = dynamic_cast< const CCStrike15ItemDefinition* >( mapItems[ i ] );
  6110. if ( !pItemDef->IsHidden() && pItemDef != pRandomAppearanceDef &&
  6111. AreSlotsConsideredIdentical( pItemDef->GetLoadoutSlot( GetTeamNumber() ), LOADOUT_POSITION_APPEARANCE ) )
  6112. {
  6113. vecAppearances.AddToTail( pItemDef );
  6114. }
  6115. }
  6116. if ( vecAppearances.Count() > 0 )
  6117. {
  6118. pDef = vecAppearances[ RandomInt( 0, vecAppearances.Count() - 1 ) ];
  6119. }
  6120. }
  6121. m_unAppearanceIndex = pDef->GetDefinitionIndex();
  6122. }
  6123. }*/
  6124. bool CCSPlayer::BAttemptToBuyCheckSufficientBalance( int nCostOfPurchaseToCheck, bool bClientPrint )
  6125. {
  6126. if ( GetAccountBalance() - m_iAccountMoneyEarnedForNextRound < nCostOfPurchaseToCheck )
  6127. {
  6128. if ( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6129. {
  6130. if ( ( m_iAccountMoneyEarnedForNextRound <= 0 ) || ( GetAccountBalance() < nCostOfPurchaseToCheck ) ) // simply not enough money
  6131. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  6132. else // money has been earned that is only useful next round, inform the user about that case separately
  6133. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money_NextRound", CFmtStr( "%d", m_iAccountMoneyEarnedForNextRound ) );
  6134. }
  6135. return false;
  6136. }
  6137. else
  6138. return true;
  6139. }
  6140. BuyResult_e CCSPlayer::AttemptToBuyVest( void )
  6141. {
  6142. int iKevlarPrice = ITEM_PRICE_KEVLAR;
  6143. if ( ArmorValue() >= 100 )
  6144. {
  6145. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6146. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar" );
  6147. return BUY_ALREADY_HAVE;
  6148. }
  6149. else if ( !BAttemptToBuyCheckSufficientBalance( iKevlarPrice ) )
  6150. {
  6151. return BUY_CANT_AFFORD;
  6152. }
  6153. else
  6154. {
  6155. if ( m_bHasHelmet )
  6156. {
  6157. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6158. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
  6159. }
  6160. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  6161. if( event )
  6162. {
  6163. event->SetInt( "userid", GetUserID() );
  6164. event->SetString( "item", "vest" );
  6165. event->SetBool( "silent", false );
  6166. gameeventmanager->FireEvent( event );
  6167. }
  6168. EmitSound( "Player.PickupWeapon" );
  6169. const char* szKevlarName = "item_kevlar";
  6170. GiveNamedItem( szKevlarName );
  6171. AddAccount( -iKevlarPrice, true, true, szKevlarName );
  6172. return BUY_BOUGHT;
  6173. }
  6174. }
  6175. //************************************
  6176. // Try to buy kevlar vest + helmet
  6177. //************************************
  6178. BuyResult_e CCSPlayer::AttemptToBuyAssaultSuit( void )
  6179. {
  6180. int iPrice = GetWeaponPrice( ITEM_ASSAULTSUIT );
  6181. // process the result
  6182. if ( !BAttemptToBuyCheckSufficientBalance( iPrice ) )
  6183. {
  6184. return BUY_CANT_AFFORD;
  6185. }
  6186. bool bHasFullArmor = ArmorValue() >= 100;
  6187. // special messaging
  6188. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6189. {
  6190. if ( bHasFullArmor && m_bHasHelmet )
  6191. {
  6192. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Helmet" );
  6193. }
  6194. else if ( bHasFullArmor && !m_bHasHelmet )
  6195. {
  6196. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Bought_Helmet" );
  6197. }
  6198. else if ( !bHasFullArmor && m_bHasHelmet )
  6199. {
  6200. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
  6201. iPrice = ITEM_PRICE_KEVLAR;
  6202. }
  6203. }
  6204. if ( /*bHasFullArmor && */m_bHasHelmet )
  6205. {
  6206. return BUY_ALREADY_HAVE;
  6207. }
  6208. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  6209. if( event )
  6210. {
  6211. event->SetInt( "userid", GetUserID() );
  6212. event->SetString( "item", "vesthelm" );
  6213. event->SetBool( "silent", false );
  6214. gameeventmanager->FireEvent( event );
  6215. }
  6216. EmitSound( "Player.PickupWeapon" );
  6217. const char* szAssaultSuitName = "item_assaultsuit";
  6218. GiveNamedItem( szAssaultSuitName );
  6219. AddAccount( -iPrice, true, true, szAssaultSuitName );
  6220. return BUY_BOUGHT;
  6221. }
  6222. //************************************
  6223. // Try to buy kevlar vest + helmet
  6224. //************************************
  6225. BuyResult_e CCSPlayer::AttemptToBuyHeavyAssaultSuit( void )
  6226. {
  6227. int iPrice = GetWeaponPrice( ITEM_HEAVYASSAULTSUIT );
  6228. // process the result
  6229. if ( !BAttemptToBuyCheckSufficientBalance( iPrice ) )
  6230. {
  6231. return BUY_CANT_AFFORD;
  6232. }
  6233. bool bHasFullArmor = ArmorValue() >= 200;
  6234. // special messaging
  6235. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6236. {
  6237. if ( bHasFullArmor && m_bHasHelmet )
  6238. {
  6239. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Helmet" );
  6240. }
  6241. else if ( bHasFullArmor && !m_bHasHelmet )
  6242. {
  6243. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Bought_Helmet" );
  6244. }
  6245. else if ( !bHasFullArmor && m_bHasHelmet )
  6246. {
  6247. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
  6248. iPrice = ITEM_PRICE_KEVLAR;
  6249. }
  6250. }
  6251. // if ( /*bHasFullArmor && */m_bHasHelmet )
  6252. // {
  6253. // return BUY_ALREADY_HAVE;
  6254. // }
  6255. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  6256. if( event )
  6257. {
  6258. event->SetInt( "userid", GetUserID() );
  6259. event->SetString( "item", "heavyarmor" );
  6260. event->SetBool( "silent", false );
  6261. gameeventmanager->FireEvent( event );
  6262. }
  6263. EmitSound( "Player.PickupWeapon" );
  6264. const char* szAssaultSuitName = "item_heavyassaultsuit";
  6265. GiveNamedItem( szAssaultSuitName );
  6266. AddAccount( -iPrice, true, true, szAssaultSuitName );
  6267. return BUY_BOUGHT;
  6268. }
  6269. BuyResult_e CCSPlayer::AttemptToBuyDefuser( void )
  6270. {
  6271. CCSGameRules *MPRules = CSGameRules();
  6272. if( ( GetTeamNumber() == TEAM_CT ) && MPRules->IsBombDefuseMap() || MPRules->IsHostageRescueMap() )
  6273. {
  6274. if ( HasDefuser() ) // prevent this guy from buying more than 1 Defuse Kit
  6275. {
  6276. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6277. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
  6278. return BUY_ALREADY_HAVE;
  6279. }
  6280. else if ( !BAttemptToBuyCheckSufficientBalance( ITEM_PRICE_DEFUSEKIT ) )
  6281. {
  6282. return BUY_CANT_AFFORD;
  6283. }
  6284. else
  6285. {
  6286. GiveDefuser();
  6287. // CBroadcastRecipientFilter filter;
  6288. // EmitSound( filter, entindex(), "Player.PickupWeapon" );
  6289. if ( CSGameRules()->IsHostageRescueMap() )
  6290. AddAccount( -ITEM_PRICE_DEFUSEKIT, true, true, "item_cutters" );
  6291. else
  6292. AddAccount( -ITEM_PRICE_DEFUSEKIT, true, true, "item_defuser" );
  6293. return BUY_BOUGHT;
  6294. }
  6295. }
  6296. return BUY_NOT_ALLOWED;
  6297. }
  6298. BuyResult_e CCSPlayer::AttemptToBuyNightVision( void )
  6299. {
  6300. int iNVGPrice = ITEM_PRICE_NVG;
  6301. if ( m_bHasNightVision == TRUE )
  6302. {
  6303. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6304. ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
  6305. return BUY_ALREADY_HAVE;
  6306. }
  6307. else if ( !BAttemptToBuyCheckSufficientBalance( iNVGPrice ) )
  6308. {
  6309. return BUY_CANT_AFFORD;
  6310. }
  6311. else
  6312. {
  6313. // CBroadcastRecipientFilter filter;
  6314. // EmitSound( filter, entindex(), "Player.PickupWeapon" );
  6315. m_bHasNightVision = true;
  6316. AddAccount( -iNVGPrice, true, true, "weapon_nvg" );
  6317. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  6318. if( event )
  6319. {
  6320. event->SetInt( "userid", GetUserID() );
  6321. event->SetString( "item", "nvgs" );
  6322. event->SetBool( "silent", false );
  6323. gameeventmanager->FireEvent( event );
  6324. }
  6325. EmitSound( "Player.PickupWeapon" );
  6326. if ( !(m_iDisplayHistoryBits & DHF_NIGHTVISION ) )
  6327. {
  6328. HintMessage( "#Hint_use_nightvision", false );
  6329. m_iDisplayHistoryBits |= DHF_NIGHTVISION;
  6330. }
  6331. return BUY_BOUGHT;
  6332. }
  6333. }
  6334. // Handles the special "buy" alias commands we're creating to accommodate the buy
  6335. // scripts players use (now that we've rearranged the buy menus and broken the scripts )
  6336. BuyResult_e CCSPlayer::HandleCommand_Buy( const char *item, int nPos, bool bAddToRebuy/* = true */ )
  6337. {
  6338. bAddToRebuy = ( bAddToRebuy && !m_bIsInRebuy ); // Only addtorebuy if bAddToRebuy is default and we're not in rebuy.
  6339. if ( StringIsEmpty( item ) )
  6340. {
  6341. CEconItemView *pItem = Inventory()->GetItemInLoadout( GetTeamNumber(), nPos );
  6342. if ( pItem && pItem->IsValid() )
  6343. {
  6344. item = pItem->GetItemDefinition()->GetDefinitionName();
  6345. }
  6346. }
  6347. BuyResult_e result = HandleCommand_Buy_Internal( item, nPos, bAddToRebuy );
  6348. if (result == BUY_BOUGHT )
  6349. {
  6350. m_bMadePurchseThisRound = true;
  6351. CCS_GameStats.IncrementStat(this, CSSTAT_ITEMS_PURCHASED, 1 );
  6352. // strip "weapon_" from the weapon class name
  6353. const char *item_name = item;
  6354. if ( IsWeaponClassname( item_name ) )
  6355. {
  6356. item_name += WEAPON_CLASSNAME_PREFIX_LENGTH;
  6357. }
  6358. IGameEvent * event = gameeventmanager->CreateEvent( "item_purchase" );
  6359. if ( event )
  6360. {
  6361. event->SetInt( "userid", GetUserID() );
  6362. event->SetInt( "team", GetTeamNumber() );
  6363. event->SetString( "weapon", item_name );
  6364. gameeventmanager->FireEvent( event );
  6365. }
  6366. if ( CSGameRules() && CSGameRules()->IsPlayingGunGameDeathmatch() )
  6367. AddAccount( 9999, false, false );
  6368. }
  6369. return result;
  6370. }
  6371. BuyResult_e CCSPlayer::HandleCommand_Buy_Internal( const char * wpnName, int nPos, bool bAddToRebuy/* = true */ )
  6372. {
  6373. char itemNameUsed[ 256 ];
  6374. BuyResult_e result = CanPlayerBuy( false ) ? BUY_PLAYER_CANT_BUY : BUY_INVALID_ITEM; // set some defaults
  6375. // translate the new weapon names to the old ones that are actually being used.
  6376. wpnName = GetTranslatedWeaponAlias( wpnName );
  6377. CEconItemView *pItem = Inventory()->GetItemInLoadoutFilteredByProhibition( GetTeamNumber(), nPos );
  6378. CSWeaponID weaponId = WEAPON_NONE;
  6379. const CCSWeaponInfo* pWeaponInfo = NULL;
  6380. if ( pItem && pItem->IsValid() )
  6381. {
  6382. weaponId = WeaponIdFromString( pItem->GetStaticData()->GetItemClass() );
  6383. if ( weaponId == WEAPON_NONE )
  6384. return BUY_INVALID_ITEM;
  6385. pWeaponInfo = GetWeaponInfo( weaponId );
  6386. }
  6387. else
  6388. {
  6389. // Since the loadout pos we got is invalid, make sure that this item isn't in the item schema before giving it by name. Fixes exploit of bypassing loadout with "buy m4a1 1"
  6390. // and still allows buying of gear such as kevlar, assaultsuit, etc.
  6391. char wpnClassName[MAX_WEAPON_STRING];
  6392. wpnClassName[0] = '\0';
  6393. V_sprintf_safe( wpnClassName, "weapon_%s", wpnName );
  6394. V_strlower( wpnClassName );
  6395. if ( !GetItemSchema()->GetItemDefinitionByName( wpnClassName ) || !( CSGameRules() && CSGameRules()->IsPlayingClassic() ) )
  6396. {
  6397. weaponId = AliasToWeaponID(wpnName );
  6398. pWeaponInfo = GetWeaponInfo( weaponId );
  6399. }
  6400. }
  6401. if ( pWeaponInfo == NULL )
  6402. {
  6403. if ( Q_stricmp( itemNameUsed, "primammo" ) == 0 )
  6404. {
  6405. result = AttemptToBuyAmmo( 0 );
  6406. }
  6407. else if ( Q_stricmp( itemNameUsed, "secammo" ) == 0 )
  6408. {
  6409. result = AttemptToBuyAmmo( 1 );
  6410. }
  6411. }
  6412. else
  6413. {
  6414. if( !CanPlayerBuy( true ) )
  6415. {
  6416. return BUY_PLAYER_CANT_BUY;
  6417. }
  6418. AcquireResult::Type acquireResult = CanAcquire( weaponId, AcquireMethod::Buy, pItem );
  6419. switch ( acquireResult )
  6420. {
  6421. case AcquireResult::Allowed:
  6422. break;
  6423. case AcquireResult::AlreadyOwned:
  6424. case AcquireResult::ReachedGrenadeTotalLimit:
  6425. case AcquireResult::ReachedGrenadeTypeLimit:
  6426. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6427. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Carry_Anymore" );
  6428. return BUY_ALREADY_HAVE;
  6429. case AcquireResult::NotAllowedByTeam:
  6430. if ( !m_bIsInAutoBuy && !m_bIsInRebuy && pWeaponInfo->GetWrongTeamMsg()[0] != 0 )
  6431. {
  6432. ClientPrint( this, HUD_PRINTCENTER, "#Alias_Not_Avail", pWeaponInfo->GetWrongTeamMsg() );
  6433. }
  6434. return BUY_NOT_ALLOWED;
  6435. case AcquireResult::NotAllowedByProhibition:
  6436. return BUY_NOT_ALLOWED;
  6437. default:
  6438. // other unhandled reason
  6439. return BUY_NOT_ALLOWED;
  6440. }
  6441. BuyResult_e equipResult = BUY_INVALID_ITEM;
  6442. if ( weaponId == ITEM_KEVLAR )
  6443. {
  6444. equipResult = AttemptToBuyVest();
  6445. }
  6446. else if ( weaponId == ITEM_ASSAULTSUIT )
  6447. {
  6448. equipResult = AttemptToBuyAssaultSuit();
  6449. }
  6450. else if ( weaponId == ITEM_HEAVYASSAULTSUIT )
  6451. {
  6452. equipResult = AttemptToBuyHeavyAssaultSuit();
  6453. }
  6454. else if ( weaponId == ITEM_DEFUSER || weaponId == ITEM_CUTTERS )
  6455. {
  6456. equipResult = AttemptToBuyDefuser();
  6457. }
  6458. else if ( weaponId == ITEM_NVG )
  6459. {
  6460. equipResult = AttemptToBuyNightVision();
  6461. }
  6462. if ( equipResult != BUY_INVALID_ITEM )
  6463. {
  6464. if ( equipResult == BUY_BOUGHT )
  6465. {
  6466. if ( bAddToRebuy )
  6467. {
  6468. itemid_t ullItemID = 0;
  6469. if ( pItem )
  6470. {
  6471. pItem->GetItemID() != 0 ? pItem->GetItemID() : pItem->GetFauxItemIDFromDefinitionIndex();
  6472. }
  6473. else
  6474. {
  6475. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pWeaponInfo->szClassName );
  6476. if ( pDef )
  6477. {
  6478. ullItemID = CombinedItemIdMakeFromDefIndexAndPaint( pDef->GetDefinitionIndex(), 0 );
  6479. }
  6480. }
  6481. AddToRebuy( weaponId, ullItemID );
  6482. }
  6483. m_iWeaponPurchasesThisRound.GetForModify(weaponId)++;
  6484. COMPILE_TIME_ASSERT( WEAPON_MAX < MAX_WEAPONS );
  6485. }
  6486. return equipResult; // intentional early return here
  6487. }
  6488. bool bPurchase = false;
  6489. // do they have enough money?
  6490. if ( !BAttemptToBuyCheckSufficientBalance( pWeaponInfo->GetWeaponPrice( pItem ) ) )
  6491. {
  6492. return BUY_CANT_AFFORD;
  6493. }
  6494. else // essentially means: ( GetAccountBalance() >= pWeaponInfo->GetWeaponPrice( pItem ) )
  6495. {
  6496. if ( m_lifeState != LIFE_DEAD )
  6497. {
  6498. if ( pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
  6499. {
  6500. DropWeaponSlot( WEAPON_SLOT_PISTOL );
  6501. }
  6502. else if ( pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE )
  6503. {
  6504. DropWeaponSlot( WEAPON_SLOT_RIFLE );
  6505. }
  6506. }
  6507. bPurchase = true;
  6508. }
  6509. if ( HasShield() )
  6510. {
  6511. if ( pWeaponInfo->CanBeUsedWithShield() == false )
  6512. {
  6513. return BUY_NOT_ALLOWED;
  6514. }
  6515. }
  6516. if ( bPurchase )
  6517. {
  6518. result = BUY_BOUGHT;
  6519. if ( pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
  6520. m_bUsingDefaultPistol = false;
  6521. // if ( IsGrenadeWeapon( weaponId ) )
  6522. // {
  6523. // CBroadcastRecipientFilter filter;
  6524. // EmitSound( filter, entindex(), "Player.PickupWeapon" );
  6525. // }
  6526. // when all is said and done, regardless of what weapon came down the pipe, give the player the one that
  6527. // is appropriate for the loadout slot that the player is attempting to purchase for.
  6528. int slot = -1;
  6529. // get the slot from the pItem directly or from the pWeaponInfo if there is no pItem
  6530. if ( !pItem || !pItem->IsValid() )
  6531. {
  6532. CEconItemDefinition * pItemDef = GetItemSchema()->GetItemDefinitionByName( pWeaponInfo->szClassName );
  6533. if ( pItemDef )
  6534. slot = ((CCStrike15ItemDefinition*)pItemDef)->GetLoadoutSlot( GetTeamNumber() ) ;
  6535. else
  6536. result = BUY_INVALID_ITEM;
  6537. }
  6538. else
  6539. {
  6540. slot = pItem->GetItemDefinition()->GetLoadoutSlot( GetTeamNumber() );
  6541. }
  6542. CEconItemView *pResultItem = ( result == BUY_BOUGHT )
  6543. ? Inventory()->GetItemInLoadoutFilteredByProhibition( GetTeamNumber(), slot ) : NULL;
  6544. if ( !pResultItem || !pResultItem->IsValid() )
  6545. {
  6546. result = BUY_INVALID_ITEM;
  6547. }
  6548. else
  6549. {
  6550. const char * szWeaponName = pResultItem->GetItemDefinition( )->GetDefinitionName( );
  6551. bool bWeaponMismatch = false;
  6552. const CEconItemDefinition * pOriginalItemDef = NULL;
  6553. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  6554. // Use the cache of purchases to determine what item to give the player
  6555. // The cache guarantees that the inventory that the player starts with is the one that's guaranteed to them
  6556. // throughout the match
  6557. //
  6558. if ( CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() && !CSGameRules()->IsWarmupPeriod() )
  6559. {
  6560. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  6561. {
  6562. int nTeamArrayIndex = GetTeamNumber() - 2;
  6563. if ( ( nTeamArrayIndex != TEAM_TERRORIST_BASE0 ) && ( nTeamArrayIndex != TEAM_CT_BASE0 ) )
  6564. {
  6565. Assert( 0 );
  6566. }
  6567. else
  6568. {
  6569. CCSGameRules::CQMMPlayerData_t::LoadoutSlotToDefIndexMap_t::IndexType_t idx = pQMM->m_mapLoadoutSlotToItem[ nTeamArrayIndex ].Find( slot );
  6570. if ( idx != CCSGameRules::CQMMPlayerData_t::LoadoutSlotToDefIndexMap_t::InvalidIndex() )
  6571. {
  6572. item_definition_index_t un16DefIndex = pQMM->m_mapLoadoutSlotToItem[ nTeamArrayIndex ].Element( idx );
  6573. if ( pResultItem->GetItemDefinition()->GetDefinitionIndex() != un16DefIndex )
  6574. {
  6575. // cached item does not match attempted purchase. give the base item instead of the one asked for.
  6576. pResultItem = NULL;
  6577. pOriginalItemDef = GetItemSchema( )->GetItemDefinition( un16DefIndex );
  6578. szWeaponName = pOriginalItemDef ? pOriginalItemDef->GetDefinitionName( ) : "";
  6579. bWeaponMismatch = true;
  6580. }
  6581. }
  6582. else if ( Inventory()->InventoryRetrievedFromSteamAtLeastOnce() ) // player hasn't bought a weapon from this slot yet. Cache it (but only if we got valid inventory from Steam)
  6583. {
  6584. pQMM->m_mapLoadoutSlotToItem[ nTeamArrayIndex ].Insert( slot, pResultItem->GetItemDefinition()->GetDefinitionIndex() );
  6585. }
  6586. }
  6587. }
  6588. }
  6589. // if the item didn't match, first look up the price in the item definition
  6590. if ( bWeaponMismatch )
  6591. {
  6592. int price = 0;
  6593. if ( pOriginalItemDef )
  6594. {
  6595. KeyValues *pkvAttrib = pOriginalItemDef->GetRawDefinition( )->FindKey( "attributes" );
  6596. if ( pkvAttrib )
  6597. {
  6598. price = V_atoi( pkvAttrib->GetString( "in game price" ) );
  6599. }
  6600. }
  6601. // this item had no price override in schema attributes so use the legacy class price from weaponinfo
  6602. if ( price == 0 )
  6603. price = pWeaponInfo->GetWeaponPrice( );
  6604. if ( !BAttemptToBuyCheckSufficientBalance( price ) )
  6605. {
  6606. result = BUY_CANT_AFFORD;
  6607. }
  6608. else // essentially means: ( GetAccountBalance( ) >= price )
  6609. {
  6610. AddAccount( -price, true, true, szWeaponName );
  6611. GiveNamedItem( szWeaponName, 0 );
  6612. }
  6613. }
  6614. else
  6615. {
  6616. AddAccount( -pWeaponInfo->GetWeaponPrice( pItem ), true, true, szWeaponName );
  6617. GiveNamedItem( szWeaponName, 0, pResultItem );
  6618. }
  6619. }
  6620. }
  6621. }
  6622. if ( result == BUY_BOUGHT )
  6623. {
  6624. if ( bAddToRebuy )
  6625. {
  6626. AddToRebuy( weaponId, nPos );
  6627. }
  6628. m_iWeaponPurchasesThisRound.GetForModify(weaponId)++;
  6629. }
  6630. return result;
  6631. }
  6632. void CCSPlayer::SetBuyMenuOpen( bool bOpen )
  6633. {
  6634. m_bIsBuyMenuOpen = bOpen;
  6635. extern ConVar mp_buy_during_immunity;
  6636. if ( CanBuyDuringImmunity() )
  6637. {
  6638. if ( bOpen )
  6639. m_fImmuneToGunGameDamageTime += 10;
  6640. else
  6641. m_fImmuneToGunGameDamageTime -= 10;
  6642. }
  6643. }
  6644. BuyResult_e CCSPlayer::BuyGunAmmo( CBaseCombatWeapon *pWeapon, bool bBlinkMoney )
  6645. {
  6646. if ( !CanPlayerBuy( false ) )
  6647. {
  6648. return BUY_PLAYER_CANT_BUY;
  6649. }
  6650. // Ensure that the weapon uses ammo
  6651. int nAmmo = pWeapon->GetPrimaryAmmoType();
  6652. if ( nAmmo == -1 )
  6653. {
  6654. return BUY_ALREADY_HAVE;
  6655. }
  6656. // Can only buy if the player does not already have full ammo
  6657. int maxcarry = pWeapon->GetReserveAmmoMax( AMMO_POSITION_PRIMARY );
  6658. if ( pWeapon->GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) >= maxcarry )
  6659. {
  6660. return BUY_ALREADY_HAVE;
  6661. }
  6662. // Purchase the ammo if the player has enough money
  6663. if ( GetAccountBalance() >= GetCSAmmoDef()->GetCost( nAmmo ) )
  6664. {
  6665. GiveAmmo( GetCSAmmoDef()->GetBuySize( nAmmo ), nAmmo, true );
  6666. AddAccount( -GetCSAmmoDef()->GetCost( nAmmo ), true, true, GetCSAmmoDef()->GetAmmoOfIndex( nAmmo )->pName );
  6667. return BUY_BOUGHT;
  6668. }
  6669. if ( bBlinkMoney )
  6670. {
  6671. // Not enough money.. let the player know
  6672. if( !m_bIsInAutoBuy && !m_bIsInRebuy )
  6673. ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
  6674. }
  6675. return BUY_CANT_AFFORD;
  6676. }
  6677. BuyResult_e CCSPlayer::BuyAmmo( int nSlot, bool bBlinkMoney )
  6678. {
  6679. if ( !CanPlayerBuy( false ) )
  6680. {
  6681. return BUY_PLAYER_CANT_BUY;
  6682. }
  6683. if ( nSlot < 0 || nSlot > 1 )
  6684. {
  6685. return BUY_INVALID_ITEM;
  6686. }
  6687. // Buy one ammo clip for all weapons in the given slot
  6688. //
  6689. // nSlot == 1 : Primary weapons
  6690. // nSlot == 2 : Secondary weapons
  6691. CBaseCombatWeapon *pSlot = Weapon_GetSlot( nSlot );
  6692. if ( !pSlot )
  6693. return BUY_INVALID_ITEM;
  6694. //MIKETODO: shield.
  6695. //if ( player->HasShield() && player->m_rgpPlayerItems[2] )
  6696. // pItem = player->m_rgpPlayerItems[2];
  6697. return BuyGunAmmo( pSlot, bBlinkMoney );
  6698. }
  6699. BuyResult_e CCSPlayer::AttemptToBuyAmmo( int iAmmoType )
  6700. {
  6701. Assert( iAmmoType == 0 || iAmmoType == 1 );
  6702. BuyResult_e result = BuyAmmo( iAmmoType, true );
  6703. if ( result == BUY_BOUGHT )
  6704. {
  6705. while ( BuyAmmo( iAmmoType, false ) == BUY_BOUGHT )
  6706. {
  6707. // empty loop - keep buying
  6708. }
  6709. return BUY_BOUGHT;
  6710. }
  6711. return result;
  6712. }
  6713. BuyResult_e CCSPlayer::AttemptToBuyAmmoSingle( int iAmmoType )
  6714. {
  6715. return BuyAmmo( iAmmoType, true );
  6716. }
  6717. void CCSPlayer::InternalAutoBuyAmmo( int nSlot )
  6718. {
  6719. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( nSlot );
  6720. if ( pWeapon )
  6721. {
  6722. int nAmmo = pWeapon->GetPrimaryAmmoType();
  6723. if ( nAmmo != -1 )
  6724. {
  6725. int maxAmmo = pWeapon->GetReserveAmmoMax( AMMO_POSITION_PRIMARY );
  6726. if ( pWeapon->GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) < maxAmmo )
  6727. {
  6728. bool playRearmSound = true;
  6729. do
  6730. {
  6731. pWeapon->GiveReserveAmmo( AMMO_POSITION_PRIMARY, GetCSAmmoDef()->GetBuySize( nAmmo ), playRearmSound );
  6732. playRearmSound = false;
  6733. }
  6734. while( pWeapon->GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) < maxAmmo );
  6735. }
  6736. }
  6737. }
  6738. }
  6739. void CCSPlayer::AutoBuyAmmo( bool bForce )
  6740. {
  6741. if ( (sv_autobuyammo.GetBool() || bForce) && ( m_flNextAutoBuyAmmoTime < gpGlobals->curtime ) && (CanPlayerBuy( false ) || CSGameRules()->IsPlayingCoopMission()) )
  6742. {
  6743. // only allow this check to happen every 2 seconds.
  6744. m_flNextAutoBuyAmmoTime = gpGlobals->curtime + SECONDS_BETWEEN_AUTOAMMOBUY_CHECKS - fmodf( gpGlobals->curtime - m_flNextAutoBuyAmmoTime, SECONDS_BETWEEN_AUTOAMMOBUY_CHECKS );
  6745. InternalAutoBuyAmmo( WEAPON_SLOT_PISTOL );
  6746. InternalAutoBuyAmmo( WEAPON_SLOT_RIFLE );
  6747. }
  6748. }
  6749. void CCSPlayer::GuardianForceFillAmmo( void )
  6750. {
  6751. if ( CSGameRules()->IsPlayingCoopGuardian() )
  6752. {
  6753. InternalAutoBuyAmmo( WEAPON_SLOT_PISTOL );
  6754. InternalAutoBuyAmmo( WEAPON_SLOT_RIFLE );
  6755. }
  6756. }
  6757. const char *RadioEventName[ RADIO_NUM_EVENTS+1 ] =
  6758. {
  6759. "RADIO_INVALID",
  6760. "EVENT_START_RADIO_1",
  6761. "EVENT_RADIO_GO_GO_GO",
  6762. "EVENT_RADIO_TEAM_FALL_BACK",
  6763. "EVENT_RADIO_STICK_TOGETHER_TEAM",
  6764. "EVENT_RADIO_HOLD_THIS_POSITION",
  6765. "EVENT_RADIO_FOLLOW_ME",
  6766. "EVENT_START_RADIO_2",
  6767. "EVENT_RADIO_AFFIRMATIVE",
  6768. "EVENT_RADIO_NEGATIVE",
  6769. "EVENT_RADIO_CHEER",
  6770. "EVENT_RADIO_COMPLIMENT",
  6771. "EVENT_RADIO_THANKS",
  6772. "EVENT_START_RADIO_3",
  6773. "EVENT_RADIO_ENEMY_SPOTTED",
  6774. "EVENT_RADIO_NEED_BACKUP",
  6775. "EVENT_RADIO_YOU_TAKE_THE_POINT",
  6776. "EVENT_RADIO_SECTOR_CLEAR",
  6777. "EVENT_RADIO_IN_POSITION",
  6778. // unused
  6779. "EVENT_RADIO_COVER_ME",
  6780. "EVENT_RADIO_REGROUP_TEAM",
  6781. "EVENT_RADIO_TAKING_FIRE",
  6782. "EVENT_RADIO_REPORT_IN_TEAM",
  6783. "EVENT_RADIO_REPORTING_IN",
  6784. "EVENT_RADIO_GET_OUT_OF_THERE",
  6785. "EVENT_RADIO_ENEMY_DOWN",
  6786. "EVENT_RADIO_STORM_THE_FRONT",
  6787. "EVENT_RADIO_END",
  6788. NULL // must be NULL-terminated
  6789. };
  6790. /**
  6791. * Convert name to RadioType
  6792. */
  6793. RadioType NameToRadioEvent( const char *name )
  6794. {
  6795. for( int i=0; RadioEventName[i]; ++i )
  6796. if (!stricmp( RadioEventName[i], name ) )
  6797. return static_cast<RadioType>( i );
  6798. return RADIO_INVALID;
  6799. }
  6800. void CCSPlayer::HandleMenu_Radio1( int slot )
  6801. {
  6802. if( m_iRadioMessages < 0 )
  6803. return;
  6804. if( m_flRadioTime > gpGlobals->curtime )
  6805. return;
  6806. m_iRadioMessages--;
  6807. m_flRadioTime = gpGlobals->curtime + 1.5;
  6808. switch ( slot )
  6809. {
  6810. case RADIO_COVER_ME :
  6811. Radio( "Radio.CoverMe", "#Cstrike_TitlesTXT_Cover_me" );
  6812. break;
  6813. case RADIO_YOU_TAKE_THE_POINT :
  6814. Radio( "Radio.YouTakeThePoint", "#Cstrike_TitlesTXT_You_take_the_point" );
  6815. break;
  6816. case RADIO_HOLD_THIS_POSITION :
  6817. Radio( "Radio.HoldPosition", "#Cstrike_TitlesTXT_Hold_this_position" );
  6818. break;
  6819. case RADIO_REGROUP_TEAM :
  6820. Radio( "Radio.Regroup", "#Cstrike_TitlesTXT_Regroup_team" );
  6821. break;
  6822. case RADIO_FOLLOW_ME :
  6823. Radio( "Radio.FollowMe", "#Cstrike_TitlesTXT_Follow_me" );
  6824. break;
  6825. case RADIO_TAKING_FIRE :
  6826. Radio( "Radio.TakingFire", "#Cstrike_TitlesTXT_Taking_fire" );
  6827. break;
  6828. }
  6829. // tell bots about radio message
  6830. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  6831. if ( event )
  6832. {
  6833. event->SetInt("userid", GetUserID() );
  6834. event->SetInt("slot", slot );
  6835. gameeventmanager->FireEvent( event );
  6836. }
  6837. }
  6838. void CCSPlayer::HandleMenu_Radio2( int slot )
  6839. {
  6840. if( m_iRadioMessages < 0 )
  6841. return;
  6842. if( m_flRadioTime > gpGlobals->curtime )
  6843. return;
  6844. m_iRadioMessages--;
  6845. m_flRadioTime = gpGlobals->curtime + 1.5;
  6846. switch ( slot )
  6847. {
  6848. case RADIO_GO_GO_GO :
  6849. Radio( "Radio.GoGoGo", "#Cstrike_TitlesTXT_Go_go_go" );
  6850. break;
  6851. case RADIO_TEAM_FALL_BACK :
  6852. Radio( "Radio.TeamFallBack", "#Cstrike_TitlesTXT_Team_fall_back" );
  6853. break;
  6854. case RADIO_STICK_TOGETHER_TEAM :
  6855. Radio( "Radio.StickTogether", "#Cstrike_TitlesTXT_Stick_together_team" );
  6856. break;
  6857. case RADIO_THANKS :
  6858. Radio( "Radio.Thanks", "#Cstrike_TitlesTXT_Thanks" );
  6859. break;
  6860. case RADIO_CHEER :
  6861. Radio( "Radio.Cheer", "#Cstrike_TitlesTXT_Cheer" );
  6862. break;
  6863. case RADIO_COMPLIMENT :
  6864. Radio( "Radio.Compliment", "#Cstrike_TitlesTXT_Compliment" );
  6865. break;
  6866. case RADIO_REPORT_IN_TEAM :
  6867. Radio( "Radio.ReportInTeam", "#Cstrike_TitlesTXT_Report_in_team" );
  6868. break;
  6869. }
  6870. // tell bots about radio message
  6871. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  6872. if ( event )
  6873. {
  6874. event->SetInt("userid", GetUserID() );
  6875. event->SetInt("slot", slot );
  6876. gameeventmanager->FireEvent( event );
  6877. }
  6878. }
  6879. void CCSPlayer::HandleMenu_Radio3( int slot )
  6880. {
  6881. if( m_iRadioMessages < 0 )
  6882. return;
  6883. if( m_flRadioTime > gpGlobals->curtime )
  6884. return;
  6885. m_iRadioMessages--;
  6886. m_flRadioTime = gpGlobals->curtime + 1.5;
  6887. switch ( slot )
  6888. {
  6889. case RADIO_AFFIRMATIVE :
  6890. if ( random->RandomInt( 0,1 ) )
  6891. Radio( "Radio.Affirmitive", "#Cstrike_TitlesTXT_Affirmative" );
  6892. else
  6893. Radio( "Radio.Roger", "#Cstrike_TitlesTXT_Roger_that" );
  6894. break;
  6895. case RADIO_ENEMY_SPOTTED :
  6896. Radio( "Radio.EnemySpotted", "#Cstrike_TitlesTXT_Enemy_spotted" );
  6897. break;
  6898. case RADIO_NEED_BACKUP :
  6899. Radio( "Radio.NeedBackup", "#Cstrike_TitlesTXT_Need_backup" );
  6900. break;
  6901. case RADIO_SECTOR_CLEAR :
  6902. Radio( "Radio.SectorClear", "#Cstrike_TitlesTXT_Sector_clear" );
  6903. break;
  6904. case RADIO_IN_POSITION :
  6905. Radio( "Radio.InPosition", "#Cstrike_TitlesTXT_In_position" );
  6906. break;
  6907. case RADIO_REPORTING_IN :
  6908. Radio( "Radio.ReportingIn", "#Cstrike_TitlesTXT_Reporting_in" );
  6909. break;
  6910. case RADIO_GET_OUT_OF_THERE :
  6911. Radio( "Radio.GetOutOfThere", "#Cstrike_TitlesTXT_Get_out_of_there" );
  6912. break;
  6913. case RADIO_NEGATIVE :
  6914. Radio( "Radio.Negative", "#Cstrike_TitlesTXT_Negative" );
  6915. break;
  6916. case RADIO_ENEMY_DOWN :
  6917. Radio( "Radio.EnemyDown", "#Cstrike_TitlesTXT_Enemy_down" );
  6918. break;
  6919. }
  6920. // tell bots about radio message
  6921. IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
  6922. if ( event )
  6923. {
  6924. event->SetInt("userid", GetUserID() );
  6925. event->SetInt("slot", slot );
  6926. gameeventmanager->FireEvent( event );
  6927. }
  6928. }
  6929. 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 )
  6930. {
  6931. CCSUsrMsg_RadioText msg;
  6932. msg.set_msg_dst( msg_dest );
  6933. msg.set_client( iClient );
  6934. msg.set_msg_name( msg_name );
  6935. if ( param1 )
  6936. msg.add_params( param1 );
  6937. else
  6938. msg.add_params( "" );
  6939. if ( param2 )
  6940. msg.add_params( param2 );
  6941. else
  6942. msg.add_params( "" );
  6943. if ( param3 )
  6944. msg.add_params( param3 );
  6945. else
  6946. msg.add_params( "" );
  6947. if ( param4 )
  6948. msg.add_params( param4 );
  6949. else
  6950. msg.add_params( "" );
  6951. SendUserMessage( filter, CS_UM_RadioText, msg );
  6952. }
  6953. void CCSPlayer::ConstructRadioFilter( CRecipientFilter& filter )
  6954. {
  6955. filter.MakeReliable();
  6956. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  6957. {
  6958. CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) );
  6959. if ( !player )
  6960. continue;
  6961. if ( player->IsHLTV() )
  6962. {
  6963. if ( tv_relayradio.GetBool() )
  6964. filter.AddRecipient( player );
  6965. else
  6966. continue;
  6967. }
  6968. else
  6969. {
  6970. // Skip players ignoring the radio
  6971. if ( player->m_bIgnoreRadio )
  6972. continue;
  6973. bool bTeamOnly = true;
  6974. #if defined ( CSTRIKE15 )
  6975. if ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() )
  6976. bTeamOnly = false;
  6977. #endif
  6978. if ( CSGameRules()->CanPlayerHearTalker( player, this, bTeamOnly ) )
  6979. filter.AddRecipient( player );
  6980. }
  6981. }
  6982. }
  6983. void CCSPlayer::Radio( const char *pszRadioSound, const char *pszRadioText, bool bTriggeredAutomatically )
  6984. {
  6985. if ( !IsAlive() )
  6986. return;
  6987. if ( IsObserver() )
  6988. return;
  6989. if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
  6990. return;
  6991. CRecipientFilter filter;
  6992. ConstructRadioFilter( filter );
  6993. // Don't show radio messages in chat for terrorists in coop missions
  6994. bool bHideRadioChatText = ( CSGameRules()->IsPlayingCoopMission() && GetTeamNumber() == TEAM_TERRORIST );
  6995. if ( pszRadioText && !bHideRadioChatText )
  6996. {
  6997. CFmtStr fmtPrintEntName( "#ENTNAME[%d]%s", entindex(), GetPlayerName() );
  6998. const char *pszLocationText = CSGameRules()->GetChatLocation( true, this );
  6999. if ( pszLocationText && *pszLocationText )
  7000. {
  7001. UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio_location", fmtPrintEntName.Access(), pszLocationText, pszRadioText, (bTriggeredAutomatically ? "auto" : "") );
  7002. }
  7003. else
  7004. {
  7005. UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio", fmtPrintEntName.Access(), pszRadioText, "", (bTriggeredAutomatically ? "auto" : "") );
  7006. }
  7007. }
  7008. if ( bot_chatter_use_rr.GetBool() )
  7009. {
  7010. AIConcept_t concept( pszRadioSound );
  7011. AI_CriteriaSet botCriteria;
  7012. if ( IsBot() )
  7013. {
  7014. if ( CSGameRules()->IsPlayingCoopMission() )
  7015. {
  7016. botCriteria.AppendCriteria( "gamemode", "coop" );
  7017. }
  7018. if ( HasHeavyArmor() )
  7019. {
  7020. botCriteria.AppendCriteria( "isheavy", 1 );
  7021. }
  7022. }
  7023. Speak( concept, &botCriteria, NULL, 0, &filter );
  7024. }
  7025. else
  7026. {
  7027. CCSUsrMsg_SendAudio msg;
  7028. msg.set_radio_sound( pszRadioSound );
  7029. SendUserMessage ( filter, CS_UM_SendAudio, msg );
  7030. }
  7031. //icon over the head for teammates
  7032. if ( !CSGameRules()->IsPlayingCoopMission() )
  7033. TE_RadioIcon( filter, 0.0, this );
  7034. }
  7035. //-----------------------------------------------------------------------------
  7036. // Purpose: Outputs currently connected players to the console
  7037. //-----------------------------------------------------------------------------
  7038. void CCSPlayer::ListPlayers()
  7039. {
  7040. char buf[64];
  7041. for ( int i=1; i <= gpGlobals->maxClients; i++ )
  7042. {
  7043. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
  7044. if ( pPlayer && !pPlayer->IsDormant() )
  7045. {
  7046. if ( pPlayer->IsBot() )
  7047. {
  7048. Q_snprintf( buf, sizeof(buf ), "B %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
  7049. }
  7050. else
  7051. {
  7052. Q_snprintf( buf, sizeof(buf ), " %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
  7053. }
  7054. ClientPrint( this, HUD_PRINTCONSOLE, buf );
  7055. }
  7056. }
  7057. ClientPrint( this, HUD_PRINTCONSOLE, "\n" );
  7058. }
  7059. //-----------------------------------------------------------------------------
  7060. // Purpose:
  7061. // Input : &info -
  7062. //-----------------------------------------------------------------------------
  7063. void CCSPlayer::OnDamagedByExplosion( const CTakeDamageInfo &info )
  7064. {
  7065. float lastDamage = info.GetDamage();
  7066. //Adrian - This is hacky since we might have been damaged by something else
  7067. //but since the round is ending, who cares.
  7068. if ( CSGameRules()->m_bTargetBombed == true )
  7069. return;
  7070. float distanceFromPlayer = 9999.0f;
  7071. CBaseEntity *inflictor = info.GetInflictor();
  7072. if ( inflictor )
  7073. {
  7074. Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin();
  7075. distanceFromPlayer = delta.Length();
  7076. }
  7077. bool shock = lastDamage >= 30.0f;
  7078. if ( !shock )
  7079. return;
  7080. m_applyDeafnessTime = gpGlobals->curtime + 0.3;
  7081. m_currentDeafnessFilter = 0;
  7082. }
  7083. void CCSPlayer::ApplyDeafnessEffect()
  7084. {
  7085. // what's happening here is that the low-pass filter and the oscillator frequency effects need
  7086. // to fade in and out slowly. So we have several filters that we switch between to achieve this
  7087. // effect. The first 3rd of the total effect will be the "fade in" of the effect. Which means going
  7088. // from filter to filter from the first to the last. Then we keep on the "last" filter for another
  7089. // third of the total effect time. Then the last third of the time we go back from the last filter
  7090. // to the first. Clear as mud?
  7091. // glossary:
  7092. // filter: an individual filter as defined in dsp_presets.txt
  7093. // section: one of the sections for the total effect, fade in, full, fade out are the possible sections
  7094. // effect: the total effect of combining all the sections, the whole of what the player hears from start to finish.
  7095. const int firstGrenadeFilterIndex = 137;
  7096. const int lastGrenadeFilterIndex = 139;
  7097. const float grenadeEffectLengthInSecs = 4.5f; // time of the total effect
  7098. const float fadeInSectionTime = 0.1f;
  7099. const float fadeOutSectionTime = 1.5f;
  7100. const float timeForEachFilterInFadeIn = fadeInSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex );
  7101. const float timeForEachFilterInFadeOut = fadeOutSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex );
  7102. float timeIntoEffect = gpGlobals->curtime - m_applyDeafnessTime;
  7103. if (timeIntoEffect >= grenadeEffectLengthInSecs )
  7104. {
  7105. // the effect is done, so reset the deafness variables.
  7106. m_applyDeafnessTime = 0.0f;
  7107. m_currentDeafnessFilter = 0;
  7108. return;
  7109. }
  7110. int section = 0;
  7111. if (timeIntoEffect < fadeInSectionTime )
  7112. {
  7113. section = 0;
  7114. }
  7115. else if (timeIntoEffect < (grenadeEffectLengthInSecs - fadeOutSectionTime ) )
  7116. {
  7117. section = 1;
  7118. }
  7119. else
  7120. {
  7121. section = 2;
  7122. }
  7123. int filterToUse = 0;
  7124. if (section == 0 )
  7125. {
  7126. // fade into the effect.
  7127. int filterIndex = (int )(timeIntoEffect / timeForEachFilterInFadeIn );
  7128. filterToUse = filterIndex += firstGrenadeFilterIndex;
  7129. }
  7130. else if (section == 1 )
  7131. {
  7132. // in full effect.
  7133. filterToUse = lastGrenadeFilterIndex;
  7134. }
  7135. else if (section == 2 )
  7136. {
  7137. // fade out of the effect
  7138. float timeIntoSection = timeIntoEffect - (grenadeEffectLengthInSecs - fadeOutSectionTime );
  7139. int filterIndex = (int )(timeIntoSection / timeForEachFilterInFadeOut );
  7140. filterToUse = lastGrenadeFilterIndex - filterIndex - 1;
  7141. }
  7142. if (filterToUse != m_currentDeafnessFilter )
  7143. {
  7144. m_currentDeafnessFilter = filterToUse;
  7145. CSingleUserRecipientFilter user( this );
  7146. enginesound->SetPlayerDSP( user, m_currentDeafnessFilter, false );
  7147. }
  7148. }
  7149. void CCSPlayer::NoteWeaponFired()
  7150. {
  7151. Assert( m_pCurrentCommand );
  7152. if( m_pCurrentCommand )
  7153. {
  7154. m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
  7155. }
  7156. // Remember the tickcount when the weapon was fired and lock viewangles here!
  7157. if ( m_iLockViewanglesTickNumber != gpGlobals->tickcount )
  7158. {
  7159. m_iLockViewanglesTickNumber = gpGlobals->tickcount;
  7160. m_qangLockViewangles = pl.v_angle;
  7161. }
  7162. }
  7163. bool CCSPlayer::WantsLagCompensationOnEntity( const CBaseEntity *entity, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
  7164. {
  7165. // No need to lag compensate at all if we're not attacking in this command and
  7166. // we haven't attacked recently.
  7167. if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5 ) )
  7168. {
  7169. if ( ( pCmd->buttons & (IN_ATTACK2 | IN_ZOOM) ) == 0 )
  7170. return false;
  7171. CWeaponCSBase *weapon = GetActiveCSWeapon();
  7172. if ( !weapon )
  7173. return false;
  7174. if ( weapon->GetCSWeaponID() != WEAPON_KNIFE )
  7175. return false; // IN_ATTACK2 with WEAPON_KNIFE should do lag compensation
  7176. }
  7177. return BaseClass::WantsLagCompensationOnEntity( entity, pCmd, pEntityTransmitBits );
  7178. }
  7179. // Handles the special "radio" alias commands we're creating to accommodate the scripts players use
  7180. // ** Returns true if we've handled the command **
  7181. bool HandleRadioAliasCommands( CCSPlayer *pPlayer, const char *pszCommand )
  7182. {
  7183. bool bRetVal = false;
  7184. // don't execute them if we are not alive or are an observer
  7185. if( !pPlayer->IsAlive() || pPlayer->IsObserver() )
  7186. return false;
  7187. // Radio1 commands
  7188. if ( FStrEq( pszCommand, "coverme" ) )
  7189. {
  7190. bRetVal = true;
  7191. pPlayer->HandleMenu_Radio1( RADIO_COVER_ME );
  7192. }
  7193. else if ( FStrEq( pszCommand, "takepoint" ) )
  7194. {
  7195. bRetVal = true;
  7196. pPlayer->HandleMenu_Radio1( RADIO_YOU_TAKE_THE_POINT );
  7197. }
  7198. else if ( FStrEq( pszCommand, "holdpos" ) )
  7199. {
  7200. bRetVal = true;
  7201. pPlayer->HandleMenu_Radio1( RADIO_HOLD_THIS_POSITION );
  7202. }
  7203. else if ( FStrEq( pszCommand, "regroup" ) )
  7204. {
  7205. bRetVal = true;
  7206. pPlayer->HandleMenu_Radio1( RADIO_REGROUP_TEAM );
  7207. }
  7208. else if ( FStrEq( pszCommand, "followme" ) )
  7209. {
  7210. bRetVal = true;
  7211. pPlayer->HandleMenu_Radio1( RADIO_FOLLOW_ME );
  7212. }
  7213. else if ( FStrEq( pszCommand, "takingfire" ) )
  7214. {
  7215. bRetVal = true;
  7216. pPlayer->HandleMenu_Radio1( RADIO_TAKING_FIRE );
  7217. }
  7218. // Radio2 commands
  7219. else if ( FStrEq( pszCommand, "go" ) )
  7220. {
  7221. bRetVal = true;
  7222. pPlayer->HandleMenu_Radio2( RADIO_GO_GO_GO );
  7223. }
  7224. else if ( FStrEq( pszCommand, "fallback" ) )
  7225. {
  7226. bRetVal = true;
  7227. pPlayer->HandleMenu_Radio2( RADIO_TEAM_FALL_BACK );
  7228. }
  7229. else if ( FStrEq( pszCommand, "sticktog" ) )
  7230. {
  7231. bRetVal = true;
  7232. pPlayer->HandleMenu_Radio2( RADIO_STICK_TOGETHER_TEAM );
  7233. }
  7234. else if ( FStrEq( pszCommand, "cheer" ) )
  7235. {
  7236. bRetVal = true;
  7237. pPlayer->HandleMenu_Radio2( RADIO_CHEER );
  7238. }
  7239. else if ( FStrEq( pszCommand, "thanks" ) )
  7240. {
  7241. bRetVal = true;
  7242. pPlayer->HandleMenu_Radio2( RADIO_THANKS );
  7243. }
  7244. else if ( FStrEq( pszCommand, "compliment" ) )
  7245. {
  7246. bRetVal = true;
  7247. pPlayer->HandleMenu_Radio2( RADIO_COMPLIMENT );
  7248. }
  7249. //else if ( FStrEq( pszCommand, "getinpos" ) )
  7250. //{
  7251. // bRetVal = true;
  7252. // pPlayer->HandleMenu_Radio2( 4 );
  7253. //}
  7254. //else if ( FStrEq( pszCommand, "stormfront" ) )
  7255. //{
  7256. // bRetVal = true;
  7257. // pPlayer->HandleMenu_Radio2( 5 );
  7258. //}
  7259. else if ( FStrEq( pszCommand, "report" ) )
  7260. {
  7261. bRetVal = true;
  7262. pPlayer->HandleMenu_Radio2( RADIO_REPORT_IN_TEAM );
  7263. }
  7264. // Radio3 commands
  7265. else if ( FStrEq( pszCommand, "roger" ) )
  7266. {
  7267. bRetVal = true;
  7268. pPlayer->HandleMenu_Radio3( RADIO_AFFIRMATIVE );
  7269. }
  7270. else if ( FStrEq( pszCommand, "enemyspot" ) )
  7271. {
  7272. bRetVal = true;
  7273. pPlayer->HandleMenu_Radio3( RADIO_ENEMY_SPOTTED );
  7274. }
  7275. else if ( FStrEq( pszCommand, "needbackup" ) )
  7276. {
  7277. bRetVal = true;
  7278. pPlayer->HandleMenu_Radio3( RADIO_NEED_BACKUP );
  7279. }
  7280. else if ( FStrEq( pszCommand, "sectorclear" ) )
  7281. {
  7282. bRetVal = true;
  7283. pPlayer->HandleMenu_Radio3( RADIO_SECTOR_CLEAR);
  7284. }
  7285. else if ( FStrEq( pszCommand, "inposition" ) )
  7286. {
  7287. bRetVal = true;
  7288. pPlayer->HandleMenu_Radio3( RADIO_IN_POSITION );
  7289. }
  7290. else if ( FStrEq( pszCommand, "reportingin" ) )
  7291. {
  7292. bRetVal = true;
  7293. pPlayer->HandleMenu_Radio3( RADIO_REPORTING_IN );
  7294. }
  7295. else if ( FStrEq( pszCommand, "getout" ) )
  7296. {
  7297. bRetVal = true;
  7298. pPlayer->HandleMenu_Radio3( RADIO_GET_OUT_OF_THERE );
  7299. }
  7300. else if ( FStrEq( pszCommand, "negative" ) )
  7301. {
  7302. bRetVal = true;
  7303. pPlayer->HandleMenu_Radio3( RADIO_NEGATIVE );
  7304. }
  7305. else if ( FStrEq( pszCommand, "enemydown" ) )
  7306. {
  7307. bRetVal = true;
  7308. pPlayer->HandleMenu_Radio3( RADIO_ENEMY_DOWN );
  7309. }
  7310. return bRetVal;
  7311. }
  7312. bool CCSPlayer::ShouldRunRateLimitedCommand( const CCommand &args )
  7313. {
  7314. const char *pcmd = args[0];
  7315. int i = m_RateLimitLastCommandTimes.Find( pcmd );
  7316. if ( i == m_RateLimitLastCommandTimes.InvalidIndex() )
  7317. {
  7318. m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime );
  7319. return true;
  7320. }
  7321. else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i] ) < CS_COMMAND_MAX_RATE )
  7322. {
  7323. // Too fast.
  7324. return false;
  7325. }
  7326. else
  7327. {
  7328. m_RateLimitLastCommandTimes[i] = gpGlobals->curtime;
  7329. return true;
  7330. }
  7331. }
  7332. bool CCSPlayer::ClientCommand( const CCommand &args )
  7333. {
  7334. const char *pcmd = args[0];
  7335. // Bots mimic our client commands.
  7336. /*
  7337. if ( bot_mimic.GetInt() && !( GetFlags() & FL_FAKECLIENT ) )
  7338. {
  7339. for ( int i=1; i <= gpGlobals->maxClients; i++ )
  7340. {
  7341. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
  7342. if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
  7343. {
  7344. pPlayer->ClientCommand( pcmd );
  7345. }
  7346. }
  7347. }
  7348. */
  7349. static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" );
  7350. const bool cbIsMatchmaking = sv_mmqueue_reservation.GetString()[ 0 ] == 'Q';
  7351. #if defined ( DEBUG )
  7352. if ( FStrEq( pcmd, "bot_cmd" ) )
  7353. {
  7354. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( atoi( args[1] ) ) );
  7355. if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
  7356. {
  7357. CCommand botArgs( args.ArgC() - 2, &args.ArgV()[2], kCommandSrcCode );
  7358. pPlayer->ClientCommand( botArgs );
  7359. pPlayer->RemoveEffects( EF_NODRAW );
  7360. }
  7361. return true;
  7362. }
  7363. if ( FStrEq( pcmd, "blind" ) )
  7364. {
  7365. if ( ShouldRunRateLimitedCommand( args ) )
  7366. {
  7367. if ( args.ArgC() == 3 )
  7368. {
  7369. Blind( atof( args[1] ), atof( args[2] ) );
  7370. }
  7371. else
  7372. {
  7373. ClientPrint( this, HUD_PRINTCONSOLE, "usage: blind holdtime fadetime\n" );
  7374. }
  7375. }
  7376. return true;
  7377. }
  7378. if ( FStrEq( pcmd, "deafen" ) )
  7379. {
  7380. Deafen( 0.0f );
  7381. return true;
  7382. }
  7383. if ( FStrEq( pcmd, "he_deafen" ) )
  7384. {
  7385. m_applyDeafnessTime = gpGlobals->curtime + 0.3;
  7386. m_currentDeafnessFilter = 0;
  7387. return true;
  7388. }
  7389. if ( FStrEq( pcmd, "hint_reset" ) )
  7390. {
  7391. m_iDisplayHistoryBits = 0;
  7392. return true;
  7393. }
  7394. if ( FStrEq( pcmd, "punch" ) )
  7395. {
  7396. float flDamage = 100;
  7397. QAngle punchAngle = GetViewPunchAngle();
  7398. punchAngle.x = flDamage * random->RandomFloat ( -0.15, 0.15 );
  7399. punchAngle.y = flDamage * random->RandomFloat ( -0.15, 0.15 );
  7400. punchAngle.z = flDamage * random->RandomFloat ( -0.15, 0.15 );
  7401. clamp( punchAngle.x, -4, punchAngle.x );
  7402. clamp( punchAngle.y, -5, 5 );
  7403. clamp( punchAngle.z, -5, 5 );
  7404. // +y == down
  7405. // +x == left
  7406. // +z == roll clockwise
  7407. if ( args.ArgC() == 4 )
  7408. {
  7409. punchAngle.x = atof(args[1] );
  7410. punchAngle.y = atof(args[2] );
  7411. punchAngle.z = atof(args[3] );
  7412. }
  7413. SetViewPunchAngle( punchAngle );
  7414. return true;
  7415. }
  7416. #endif //DEBUG
  7417. if ( FStrEq( pcmd, "jointeam" ) )
  7418. {
  7419. // Players can spam this at the wrong time to get into states we don't want them in. Ignore those cases.
  7420. if ( ( !IsBot() && !IsHLTV() ) && ( !m_bHasSeenJoinGame || cbIsMatchmaking ) )
  7421. return true;
  7422. if ( args.ArgC() < 2 )
  7423. {
  7424. Warning( "Player sent bad jointeam syntax\n" );
  7425. }
  7426. if ( ShouldRunRateLimitedCommand( args ) )
  7427. {
  7428. int iTeam = atoi( args[1] );
  7429. bool bForceChange = false;
  7430. if ( args.ArgC() > 2)
  7431. {
  7432. bForceChange = atoi( args[2] ) > 0;
  7433. }
  7434. if ( m_fForceTeam == -1.0f || gpGlobals->curtime < m_fForceTeam )
  7435. {
  7436. HandleCommand_JoinTeam( iTeam, !bForceChange );
  7437. }
  7438. }
  7439. return true;
  7440. }
  7441. // [dwenger] - reset the team info (used only in support of the "back" option
  7442. // from the class choice menu ).
  7443. else if ( FStrEq( pcmd, "resetteam" ) )
  7444. {
  7445. // Players can spam this at the wrong time to get into states we don't want them in. Ignore those cases.
  7446. if ( ( !IsBot() && !IsHLTV() ) && ( !m_bHasSeenJoinGame || cbIsMatchmaking ) )
  7447. return true;
  7448. m_bTeamChanged = false;
  7449. m_iOldTeam = TEAM_UNASSIGNED;
  7450. m_iClass = (int )CS_CLASS_NONE;
  7451. return true;
  7452. }
  7453. else if ( FStrEq( pcmd, "spectate" ) )
  7454. {
  7455. // Players can spam this at the wrong time to get into states we don't want them in. Ignore those cases.
  7456. if ( ( !IsBot() && !IsHLTV() ) && ( !m_bHasSeenJoinGame || cbIsMatchmaking ) )
  7457. return true;
  7458. if ( ShouldRunRateLimitedCommand( args ) )
  7459. {
  7460. // instantly join spectators
  7461. HandleCommand_JoinTeam( TEAM_SPECTATOR );
  7462. }
  7463. return true;
  7464. }
  7465. else if ( FStrEq( pcmd, "coach" ) )
  7466. {
  7467. // Players can spam this at the wrong time to get into states we don't want them in. Ignore those cases.
  7468. if ( ( !IsBot() && !IsHLTV() ) && ( !m_bHasSeenJoinGame || cbIsMatchmaking ) )
  7469. return true;
  7470. if ( sv_coaching_enabled.GetBool() && args.ArgC() == 2 )
  7471. {
  7472. if ( !CSGameRules()->IsFreezePeriod() && !CSGameRules()->IsWarmupPeriod() )
  7473. {
  7474. Msg( "You can only choose to coach a team during warmup or freeze time.\n" );
  7475. return true;
  7476. }
  7477. CReliableBroadcastRecipientFilter filter;
  7478. if ( FStrEq( args[1], "ct") || FStrEq( args[1], "t") )
  7479. {
  7480. if ( ShouldRunRateLimitedCommand( args ) )
  7481. {
  7482. // already coaching CTs
  7483. if ( FStrEq( args[1], "ct") )
  7484. {
  7485. if ( GetCoachingTeam() == TEAM_CT )
  7486. {
  7487. return true;
  7488. }
  7489. HandleCommand_JoinTeam( TEAM_SPECTATOR, false, TEAM_CT );
  7490. UTIL_SayText2Filter( filter, this, kEUtilSayTextMessageType_AllChat, "#CSGO_Coach_Join_CT", GetPlayerName() );
  7491. }
  7492. else if ( FStrEq( args[1], "t") )
  7493. {
  7494. // already coaching Ts
  7495. if ( GetCoachingTeam() == TEAM_TERRORIST )
  7496. {
  7497. return true;
  7498. }
  7499. HandleCommand_JoinTeam( TEAM_SPECTATOR, false, TEAM_TERRORIST );
  7500. UTIL_SayText2Filter( filter, this, kEUtilSayTextMessageType_AllChat, "#CSGO_Coach_Join_T", GetPlayerName() );
  7501. }
  7502. SetObserverMode( OBS_MODE_IN_EYE );
  7503. }
  7504. }
  7505. else
  7506. {
  7507. Msg( "Type either 'coach ct' or 'coach t'.\n" );
  7508. m_iCoachingTeam = 0;
  7509. }
  7510. }
  7511. else if ( !sv_coaching_enabled.GetBool() )
  7512. {
  7513. Msg( "Coaching is disabled. The server needs to set sv_coaching_enabled 1.\n" );
  7514. m_iCoachingTeam = 0;
  7515. }
  7516. else if ( args.ArgC() != 2 )
  7517. {
  7518. Msg( "Type either 'coach ct' or 'coach t'.\n" );
  7519. m_iCoachingTeam = 0;
  7520. }
  7521. return true;
  7522. }
  7523. else if ( FStrEq( pcmd, "joingame" ) )
  7524. {
  7525. // player just closed MOTD dialog
  7526. if ( m_iPlayerState == STATE_WELCOME )
  7527. {
  7528. if ( !CSGameRules() )
  7529. return false;
  7530. SetContextThink( &CBasePlayer::PlayerForceTeamThink, gpGlobals->curtime + 0.5f, CS_FORCE_TEAM_THINK_CONTEXT );
  7531. int nAutoJoinTeam = 0;
  7532. if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
  7533. nAutoJoinTeam = TEAM_CT;
  7534. if ( !IsBot() && CSGameRules()->IsPlayingCoopMission() )
  7535. nAutoJoinTeam = TEAM_CT;
  7536. if ( CSGameRules()->IsPlayingCoopGuardian() )
  7537. {
  7538. if ( CSGameRules()->IsHostageRescueMap() )
  7539. {
  7540. nAutoJoinTeam = TEAM_TERRORIST;
  7541. }
  7542. else
  7543. {
  7544. nAutoJoinTeam = TEAM_CT;
  7545. }
  7546. }
  7547. if ( nAutoJoinTeam != 0 )
  7548. {
  7549. HandleCommand_JoinTeam( nAutoJoinTeam, false );
  7550. }
  7551. else
  7552. {
  7553. // When playing queued matchmaking we will just automatically join the correct team
  7554. if ( cbIsMatchmaking )
  7555. HandleCommand_JoinTeam( TEAM_CT, false ); // join team code will actually auto-adjust the team based on server queue reservation
  7556. else
  7557. State_Transition( STATE_PICKINGTEAM );
  7558. }
  7559. // Let other commands know they may now be legal.
  7560. m_bHasSeenJoinGame = true;
  7561. }
  7562. return true;
  7563. }
  7564. // else if ( FStrEq( pcmd, "joinclass" ) )
  7565. // {
  7566. // if ( ShouldRunRateLimitedCommand( args ) && ( !g_pGameRules->IgnorePlayerKillCommand() || !IsAlive() ) )
  7567. // {
  7568. // HandleCommand_JoinClass();
  7569. // }
  7570. // return true;
  7571. // }
  7572. else if ( FStrEq( pcmd, "drop" ) )
  7573. {
  7574. if ( !AttemptToBuyDMBonusWeapon() ) // Overload 'drop' when in deathmatch.
  7575. HandleDropWeapon();
  7576. return true;
  7577. }
  7578. else if ( FStrEq( pcmd, "buy" ) )
  7579. {
  7580. BuyResult_e result = BUY_INVALID_ITEM;
  7581. if ( args.ArgC() == 3 )
  7582. {
  7583. int nPos = atoi( args[2] );
  7584. result = HandleCommand_Buy( args[1], nPos );
  7585. }
  7586. else if ( args.ArgC() == 2 )
  7587. {
  7588. AutoBuyInfoStruct * commandInfo = GetAutoBuyCommandInfo( args[1] );
  7589. if ( commandInfo )
  7590. result = HandleCommand_Buy( args[1], commandInfo->m_LoadoutPosition );
  7591. }
  7592. if ( ( result == BUY_INVALID_ITEM ) && ShouldRunRateLimitedCommand( args ) ) // rate limit spew
  7593. {
  7594. // Print out a message on the console
  7595. int msg_dest = HUD_PRINTCONSOLE;
  7596. ClientPrint( this, msg_dest, "usage: buy <item>\n" );
  7597. ClientPrint( this, msg_dest, " p228\n" );
  7598. ClientPrint( this, msg_dest, " glock\n" );
  7599. ClientPrint( this, msg_dest, " scout\n" );
  7600. ClientPrint( this, msg_dest, " xm1014\n" );
  7601. ClientPrint( this, msg_dest, " mac10\n" );
  7602. ClientPrint( this, msg_dest, " aug\n" );
  7603. ClientPrint( this, msg_dest, " elite\n" );
  7604. ClientPrint( this, msg_dest, " fiveseven\n" );
  7605. ClientPrint( this, msg_dest, " ump45\n" );
  7606. ClientPrint( this, msg_dest, " sg550\n" );
  7607. ClientPrint( this, msg_dest, " galil\n" );
  7608. ClientPrint( this, msg_dest, " galilar\n" );
  7609. ClientPrint( this, msg_dest, " famas\n" );
  7610. ClientPrint( this, msg_dest, " usp_silencer\n" );
  7611. ClientPrint( this, msg_dest, " awp\n" );
  7612. ClientPrint( this, msg_dest, " mp5navy\n" );
  7613. ClientPrint( this, msg_dest, " m249\n" );
  7614. ClientPrint( this, msg_dest, " nova\n" );
  7615. ClientPrint( this, msg_dest, " m4a1\n" );
  7616. ClientPrint( this, msg_dest, " m4a1_silencer\n" );
  7617. ClientPrint( this, msg_dest, " tmp\n" );
  7618. ClientPrint( this, msg_dest, " g3sg1\n" );
  7619. ClientPrint( this, msg_dest, " deagle\n" );
  7620. ClientPrint( this, msg_dest, " sg552\n" );
  7621. ClientPrint( this, msg_dest, " ak47\n" );
  7622. ClientPrint( this, msg_dest, " p90\n" );
  7623. ClientPrint( this, msg_dest, " bizon\n" );
  7624. ClientPrint( this, msg_dest, " mag7\n" );
  7625. ClientPrint( this, msg_dest, " negev\n" );
  7626. ClientPrint( this, msg_dest, " sawedoff\n" );
  7627. ClientPrint( this, msg_dest, " tec9\n" );
  7628. ClientPrint( this, msg_dest, " taser\n" );
  7629. ClientPrint( this, msg_dest, " hkp2000\n" );
  7630. ClientPrint( this, msg_dest, " mp7\n" );
  7631. ClientPrint( this, msg_dest, " mp9\n" );
  7632. ClientPrint( this, msg_dest, " nova\n" );
  7633. ClientPrint( this, msg_dest, " p250\n" );
  7634. ClientPrint( this, msg_dest, " scar17\n" );
  7635. ClientPrint( this, msg_dest, " scar20\n" );
  7636. ClientPrint( this, msg_dest, " sg556\n" );
  7637. ClientPrint( this, msg_dest, " ssg08\n" );
  7638. ClientPrint( this, msg_dest, " flashbang\n" );
  7639. ClientPrint( this, msg_dest, " smokegrenade\n" );
  7640. ClientPrint( this, msg_dest, " hegrenade\n" );
  7641. ClientPrint( this, msg_dest, " molotov\n" );
  7642. ClientPrint( this, msg_dest, " decoy\n" );
  7643. ClientPrint( this, msg_dest, " primammo\n" );
  7644. ClientPrint( this, msg_dest, " secammo\n" );
  7645. }
  7646. return true;
  7647. }
  7648. else if ( FStrEq( pcmd, "autobuy" ) )
  7649. {
  7650. // hijack autobuy for when money isnt relevant and we want random weapons instead, such as deathmatch.
  7651. if ( CSGameRules()->IsPlayingGunGameDeathmatch() )
  7652. {
  7653. if ( IsBuyMenuOpen() )
  7654. {
  7655. BuyRandom();
  7656. }
  7657. else
  7658. {
  7659. engine->ClientCommand( edict(), "dm_togglerandomweapons" );
  7660. }
  7661. }
  7662. else
  7663. {
  7664. AutoBuy( ( args.ArgC() > 1 ) ? args.Arg( 1 ) : "" );
  7665. }
  7666. return true;
  7667. }
  7668. else if ( FStrEq( pcmd, "rebuy" ) )
  7669. {
  7670. Rebuy( (args.ArgC() > 1) ? args.Arg( 1 ) : "" );
  7671. return true;
  7672. }
  7673. else if ( FStrEq( pcmd, "open_buymenu" ) )
  7674. {
  7675. SetBuyMenuOpen( true );
  7676. return true;
  7677. }
  7678. else if ( FStrEq( pcmd, "close_buymenu" ) )
  7679. {
  7680. SetBuyMenuOpen( false );
  7681. return true;
  7682. }
  7683. // else if ( FStrEq( pcmd, "buyammo1" ) )
  7684. // {
  7685. // AttemptToBuyAmmoSingle(0 );
  7686. // return true;
  7687. // }
  7688. // else if ( FStrEq( pcmd, "buyammo2" ) )
  7689. // {
  7690. // AttemptToBuyAmmoSingle(1 );
  7691. // return true;
  7692. // }
  7693. else if ( FStrEq( pcmd, "mute" ) )
  7694. {
  7695. // This is only used in scaleform (by the scoreboard),
  7696. // but there is an error if the binding doesn't have a handler in code.
  7697. // So we simply add a handler to to eat the command here.
  7698. return true;
  7699. }
  7700. else if ( FStrEq( pcmd, "nightvision" ) )
  7701. {
  7702. if ( ShouldRunRateLimitedCommand( args ) )
  7703. {
  7704. if( m_bHasNightVision )
  7705. {
  7706. if( m_bNightVisionOn )
  7707. {
  7708. CBroadcastRecipientFilter filter;
  7709. EmitSound( filter, entindex(), "Player.NightVisionOff" );
  7710. }
  7711. else
  7712. {
  7713. CBroadcastRecipientFilter filter;
  7714. EmitSound( filter, entindex(), "Player.NightVisionOn" );
  7715. }
  7716. m_bNightVisionOn = !m_bNightVisionOn;
  7717. }
  7718. }
  7719. return true;
  7720. }
  7721. else if ( FStrEq( pcmd, "extendfreeze" ) )
  7722. {
  7723. if ( ShouldRunRateLimitedCommand( args ) && CSGameRules() && !CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() )
  7724. {
  7725. m_flDeathTime += 2.0f;
  7726. }
  7727. return true;
  7728. }
  7729. else if ( FStrEq( pcmd, "menuselect" ) )
  7730. {
  7731. return true;
  7732. }
  7733. else if ( HandleRadioAliasCommands( this, pcmd ) )
  7734. {
  7735. return true;
  7736. }
  7737. else if ( FStrEq( pcmd, "listplayers" ) )
  7738. {
  7739. if ( ShouldRunRateLimitedCommand( args ) )
  7740. ListPlayers();
  7741. return true;
  7742. }
  7743. else if ( FStrEq( pcmd, "ignorerad" ) )
  7744. {
  7745. if ( ShouldRunRateLimitedCommand( args ) )
  7746. {
  7747. m_bIgnoreRadio = !m_bIgnoreRadio;
  7748. if ( m_bIgnoreRadio )
  7749. {
  7750. ClientPrint( this, HUD_PRINTTALK, "#Ignore_Radio" );
  7751. }
  7752. else
  7753. {
  7754. ClientPrint( this, HUD_PRINTTALK, "#Accept_Radio" );
  7755. }
  7756. }
  7757. return true;
  7758. }
  7759. else if ( FStrEq( pcmd, "become_vip" ) )
  7760. {
  7761. //MIKETODO: VIP mode
  7762. /*
  7763. if ( ( CSGameRules()->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
  7764. {
  7765. mp->AddToVIPQueue( this );
  7766. }
  7767. */
  7768. return true;
  7769. }
  7770. else if ( FStrEq( pcmd, "spec_next" ) || FStrEq( pcmd, "spec_prev" ) || FStrEq( pcmd, "spec_grenade" ) || FStrEq( pcmd, "spec_scoreboard" ) )
  7771. {
  7772. if ( GetTeamNumber() == TEAM_SPECTATOR )
  7773. {
  7774. // Showing the scoreboard should ensure a spectating player doesn't go AFK
  7775. m_flLastAction = gpGlobals->curtime;
  7776. }
  7777. return true; // we handled this command, don't spam console
  7778. }
  7779. else if ( FStrEq( pcmd, "endmatch_votenextmap" ) )
  7780. {
  7781. if ( GetTeamNumber() == TEAM_SPECTATOR )
  7782. return false;
  7783. int index = atoi( args[1] );
  7784. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  7785. if ( !pResource || pResource->EndMatchNextMapAllVoted() || ( m_nEndMatchNextMapVote > -1 ) ||
  7786. ( index < 0 ) || ( index >= MAX_ENDMATCH_VOTE_PANELS ) || !CSGameRules() ||
  7787. !CSGameRules()->IsEndMatchVotingForNextMap() || ( CSGameRules()->m_nEndMatchMapGroupVoteOptions[index] == -1 ) )
  7788. {
  7789. CSingleUserRecipientFilter filter( this );
  7790. EmitSound( filter, entindex(), "Vote.Failed" );
  7791. }
  7792. else
  7793. {
  7794. // TODO: make a sound or message when you try to vote twice?
  7795. m_nEndMatchNextMapVote = index;
  7796. }
  7797. return true;
  7798. }
  7799. else if ( FStrEq( pcmd, "+taunt" ) )
  7800. {
  7801. m_bIsHoldingTaunt = true;
  7802. if ( ShouldRunRateLimitedCommand( args ) )
  7803. {
  7804. Taunt();
  7805. }
  7806. return true;
  7807. }
  7808. else if ( FStrEq( pcmd, "-taunt" ) )
  7809. {
  7810. m_bIsHoldingTaunt = false;
  7811. return true;
  7812. }
  7813. else if ( FStrEq( pcmd, "+lookatweapon" ) )
  7814. {
  7815. m_bIsHoldingLookAtWeapon = true;
  7816. if ( ShouldRunRateLimitedCommand( args ) )
  7817. {
  7818. LookAtHeldWeapon();
  7819. }
  7820. return true;
  7821. }
  7822. else if ( FStrEq( pcmd, "-lookatweapon" ) )
  7823. {
  7824. m_bIsHoldingLookAtWeapon = false;
  7825. return true;
  7826. }
  7827. return BaseClass::ClientCommand( args );
  7828. }
  7829. bool CCSPlayer::AllowTaunts( void )
  7830. {
  7831. // Check to see if we are in water (above our waist).
  7832. if ( GetWaterLevel() > WL_Waist )
  7833. return false;
  7834. // Check to see if we are on the ground.
  7835. if ( GetGroundEntity() == NULL )
  7836. return false;
  7837. return true;
  7838. }
  7839. void CCSPlayer::Taunt( void )
  7840. {
  7841. return;
  7842. /*
  7843. if ( IsTaunting() )
  7844. return;
  7845. // We always allow first person taunts, but 3rd person is more restricted
  7846. m_bIsThirdPersonTaunt = AllowTaunts() && CSGameRules() && CSGameRules()->AllowTaunts();
  7847. int nSequence = ACTIVITY_NOT_AVAILABLE;
  7848. // Need a weapon to taunt
  7849. CWeaponCSBase *pActiveWeapon = GetActiveCSWeapon();
  7850. if ( !pActiveWeapon )
  7851. return;
  7852. // Can't taunt while zoomed
  7853. if ( pActiveWeapon->IsZoomed() )
  7854. return;
  7855. //if ( m_bUseNewAnimstate && m_PlayerAnimStateCSGO )
  7856. //{
  7857. // // just hardcode the team-specific taunts for now
  7858. // m_flTauntEndTime = m_PlayerAnimStateCSGO->AttemptTauntAnimation(
  7859. // GetTeamNumber() == TEAM_TERRORIST ? "taunt_fistswing_pump" : "taunt_salute"
  7860. // );
  7861. //
  7862. // m_bIsTaunting = ( m_flTauntEndTime > gpGlobals->curtime );
  7863. //}
  7864. //else
  7865. {
  7866. // Do cool 3rd person taunt if this weapon allows it
  7867. CEconItemView *pItemView = pActiveWeapon->GetAttributeContainer()->GetItem();
  7868. if ( !pItemView->IsValid() )
  7869. return;
  7870. const CEconTauntDefinition *pTauntDef = GetItemSchema()->GetTauntDefinition( pItemView->GetTauntID() );
  7871. if ( !pTauntDef )
  7872. return;
  7873. const char *pchTauntSequenceName = "";
  7874. pchTauntSequenceName = pTauntDef->GetSequenceName();
  7875. if ( StringIsEmpty( pchTauntSequenceName ) )
  7876. return;
  7877. nSequence = LookupSequence( pchTauntSequenceName );
  7878. if ( nSequence == ACTIVITY_NOT_AVAILABLE )
  7879. return;
  7880. m_bIsTaunting = true;
  7881. m_flTauntEndTime = gpGlobals->curtime + SequenceDuration( nSequence );
  7882. }
  7883. // m_flTauntYaw = BodyAngles().y + 160.0f;
  7884. // m_bMustNotMoveDuringTaunt = false;
  7885. */
  7886. }
  7887. void CCSPlayer::LookAtHeldWeapon( void )
  7888. {
  7889. if ( IsTaunting() || IsLookingAtWeapon() )
  7890. return;
  7891. int nSequence = ACTIVITY_NOT_AVAILABLE;
  7892. // Need a weapon to taunt
  7893. CWeaponCSBase *pActiveWeapon = GetActiveCSWeapon();
  7894. if ( !pActiveWeapon )
  7895. return;
  7896. // Can't taunt while zoomed, reloading, or switching silencer
  7897. if ( pActiveWeapon->IsZoomed() || pActiveWeapon->m_bInReload || pActiveWeapon->IsSwitchingSilencer() )
  7898. return;
  7899. // don't let me inspect a shotgun that's reloading
  7900. if ( pActiveWeapon->GetWeaponType() == WEAPONTYPE_SHOTGUN && pActiveWeapon->GetShotgunReloadState() != 0 )
  7901. {
  7902. return;
  7903. }
  7904. #ifdef IRONSIGHT
  7905. if ( pActiveWeapon->m_iIronSightMode == IronSight_should_approach_sighted )
  7906. return;
  7907. #endif
  7908. CBaseViewModel *pViewModel = GetViewModel();
  7909. if ( pViewModel )
  7910. {
  7911. nSequence = pViewModel->SelectWeightedSequence( ACT_VM_IDLE_LOWERED );
  7912. if ( nSequence == ACT_INVALID )
  7913. nSequence = pViewModel->LookupSequence( "lookat01" );
  7914. // make sure the silencer bodygroup is correct
  7915. if ( GetActiveCSWeapon() && GetActiveCSWeapon()->HasSilencer() )
  7916. {
  7917. pViewModel->SetBodygroup( pViewModel->FindBodygroupByName( "silencer" ), GetActiveCSWeapon()->IsSilenced() ? 0 : 1 );
  7918. }
  7919. IGameEvent * event = gameeventmanager->CreateEvent( "inspect_weapon" );
  7920. if ( event )
  7921. {
  7922. event->SetInt( "userid", GetUserID() );
  7923. gameeventmanager->FireEvent( event );
  7924. }
  7925. // UNDONE: Nothing happens if the weapon has no lookat
  7926. /*if ( nSequence == ACTIVITY_NOT_AVAILABLE )
  7927. {
  7928. // Fallback to draw since not all the lookats have been created
  7929. nSequence = pViewModel->LookupSequence( "draw" );
  7930. }*/
  7931. if ( nSequence != ACTIVITY_NOT_AVAILABLE )
  7932. {
  7933. m_flLookWeaponEndTime = gpGlobals->curtime + pViewModel->SequenceDuration( nSequence );
  7934. m_bIsLookingAtWeapon = true;
  7935. pViewModel->ForceCycle( 0 );
  7936. pViewModel->ResetSequence( nSequence );
  7937. }
  7938. }
  7939. }
  7940. // called by the jointeam to command to let the client know the reason
  7941. // for the jointeam having failed
  7942. void CCSPlayer::SendJoinTeamFailedMessage( int reason, bool raiseTeamScreen )
  7943. {
  7944. IGameEvent * event = gameeventmanager->CreateEvent( "jointeam_failed" );
  7945. if ( event )
  7946. {
  7947. event->SetInt( "userid", GetUserID() );
  7948. event->SetInt( "reason", reason );
  7949. gameeventmanager->FireEvent( event );
  7950. }
  7951. if ( raiseTeamScreen && IsHLTV() == false )
  7952. {
  7953. ShowViewPortPanel( PANEL_TEAM );
  7954. }
  7955. }
  7956. // returns true if the selection has been handled and the player's menu
  7957. // can be closed...false if the menu should be displayed again
  7958. // if bQueue is true then the team move will not occur until round restart
  7959. bool CCSPlayer::HandleCommand_JoinTeam( int team, bool bQueue, int iCoachTeam )
  7960. {
  7961. //coaching
  7962. if ( ( m_iCoachingTeam != 0 ) && ( iCoachTeam == 0 ) )
  7963. {
  7964. CReliableBroadcastRecipientFilter filter;
  7965. UTIL_SayText2Filter( filter, this, kEUtilSayTextMessageType_AllChat, "#CSGO_No_Longer_Coach", GetPlayerName() );
  7966. }
  7967. m_iCoachingTeam = iCoachTeam;
  7968. // prevent suicide for deranking
  7969. if ( g_pGameRules->IgnorePlayerKillCommand() && IsAlive() )
  7970. return false;
  7971. if ( CSGameRules()->IsPlayingCoopGuardian() )
  7972. {
  7973. int nTeam = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  7974. if ( GetTeamNumber() == nTeam )
  7975. return false;
  7976. }
  7977. if ( CSGameRules()->IsPlayingCooperativeGametype() )
  7978. {
  7979. int nTeam = TEAM_CT;
  7980. if ( GetTeamNumber() == nTeam )
  7981. return false;
  7982. }
  7983. if ( IsAbleToInstantRespawn() )
  7984. {
  7985. bQueue = false;
  7986. }
  7987. if ( !IsBot() )
  7988. {
  7989. if (( team == TEAM_UNASSIGNED ) && ( m_iLastTeam == TEAM_SPECTATOR ) && (GetTeamNumber() != TEAM_SPECTATOR) )
  7990. {
  7991. // Default the player to the spectator team since the player was a spectator during the last match
  7992. team = TEAM_SPECTATOR;
  7993. }
  7994. }
  7995. CCSGameRules *mp = CSGameRules();
  7996. //
  7997. // When playing queued matchmaking we force team assignment based on GC configuration
  7998. //
  7999. static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" );
  8000. if ( !IsBot() && ( sv_mmqueue_reservation.GetString()[0] == 'Q' ) )
  8001. {
  8002. CSteamID const *pThisSteamId = engine->GetClientSteamID( this->edict() );
  8003. int numTotalPlayers = 0;
  8004. int idxThisPlayer = -1;
  8005. for ( char const *pszPrev = sv_mmqueue_reservation.GetString(), *pszNext = pszPrev;
  8006. ( pszNext = strchr( pszPrev, '[' ) ) != NULL; pszPrev = pszNext + 1 )
  8007. {
  8008. uint32 uiAccountId = 0;
  8009. sscanf( pszNext, "[%x]", &uiAccountId );
  8010. if ( uiAccountId && pThisSteamId && ( pThisSteamId->GetAccountID() == uiAccountId ) )
  8011. {
  8012. idxThisPlayer = numTotalPlayers;
  8013. }
  8014. ++ numTotalPlayers;
  8015. }
  8016. if ( idxThisPlayer == -1 )
  8017. {
  8018. if ( strstr( sv_mmqueue_reservation.GetString(), CFmtStr( "{%x}", pThisSteamId ? pThisSteamId->GetAccountID() : 0 ) ) )
  8019. idxThisPlayer = -2;
  8020. team = TEAM_SPECTATOR;
  8021. DevMsg( "Forcing spectator team for %s player/%d (%x - %s).\n", ( ( idxThisPlayer == -2 ) ? "caster" : "UNKNOWN" ),
  8022. numTotalPlayers, ( pThisSteamId ? pThisSteamId->GetAccountID() : 0 ),
  8023. GetPlayerName() );
  8024. }
  8025. else
  8026. {
  8027. bool bThisPlayerIsFirstTeam = ( idxThisPlayer < ( numTotalPlayers / 2 ) );
  8028. bool bAreTeamsSwitched = mp ? mp->AreTeamsPlayingSwitchedSides() : false;
  8029. // Force team according to queue reservation
  8030. team = ( bThisPlayerIsFirstTeam == !bAreTeamsSwitched ) ? TEAM_CT : TEAM_TERRORIST;
  8031. // In cooperative mode use the map type
  8032. if ( mp )
  8033. {
  8034. if ( mp->IsPlayingCoopGuardian() )
  8035. {
  8036. if ( mp->IsHostageRescueMap() )
  8037. team = TEAM_TERRORIST;
  8038. else
  8039. team = TEAM_CT;
  8040. }
  8041. else if ( mp->IsPlayingCoopMission() )
  8042. {
  8043. team = TEAM_CT;
  8044. }
  8045. }
  8046. DevMsg( "Team for queue player#%d/%d (%x - %s) on %s team for %s teams: %s\n",
  8047. idxThisPlayer+1, numTotalPlayers, ( pThisSteamId ? pThisSteamId->GetAccountID() : 0 ),
  8048. GetPlayerName(), bThisPlayerIsFirstTeam ? "first" : "second", bAreTeamsSwitched ? "switched" : "normal",
  8049. ( (team == TEAM_CT) ? "CT" : "T" ) );
  8050. // Mark player as having connected once
  8051. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pThisSteamId->GetAccountID() ) )
  8052. {
  8053. if ( !pQMM->m_bEverFullyConnected )
  8054. {
  8055. pQMM->m_bEverFullyConnected = true;
  8056. }
  8057. }
  8058. }
  8059. }
  8060. else if ( mp && ( ( mp->GetGamePhase() == GAMEPHASE_WARMUP_ROUND ) || mp->IsWarmupPeriod() ) )
  8061. {
  8062. // Allow immediate team changes in warmup
  8063. bQueue = false;
  8064. }
  8065. if ( !GetGlobalTeam( team ) )
  8066. {
  8067. DevWarning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team );
  8068. return false;
  8069. }
  8070. // If this player is a VIP, don't allow him to switch teams/appearances unless the following conditions are met :
  8071. // a ) There is another TEAM_CT player who is in the queue to be a VIP
  8072. // b ) This player is dead
  8073. //MIKETODO: handle this when doing VIP mode
  8074. /*
  8075. if ( m_bIsVIP == true )
  8076. {
  8077. if ( !IsDead() )
  8078. {
  8079. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
  8080. MenuReset();
  8081. return true;
  8082. }
  8083. else if ( mp->IsVIPQueueEmpty() == true )
  8084. {
  8085. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
  8086. MenuReset();
  8087. return true;
  8088. }
  8089. }
  8090. //MIKETODO: VIP mode
  8091. case 3:
  8092. if ( ( mp->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
  8093. {
  8094. mp->AddToVIPQueue( player );
  8095. MenuReset();
  8096. return true;
  8097. }
  8098. else
  8099. {
  8100. return false;
  8101. }
  8102. break;
  8103. */
  8104. int currTeam = GetTeamNumber();
  8105. // If we already died and changed teams once, deny
  8106. if ( bQueue && ( ( GetPendingTeamNumber() != GetTeamNumber() ) || m_bJustBecameSpectator ) )
  8107. {
  8108. SendJoinTeamFailedMessage( TeamJoinFailedReason::CHANGED_TOO_OFTEN, true );
  8109. return true;
  8110. }
  8111. // check if we're limited in our team selection
  8112. if ( team == TEAM_UNASSIGNED && !IsBot() )
  8113. {
  8114. team = mp->GetHumanTeam(); // returns TEAM_UNASSIGNED if we're unrestricted
  8115. }
  8116. if ( team == TEAM_UNASSIGNED )
  8117. {
  8118. // Attempt to auto-select a team, may set team to T, CT or SPEC
  8119. team = mp->SelectDefaultTeam( !IsBot() );
  8120. if ( team == TEAM_UNASSIGNED )
  8121. {
  8122. // still team unassigned, try to kick a bot if possible
  8123. // kick a bot to allow human to join
  8124. if ( cv_bot_auto_vacate.GetBool() && !IsBot() )
  8125. {
  8126. team = ( random->RandomInt( 0, 1 ) == 0 ) ? TEAM_TERRORIST : TEAM_CT;
  8127. if ( UTIL_KickBotFromTeam( team, bQueue ) == false )
  8128. {
  8129. // no bots on that team, try the other
  8130. team = ( team == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
  8131. if ( UTIL_KickBotFromTeam( team, bQueue ) == false )
  8132. {
  8133. // couldn't kick any bots, fail
  8134. team = TEAM_UNASSIGNED;
  8135. }
  8136. }
  8137. }
  8138. if ( team == TEAM_UNASSIGNED )
  8139. {
  8140. SendJoinTeamFailedMessage( TeamJoinFailedReason::BOTH_TEAMS_FULL, true );
  8141. return false;
  8142. }
  8143. }
  8144. }
  8145. // Do custom team validation when loading round backup data
  8146. bool bLoadingRoundBackupData = ( CSGameRules() && CSGameRules()->m_bLoadingRoundBackupData );
  8147. if ( !mp->WillTeamHaveRoomForPlayer( this, team ) )
  8148. {
  8149. // attempt to kick a bot to make room for this player
  8150. bool madeRoom = false;
  8151. if ( cv_bot_auto_vacate.GetBool() && !IsBot() && team != TEAM_SPECTATOR )
  8152. {
  8153. if ( UTIL_KickBotFromTeam( team, bQueue ) )
  8154. madeRoom = true;
  8155. }
  8156. if ( !madeRoom )
  8157. {
  8158. // in training, we don't want to print messages or show the team screen or anything
  8159. if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
  8160. return false;
  8161. if ( !bLoadingRoundBackupData )
  8162. { // Don't fail team joining when loading round backup data
  8163. if ( team == TEAM_TERRORIST )
  8164. {
  8165. SendJoinTeamFailedMessage( TeamJoinFailedReason::TERRORISTS_FULL, true );
  8166. }
  8167. else if ( team == TEAM_CT )
  8168. {
  8169. SendJoinTeamFailedMessage( TeamJoinFailedReason::CTS_FULL, true );
  8170. }
  8171. else if ( team == TEAM_SPECTATOR )
  8172. {
  8173. SendJoinTeamFailedMessage( TeamJoinFailedReason::CANT_JOIN_SPECTATOR, true );
  8174. }
  8175. return false;
  8176. }
  8177. }
  8178. }
  8179. // check if humans are restricted to a single team ( Tour of Duty, etc )
  8180. if ( !IsBot() && team != TEAM_SPECTATOR )
  8181. {
  8182. int humanTeam = mp->GetHumanTeam();
  8183. if ( humanTeam != TEAM_UNASSIGNED && humanTeam != team )
  8184. {
  8185. if ( humanTeam == TEAM_TERRORIST )
  8186. {
  8187. SendJoinTeamFailedMessage( TeamJoinFailedReason::HUMANS_CAN_ONLY_JOIN_TS, true );
  8188. }
  8189. else if ( humanTeam == TEAM_CT )
  8190. {
  8191. SendJoinTeamFailedMessage( TeamJoinFailedReason::HUMANS_CAN_ONLY_JOIN_CTS, true );
  8192. }
  8193. return false;
  8194. }
  8195. }
  8196. if ( team == TEAM_SPECTATOR )
  8197. {
  8198. // Prevent this if the cvar is set
  8199. if ( !bLoadingRoundBackupData )
  8200. {
  8201. if ( (!mp_allowspectators.GetInt() && !IsHLTV()) || (CSGameRules() && CSGameRules()->IsPlayingTraining()) )
  8202. {
  8203. SendJoinTeamFailedMessage( TeamJoinFailedReason::CANT_JOIN_SPECTATOR, false );
  8204. return false;
  8205. }
  8206. }
  8207. if ( GetTeamNumber() != TEAM_UNASSIGNED && State_Get() == STATE_ACTIVE )
  8208. {
  8209. m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work
  8210. CommitSuicide();
  8211. // add 1 to frags to balance out the 1 subtracted for killing yourself
  8212. IncrementFragCount( 1 );
  8213. }
  8214. ChangeTeam( TEAM_SPECTATOR );
  8215. m_iClass = (int )CS_CLASS_NONE;
  8216. // do we have fadetoblack on? (need to fade their screen back in )
  8217. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE && CSGameRules()->IsPlayingCooperativeGametype() )
  8218. {
  8219. color32_s clr = { 0,0,0,255 };
  8220. UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
  8221. }
  8222. m_iDeathPostEffect = 0;
  8223. if ( bQueue )
  8224. {
  8225. IGameEvent * event = gameeventmanager->CreateEvent( "teamchange_pending" );
  8226. if ( event )
  8227. {
  8228. event->SetInt( "userid", GetUserID() );
  8229. event->SetInt( "toteam", GetPendingTeamNumber() );
  8230. gameeventmanager->FireEvent( event );
  8231. }
  8232. }
  8233. return true;
  8234. }
  8235. // If the code gets this far, the team is not TEAM_UNASSIGNED
  8236. if (mp->TeamStacked( team, GetTeamNumber() ) )//players are allowed to change to their own team so they can just change their model
  8237. {
  8238. // attempt to kick a bot to make room for this player
  8239. bool madeRoom = false;
  8240. if (cv_bot_auto_vacate.GetBool() && !IsBot() )
  8241. {
  8242. if (UTIL_KickBotFromTeam( team, bQueue ) )
  8243. madeRoom = true;
  8244. }
  8245. if ( !madeRoom && !bLoadingRoundBackupData )
  8246. {
  8247. // The specified team is full
  8248. SendJoinTeamFailedMessage( ( team == TEAM_TERRORIST ) ? TeamJoinFailedReason::TOO_MANY_TS : TeamJoinFailedReason::TOO_MANY_CTS, true );
  8249. return false;
  8250. }
  8251. }
  8252. // Show the appropriate Choose Appearance menu
  8253. // This must come before ClientKill() for CheckWinConditions() to function properly
  8254. SetPendingTeamNum( team );
  8255. if ( bQueue )
  8256. {
  8257. IGameEvent * event = gameeventmanager->CreateEvent( "teamchange_pending" );
  8258. if ( event )
  8259. {
  8260. event->SetInt( "userid", GetUserID() );
  8261. event->SetInt( "toteam", GetPendingTeamNumber() );
  8262. gameeventmanager->FireEvent( event );
  8263. }
  8264. CommitSuicide();
  8265. }
  8266. else
  8267. {
  8268. // Switch their actual team...
  8269. ChangeTeam( team );
  8270. if ( IsAbleToInstantRespawn() &&
  8271. ( ( CSGameRules()->IsPlayingOffline() && CSGameRules()->GetCustomBotDifficulty() != CUSTOM_BOT_DIFFICULTY_NOBOTS ) || !CSGameRules()->IsPlayingOffline() ) )
  8272. {
  8273. // Handle the case when playing Arms Race and bots are in the game
  8274. if ( team == TEAM_CT || team == TEAM_TERRORIST || team == TEAM_SPECTATOR )
  8275. {
  8276. // Fill in the spot left by the player going to another team
  8277. if ( currTeam == TEAM_CT )
  8278. {
  8279. engine->ServerCommand( UTIL_VarArgs( "bot_add_ct\n" ) );
  8280. }
  8281. else if ( currTeam == TEAM_TERRORIST )
  8282. {
  8283. engine->ServerCommand( UTIL_VarArgs( "bot_add_t\n" ) );
  8284. }
  8285. }
  8286. }
  8287. }
  8288. // If a player joined at halftime he would have missed the requirement to switch teams at round reset,
  8289. // cause him to pick up that rule here:
  8290. if ( CSGameRules() && CSGameRules()->IsSwitchingTeamsAtRoundReset() && !WillSwitchTeamsAtRoundReset() &&
  8291. ( ( GetAssociatedTeamNumber() == TEAM_CT ) || ( GetAssociatedTeamNumber() == TEAM_TERRORIST ) ) )
  8292. SwitchTeamsAtRoundReset();
  8293. //
  8294. // Transfer all parameters that we know about
  8295. //
  8296. CSteamID const *pThisSteamId = engine->GetClientSteamID( this->edict() );
  8297. if ( pThisSteamId && pThisSteamId->IsValid() && pThisSteamId->GetAccountID() && !IsBot() && CSGameRules() )
  8298. {
  8299. SetHumanPlayerAccountID( pThisSteamId->GetAccountID() );
  8300. }
  8301. return true;
  8302. }
  8303. bool CCSPlayer::HandleCommand_JoinClass( void )
  8304. {
  8305. int iClass = CS_CLASS_NONE;
  8306. // Choose a random class based on the team.
  8307. switch ( GetTeamNumber() )
  8308. {
  8309. case TEAM_CT :
  8310. case TEAM_TERRORIST :
  8311. iClass = PlayerModelInfo::GetPtr()->GetNextClassForTeam( GetTeamNumber() );
  8312. break;
  8313. }
  8314. // Reset the player's state
  8315. if ( State_Get() == STATE_ACTIVE )
  8316. {
  8317. CSGameRules()->CheckWinConditions();
  8318. }
  8319. if ( !IsBot() && State_Get() == STATE_ACTIVE ) // Bots are responsible about only switching classes when they join.
  8320. {
  8321. // Kill player if switching classes while alive.
  8322. // This mimics goldsrc CS 1.6, and prevents a player from hiding, and switching classes to
  8323. // make the opposing team think there are more enemies than there really are.
  8324. CommitSuicide();
  8325. }
  8326. m_iClass = iClass;
  8327. //if ( State_Get() == STATE_PICKINGTEAM )
  8328. {
  8329. //State_Transition( STATE_ACTIVE );
  8330. GetIntoGame();
  8331. }
  8332. return true;
  8333. }
  8334. /*
  8335. void CheckStartMoney( void )
  8336. {
  8337. if ( mp_startmoney.GetInt() > 16000 )
  8338. {
  8339. mp_startmoney.SetInt( 16000 );
  8340. }
  8341. else if ( mp_startmoney.GetInt() < 800 )
  8342. {
  8343. mp_startmoney.SetInt( 800 );
  8344. }
  8345. }
  8346. */
  8347. void CCSPlayer::GetIntoGame()
  8348. {
  8349. // Set their model and if they're allowed to spawn right now, put them into the world.
  8350. //SetPlayerModel( iClass );
  8351. SetFOV( this, 0 );
  8352. m_flLastAction = gpGlobals->curtime;
  8353. CCSGameRules *MPRules = CSGameRules();
  8354. /* //MIKETODO: Escape gameplay ?
  8355. if ( ( MPRules->m_bMapHasEscapeZone == true ) && ( m_iTeam == TEAM_CT ) )
  8356. {
  8357. ResetAccount();
  8358. CheckStartMoney();
  8359. AddAccount( (int )startmoney.value, false );
  8360. }
  8361. */
  8362. // bool bSpawnNow = IsAbleToInstantRespawn();
  8363. // if ( CSGameRules() && !CSGameRules()->IsWarmupPeriod() && mp_use_respawn_waves.GetBool() && CSGameRules()->GetNextRespawnWave( GetTeamNumber(), NULL ) > gpGlobals->curtime )
  8364. // bSpawnNow = false;
  8365. //****************New Code by SupraFiend************
  8366. if ( !MPRules->FPlayerCanRespawn( this ) )
  8367. {
  8368. // This player is joining in the middle of a round or is an observer. Put them directly into observer mode.
  8369. //pev->deadflag = DEAD_RESPAWNABLE;
  8370. //pev->classname = MAKE_STRING("player" );
  8371. //pev->flags &= ( FL_PROXY | FL_FAKECLIENT ); // clear flags, but keep proxy and bot flags that might already be set
  8372. //pev->flags |= FL_CLIENT | FL_SPECTATOR;
  8373. //SetThink(PlayerDeathThink );
  8374. State_Transition( STATE_OBSERVER_MODE );
  8375. m_wasNotKilledNaturally = true;
  8376. MPRules->CheckWinConditions();
  8377. }
  8378. else
  8379. {
  8380. State_Transition( STATE_ACTIVE );
  8381. Spawn();
  8382. MPRules->CheckWinConditions();
  8383. // [menglish] Have the rules update anything related to a player spawning in late
  8384. MPRules->SpawningLatePlayer(this );
  8385. if( MPRules->GetRoundRestartTime() == 0.0f )
  8386. {
  8387. //Bomb target, no bomber and no bomb lying around.
  8388. if( !MPRules->IsPlayingCoopMission() && !MPRules->IsPlayingGunGameProgressive() &&
  8389. !MPRules->IsPlayingGunGameDeathmatch() && !MPRules->IsWarmupPeriod() &&
  8390. MPRules->IsBombDefuseMap() && !MPRules->IsThereABomber() && !MPRules->IsThereABomb() )
  8391. {
  8392. MPRules->GiveC4ToRandomPlayer(); //Checks for terrorists.
  8393. }
  8394. }
  8395. // If a new terrorist is entering the fray, then up the # of potential escapers.
  8396. if ( GetTeamNumber() == TEAM_TERRORIST )
  8397. MPRules->m_iNumEscapers++;
  8398. // [menglish] Reset Round Based Achievement Variables
  8399. ResetRoundBasedAchievementVariables();
  8400. // HACK: Joining from the observer team happens here instead of with the rest of the team... set round start money
  8401. m_unRoundStartEquipmentValue = RecalculateCurrentEquipmentValue();
  8402. // resetting takes away the players' weapons on first spawn....
  8403. //if ( MPRules && MPRules->IsWarmupPeriod() )
  8404. // Reset( false );
  8405. if ( !IsObserver() )
  8406. {
  8407. IGameEvent * event = gameeventmanager->CreateEvent( "player_spawned" );
  8408. // Check for a pending round restart in order to determine if the 'post team select' overlay needs to be shown
  8409. int nRestartTime = static_cast<int>( ceil( CSGameRules( )->GetRoundRestartTime() ) );
  8410. // If there are needed players then a round restart will be pending after they connect
  8411. bool bNeeded = false;
  8412. CSGameRules( )->NeededPlayersCheck( bNeeded );
  8413. // If there are no bots on this map then bypass the overlay
  8414. ConVarRef bot_quota( "bot_quota" );
  8415. if ( event )
  8416. {
  8417. event->SetInt( "userid", GetUserID() );
  8418. event->SetBool( "inrestart", ( !CSGameRules()->IsPlayingTraining() && ( bot_quota.GetInt() > 0 ) && ( nRestartTime > 0 || bNeeded ) ) );
  8419. gameeventmanager->FireEvent( event );
  8420. }
  8421. }
  8422. }
  8423. }
  8424. int CCSPlayer::PlayerClass() const
  8425. {
  8426. return m_iClass;
  8427. }
  8428. bool CCSPlayer::SelectSpawnSpot( const char *pEntClassName, CBaseEntity* &pStartSpot )
  8429. {
  8430. CBaseEntity* pSpot = pStartSpot;
  8431. const int kNumSpawnSpotsToScan = 32; // Scanning spawn points loops around, so it's ok to scan same points multiple times
  8432. if ( V_strcmp( pEntClassName, "info_player_counterterrorist" ) == 0 )
  8433. {
  8434. for ( int i = kNumSpawnSpotsToScan; i > 0; i-- )
  8435. {
  8436. pSpot = ( CSGameRules()->GetNextSpawnpoint( TEAM_CT ) );
  8437. if ( pSpot && g_pGameRules->IsSpawnPointValid( pSpot, this ) && pSpot->GetAbsOrigin() != Vector( 0, 0, 0 ) )
  8438. {
  8439. pStartSpot = pSpot;
  8440. return true;
  8441. }
  8442. }
  8443. }
  8444. else if ( V_strcmp( pEntClassName, "info_player_terrorist" ) == 0 )
  8445. {
  8446. for ( int i = kNumSpawnSpotsToScan; i > 0; i-- )
  8447. {
  8448. pSpot = ( CSGameRules()->GetNextSpawnpoint( TEAM_TERRORIST ) );
  8449. if ( pSpot && g_pGameRules->IsSpawnPointValid( pSpot, this ) && pSpot->GetAbsOrigin() != Vector( 0, 0, 0 ) )
  8450. {
  8451. pStartSpot = pSpot;
  8452. return true;
  8453. }
  8454. }
  8455. }
  8456. else if ( V_strcmp( pEntClassName, "info_enemy_terrorist_spawn" ) == 0 )
  8457. {
  8458. if ( mp_randomspawn_los.GetBool() == false )
  8459. {
  8460. for ( int i = kNumSpawnSpotsToScan; i > 0; i-- )
  8461. {
  8462. pSpot = ( CSGameRules()->GetNextSpawnpoint( TEAM_TERRORIST ) );
  8463. bool bIsValid = true;
  8464. if ( !pSpot || g_pGameRules->IsSpawnPointValid( pSpot, this ) == false || pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
  8465. bIsValid = false;
  8466. if ( bIsValid )
  8467. {
  8468. pStartSpot = pSpot;
  8469. return true;
  8470. }
  8471. else
  8472. {
  8473. Msg( "FAILED TO GET A VALID SPAWN SPOT\n" );
  8474. }
  8475. }
  8476. }
  8477. // if we made it down here, we just need to find any valid spot
  8478. CBaseEntity* pSpotValidButVisible = NULL;
  8479. while ( true )
  8480. {
  8481. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  8482. SpawnPoint* pSpawnpoint = assert_cast< SpawnPoint* >( pSpot );
  8483. bool bSpawnValid = g_pGameRules->IsSpawnPointValid( pSpawnpoint, this );
  8484. // check if pSpot is valid
  8485. if ( pSpawnpoint && pSpawnpoint->IsEnabled() && bSpawnValid && pSpawnpoint->GetAbsOrigin() != Vector( 0, 0, 0 ) )
  8486. {
  8487. if ( mp_randomspawn_los.GetBool() )
  8488. {
  8489. if ( CSGameRules() && CSGameRules()->IsSpawnPointHiddenFromOtherPlayers( pSpawnpoint, this, TEAM_CT )
  8490. && UTIL_IsRandomSpawnFarEnoughAwayFromTeam( pSpawnpoint->GetAbsOrigin(), TEAM_CT ) )
  8491. {
  8492. pStartSpot = pSpawnpoint;
  8493. return true;
  8494. }
  8495. else // the spawn point is either hidden, or we don't care
  8496. {
  8497. pSpotValidButVisible = pSpawnpoint;
  8498. }
  8499. }
  8500. else // the spawn point is either hidden, or we don't care
  8501. {
  8502. pSpotValidButVisible = pSpawnpoint;
  8503. }
  8504. }
  8505. // if we're back to the start of the list
  8506. if ( pSpawnpoint == pStartSpot )
  8507. {
  8508. // use the valid but unfortunately visible spot.
  8509. if ( pSpotValidButVisible != NULL )
  8510. {
  8511. pStartSpot = pSpotValidButVisible;
  8512. return true;
  8513. }
  8514. else
  8515. {
  8516. pStartSpot = ( CSGameRules()->GetNextSpawnpoint( TEAM_TERRORIST ) );
  8517. return true;
  8518. }
  8519. break;
  8520. }
  8521. }
  8522. // for ( int i = kNumSpawnSpotsToScan; i > 0; i-- )
  8523. // {
  8524. // pSpot = ( CSGameRules()->GetNextSpawnpoint( TEAM_TERRORIST ) );
  8525. //
  8526. // bool bIsValid = true;
  8527. // if ( !pSpot || g_pGameRules->IsSpawnPointValid( pSpot, this ) == false || pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
  8528. // bIsValid = false;
  8529. //
  8530. // if ( CSGameRules()->IsPlayingCoopTilegen() && UTIL_IsVisibleToTeam( pSpot->GetAbsOrigin(), TEAM_CT ) == true )
  8531. // bIsValid = false;
  8532. //
  8533. // if ( CSGameRules()->IsPlayingCoopMission() )
  8534. // {
  8535. // if ( mp_randomspawn_los.GetBool() && GetTeamNumber() == TEAM_TERRORIST &&
  8536. // CSGameRules()->IsSpawnPointHiddenFromOtherPlayers( pSpot, this, TEAM_CT ) == false )
  8537. // {
  8538. // bIsValid = false;
  8539. // }
  8540. // }
  8541. //
  8542. // if ( bIsValid )
  8543. // {
  8544. // pStartSpot = pSpot;
  8545. // return true;
  8546. // }
  8547. // else
  8548. // {
  8549. // Msg( "FAILED TO GET A VALID SPAWN SPOT\n" );
  8550. // }
  8551. // }
  8552. }
  8553. else
  8554. {
  8555. pSpot = pStartSpot;
  8556. CBaseEntity* pSpotValidButVisible = NULL;
  8557. while ( true )
  8558. {
  8559. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  8560. SpawnPoint* pSpawnpoint = assert_cast< SpawnPoint* >( pSpot );
  8561. bool bSpawnValid = g_pGameRules->IsSpawnPointValid( pSpawnpoint, this );
  8562. // check if pSpot is valid
  8563. if ( pSpawnpoint && pSpawnpoint->IsEnabled() && bSpawnValid && pSpawnpoint->GetAbsOrigin() != Vector( 0, 0, 0 ) )
  8564. {
  8565. if ( mp_randomspawn_los.GetBool() )
  8566. {
  8567. if ( CSGameRules() && CSGameRules()->IsSpawnPointHiddenFromOtherPlayers( pSpawnpoint, this )
  8568. && UTIL_IsRandomSpawnFarEnoughAwayFromTeam( pSpawnpoint->GetAbsOrigin(), TEAM_CT ) )
  8569. {
  8570. pStartSpot = pSpawnpoint;
  8571. return true;
  8572. }
  8573. else // the spawn point is either hidden, or we don't care
  8574. {
  8575. pSpotValidButVisible = pSpawnpoint;
  8576. }
  8577. }
  8578. else // the spawn point is either hidden, or we don't care
  8579. {
  8580. pSpotValidButVisible = pSpawnpoint;
  8581. }
  8582. }
  8583. // if we're back to the start of the list
  8584. if ( pSpawnpoint == pStartSpot )
  8585. {
  8586. // use the valid but unfortunately visible spot.
  8587. if ( pSpotValidButVisible != NULL )
  8588. {
  8589. pStartSpot = pSpotValidButVisible;
  8590. return true;
  8591. }
  8592. else
  8593. {
  8594. if ( GetTeamNumber() == TEAM_CT )
  8595. SelectSpawnSpot( "info_player_counterterrorist", pStartSpot );
  8596. else if ( GetTeamNumber() == TEAM_TERRORIST )
  8597. SelectSpawnSpot( "info_player_terrorist", pStartSpot );
  8598. return true;
  8599. }
  8600. break;
  8601. }
  8602. }
  8603. DevMsg( "CCSPlayer::SelectSpawnSpot: couldn't find valid spawn point.\n" );
  8604. }
  8605. return true;
  8606. }
  8607. //-----------------------------------------------------------------------------
  8608. // Purpose: Called directly after we select a spawn point and teleport to it
  8609. //-----------------------------------------------------------------------------
  8610. void CCSPlayer::PostSpawnPointSelection( )
  8611. {
  8612. if(m_storedSpawnAngle.LengthSqr( ) > 0.0f || m_storedSpawnPosition != vec3_origin )
  8613. {
  8614. Teleport( &m_storedSpawnPosition, &m_storedSpawnAngle, &vec3_origin );
  8615. m_storedSpawnPosition = vec3_origin;
  8616. m_storedSpawnAngle.Init( );
  8617. }
  8618. }
  8619. CBaseEntity* CCSPlayer::EntSelectSpawnPoint()
  8620. {
  8621. CBaseEntity *pSpot;
  8622. pSpot = NULL;
  8623. if ( CSGameRules()->IsLogoMap() )
  8624. {
  8625. // This is a logo map. Don't allow movement or logos or menus.
  8626. SelectSpawnSpot( "info_player_logo", pSpot );
  8627. LockPlayerInPlace();
  8628. goto ReturnSpot;
  8629. }
  8630. else
  8631. {
  8632. // The map maker can force use of map-placed spawns in deathmatch using an info_map_parameters entity.
  8633. bool bUseNormalSpawnsForDM = ( g_pMapInfo ? g_pMapInfo->m_bUseNormalSpawnsForDM : false );
  8634. if ( !bUseNormalSpawnsForDM &&
  8635. ( mp_randomspawn.GetInt() == GetTeamNumber() ||
  8636. mp_randomspawn.GetInt() == 1 ) )
  8637. {
  8638. pSpot = g_pLastCTSpawn; // reusing g_pLastCTSpawn.
  8639. // Randomize the start spot
  8640. for ( int i = random->RandomInt(1,10); i > 0; i-- )
  8641. {
  8642. pSpot = gEntList.FindEntityByClassname( pSpot, "info_deathmatch_spawn" );
  8643. }
  8644. if ( !pSpot ) // skip over the null point
  8645. pSpot = gEntList.FindEntityByClassname( pSpot, "info_deathmatch_spawn" );
  8646. if ( SelectSpawnSpot( "info_deathmatch_spawn", pSpot ))
  8647. {
  8648. g_pLastCTSpawn = pSpot;
  8649. goto ReturnSpot;
  8650. }
  8651. }
  8652. else if ( GetTeamNumber() == TEAM_CT )
  8653. {
  8654. pSpot = g_pLastCTSpawn;
  8655. // if ( CSGameRules()->IsPlayingGunGameProgressive() )
  8656. // {
  8657. // if ( SelectSpawnSpot( "info_armsrace_counterterrorist", pSpot ) )
  8658. // {
  8659. // g_pLastCTSpawn = pSpot;
  8660. // goto ReturnSpot;
  8661. // }
  8662. // }
  8663. if ( SelectSpawnSpot( "info_player_counterterrorist", pSpot ) )
  8664. {
  8665. g_pLastCTSpawn = pSpot;
  8666. goto ReturnSpot;
  8667. }
  8668. }
  8669. /*********************************************************/
  8670. // The terrorist spawn points
  8671. else if ( GetTeamNumber() == TEAM_TERRORIST )
  8672. {
  8673. pSpot = g_pLastTerroristSpawn;
  8674. // if ( CSGameRules()->IsPlayingGunGameProgressive() )
  8675. // {
  8676. // if ( SelectSpawnSpot( "info_armsrace_terrorist", pSpot ) )
  8677. // {
  8678. // g_pLastCTSpawn = pSpot;
  8679. // goto ReturnSpot;
  8680. // }
  8681. // }
  8682. const char* szTSpawnEntName = "info_player_terrorist";
  8683. if ( CSGameRules()->IsPlayingCoopMission() )
  8684. szTSpawnEntName = "info_enemy_terrorist_spawn";
  8685. if ( SelectSpawnSpot( szTSpawnEntName, pSpot ) )
  8686. {
  8687. g_pLastTerroristSpawn = pSpot;
  8688. goto ReturnSpot;
  8689. }
  8690. }
  8691. }
  8692. // If forced startspot is set, (re )spawn there.
  8693. // never attempt to use any random spots because we don't want CTs spawning in Ts buyzone
  8694. if ( !!gpGlobals->startspot && V_strlen( STRING(gpGlobals->startspot ) ) )
  8695. {
  8696. pSpot = gEntList.FindEntityByTarget( NULL, STRING(gpGlobals->startspot ) );
  8697. if ( pSpot )
  8698. goto ReturnSpot;
  8699. }
  8700. ReturnSpot:
  8701. if ( !pSpot )
  8702. {
  8703. if( CSGameRules()->IsLogoMap() )
  8704. Warning( "PutClientInServer: no info_player_logo on level\n" );
  8705. else
  8706. Warning( "PutClientInServer: no info_player_start on level\n" );
  8707. return CBaseEntity::Instance( INDEXENT(0 ) );
  8708. }
  8709. return pSpot;
  8710. }
  8711. void CCSPlayer::SetProgressBarTime( int barTime )
  8712. {
  8713. m_iProgressBarDuration = barTime;
  8714. m_flProgressBarStartTime = this->m_flSimulationTime;
  8715. }
  8716. void CCSPlayer::PlayerDeathThink()
  8717. {
  8718. }
  8719. void CCSPlayer::ResetForceTeamThink()
  8720. {
  8721. m_fForceTeam = -1.0f;
  8722. SetContextThink( &CBasePlayer::PlayerForceTeamThink, TICK_NEVER_THINK, CS_FORCE_TEAM_THINK_CONTEXT );
  8723. }
  8724. void CCSPlayer::PlayerForceTeamThink()
  8725. {
  8726. if ( GetTeamNumber() != TEAM_UNASSIGNED )
  8727. {
  8728. // player joined a team between last think and now
  8729. ResetForceTeamThink();
  8730. return;
  8731. }
  8732. SetContextThink( &CBasePlayer::PlayerForceTeamThink, gpGlobals->curtime + 0.5f, CS_FORCE_TEAM_THINK_CONTEXT );
  8733. if ( m_fForceTeam == -1.0f )
  8734. {
  8735. ConVarRef cvTime( "mp_force_pick_time" );
  8736. m_fForceTeam = gpGlobals->curtime + cvTime.GetFloat();
  8737. }
  8738. if ( gpGlobals->curtime > m_fForceTeam )
  8739. {
  8740. // selection time expired. try to auto team.
  8741. if ( !HandleCommand_JoinTeam( 0, false ) )
  8742. {
  8743. // failed to join CT, T, or spectator. Force disconnect.
  8744. engine->ClientCommand( edict(), "disconnect\n" );
  8745. }
  8746. }
  8747. }
  8748. void CCSPlayer::State_Transition( CSPlayerState newState )
  8749. {
  8750. State_Leave();
  8751. State_Enter( newState );
  8752. }
  8753. void CCSPlayer::State_Enter( CSPlayerState newState )
  8754. {
  8755. m_iPlayerState = newState;
  8756. m_pCurStateInfo = State_LookupInfo( newState );
  8757. if ( cs_ShowStateTransitions.GetInt() == -1 || cs_ShowStateTransitions.GetInt() == entindex() )
  8758. {
  8759. if ( m_pCurStateInfo )
  8760. Msg( "ShowStateTransitions: entering '%s'\n", m_pCurStateInfo->m_pStateName );
  8761. else
  8762. Msg( "ShowStateTransitions: entering #%d\n", newState );
  8763. }
  8764. // Initialize the new state.
  8765. if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
  8766. (this->*m_pCurStateInfo->pfnEnterState )();
  8767. }
  8768. void CCSPlayer::State_Leave()
  8769. {
  8770. if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
  8771. {
  8772. (this->*m_pCurStateInfo->pfnLeaveState )();
  8773. }
  8774. }
  8775. void CCSPlayer::State_PreThink()
  8776. {
  8777. if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink )
  8778. {
  8779. (this->*m_pCurStateInfo->pfnPreThink )();
  8780. }
  8781. }
  8782. CCSPlayerStateInfo* CCSPlayer::State_LookupInfo( CSPlayerState state )
  8783. {
  8784. // This table MUST match the
  8785. static CCSPlayerStateInfo playerStateInfos[] =
  8786. {
  8787. { STATE_ACTIVE, "STATE_ACTIVE", &CCSPlayer::State_Enter_ACTIVE, NULL, &CCSPlayer::State_PreThink_ACTIVE },
  8788. { STATE_WELCOME, "STATE_WELCOME", &CCSPlayer::State_Enter_WELCOME, NULL, &CCSPlayer::State_PreThink_WELCOME },
  8789. { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CCSPlayer::State_Enter_PICKINGTEAM, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE },
  8790. { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CCSPlayer::State_Enter_PICKINGCLASS, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE },
  8791. { STATE_DEATH_ANIM, "STATE_DEATH_ANIM", &CCSPlayer::State_Enter_DEATH_ANIM, NULL, &CCSPlayer::State_PreThink_DEATH_ANIM },
  8792. { 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 },
  8793. { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CCSPlayer::State_Enter_OBSERVER_MODE, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE },
  8794. { STATE_GUNGAME_RESPAWN, "STATE_GUNGAME_RESPAWN", &CCSPlayer::State_Enter_GUNGAME_RESPAWN, NULL, &CCSPlayer::State_PreThink_GUNGAME_RESPAWN },
  8795. { STATE_DORMANT, "STATE_DORMANT", NULL, NULL, NULL },
  8796. };
  8797. for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
  8798. {
  8799. if ( playerStateInfos[i].m_iPlayerState == state )
  8800. return &playerStateInfos[i];
  8801. }
  8802. return NULL;
  8803. }
  8804. void CCSPlayer::PhysObjectSleep()
  8805. {
  8806. IPhysicsObject *pObj = VPhysicsGetObject();
  8807. if ( pObj )
  8808. pObj->Sleep();
  8809. }
  8810. void CCSPlayer::PhysObjectWake()
  8811. {
  8812. IPhysicsObject *pObj = VPhysicsGetObject();
  8813. if ( pObj )
  8814. pObj->Wake();
  8815. }
  8816. void CCSPlayer::State_Enter_WELCOME()
  8817. {
  8818. StartObserverMode( OBS_MODE_ROAMING );
  8819. // Important to set MOVETYPE_NONE or our physics object will fall while we're sitting at one of the intro cameras.
  8820. SetMoveType( MOVETYPE_NONE );
  8821. AddSolidFlags( FSOLID_NOT_SOLID );
  8822. PhysObjectSleep();
  8823. const ConVar *hostname = cvar->FindVar( "hostname" );
  8824. const char *title = (hostname ) ? hostname->GetString() : "MESSAGE OF THE DAY";
  8825. // this is where we send the string table data down to the client
  8826. // NOTE, we dont' show the panel here, we just send the data, the naming is bad....
  8827. // Show info panel (if it's not a simple demo map ).
  8828. if ( !CSGameRules()->IsLogoMap() )
  8829. {
  8830. const bool enableMOTD = true;
  8831. if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the MOTD when making reslists
  8832. {
  8833. engine->ClientCommand( edict(), "jointeam 3\n" );
  8834. }
  8835. else if ( enableMOTD )
  8836. {
  8837. KeyValues *data = new KeyValues("data" );
  8838. data->SetString( "title", title ); // info panel title
  8839. data->SetString( "type", "1" ); // show userdata from stringtable entry
  8840. data->SetString( "msg", "motd" ); // use this stringtable entry
  8841. // [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration
  8842. // of options. Passing a command string is dangerous and allowed a server network
  8843. // message to run arbitrary commands on the client.
  8844. data->SetInt( "cmd", TEXTWINDOW_CMD_JOINGAME ); // exec this command if panel closed
  8845. ShowViewPortPanel( PANEL_INFO, true, data );
  8846. data->deleteThis();
  8847. }
  8848. }
  8849. }
  8850. void CCSPlayer::State_PreThink_WELCOME()
  8851. {
  8852. // Verify some state.
  8853. Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  8854. #ifdef DBGFLAG_ASSERT
  8855. if ( GetAbsVelocity().Length() != 0 && !engine->IsDedicatedServer() )
  8856. {
  8857. // This happens e.g. when player moves during warmup: the player is still in WELCOME state, but CPlayerMove::FinishMove() will happily set its velocity.
  8858. ExecuteOnce( Warning( "Player velocity != 0 in State_PreThink_WELCOME.\n" ) );
  8859. }
  8860. #endif
  8861. // Update whatever intro camera it's at.
  8862. if( m_pIntroCamera && (gpGlobals->curtime >= m_fIntroCamTime ) )
  8863. {
  8864. MoveToNextIntroCamera();
  8865. }
  8866. }
  8867. void CCSPlayer::State_Enter_PICKINGTEAM()
  8868. {
  8869. ShowViewPortPanel( "team" ); // show the team menu
  8870. }
  8871. void CCSPlayer::State_Enter_DEATH_ANIM()
  8872. {
  8873. if ( HasWeapons() )
  8874. {
  8875. // we drop the guns here because weapons that have an area effect and can kill their user
  8876. // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
  8877. // player class sometimes is freed. It's safer to manipulate the weapons once we know
  8878. // we aren't calling into any of their code anymore through the player pointer.
  8879. PackDeadPlayerItems();
  8880. }
  8881. // Used for a timer.
  8882. m_flDeathTime = gpGlobals->curtime;
  8883. m_bAbortFreezeCam = false;
  8884. StartObserverMode( OBS_MODE_DEATHCAM ); // go to observer mode
  8885. RemoveEffects( EF_NODRAW ); // still draw player body
  8886. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
  8887. {
  8888. color32_s clr = {0,0,0,255};
  8889. UTIL_ScreenFade( this, clr, 0.3f, 3, FFADE_OUT | FFADE_STAYOUT );
  8890. //Don't perform any freezecam stuff if we are fading to black
  8891. State_Transition( STATE_DEATH_WAIT_FOR_KEY );
  8892. }
  8893. if ( m_bKilledByHeadshot )
  8894. {
  8895. m_iDeathPostEffect = 15; // POST_EFFECT_DEATH_CAM_HEADSHOT;
  8896. }
  8897. else
  8898. {
  8899. m_iDeathPostEffect = 14; // POST_EFFECT_DEATH_CAM_BODYSHOT;
  8900. }
  8901. }
  8902. void CCSPlayer::State_PreThink_DEATH_ANIM()
  8903. {
  8904. if ( IsAlive() )
  8905. return;
  8906. // If the anim is done playing, go to the next state (waiting for a keypress to
  8907. // either respawn the guy or put him into observer mode ).
  8908. if ( GetFlags() & FL_ONGROUND )
  8909. {
  8910. float flForward = GetAbsVelocity().Length() - 20;
  8911. if (flForward <= 0 )
  8912. {
  8913. SetAbsVelocity( vec3_origin );
  8914. }
  8915. else
  8916. {
  8917. Vector vAbsVel = GetAbsVelocity();
  8918. VectorNormalize( vAbsVel );
  8919. vAbsVel *= flForward;
  8920. SetAbsVelocity( vAbsVel );
  8921. }
  8922. }
  8923. float flDeathDelayDefault = spec_freeze_deathanim_time.GetFloat();
  8924. //float flDeathDelayDefault = CS_DEATH_ANIMATION_TIME;
  8925. CBaseEntity* pKiller = GetObserverTarget();
  8926. // there is a bug here where if you are spectating another player and they die, you won't see the death anim because the deathanim delay is so show
  8927. // we need to find a way to differentiate from the local player and a player that you are spectating. this doesn't work below
  8928. //if ( GetObserverMode() == OBS_MODE_DEATHCAM && pKiller && pKiller != this )
  8929. // flDeathDelayDefault = CS_DEATH_ANIMATION_TIME;
  8930. float fDeathEnd = m_flDeathTime + flDeathDelayDefault;
  8931. float fFreezeEnd = fDeathEnd + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();
  8932. float fFreezeLock = fDeathEnd + spec_freeze_time_lock.GetFloat();
  8933. // if we use repsawn waves, its time to respawn and we are ABLE to respawn, then do so
  8934. // otherwise, just check to see if we are able to respawn
  8935. bool bShouldRespawnNow = IsAbleToInstantRespawn();
  8936. if ( CSGameRules() && !CSGameRules()->IsWarmupPeriod() && mp_use_respawn_waves.GetBool() && CSGameRules()->GetNextRespawnWave( GetTeamNumber(), NULL ) > gpGlobals->curtime )
  8937. bShouldRespawnNow = false;
  8938. // transition to Freezecam mode once the death animation is complete
  8939. if ( gpGlobals->curtime >= fDeathEnd )
  8940. {
  8941. if ( !m_bAbortFreezeCam && gpGlobals->curtime < fFreezeEnd && ( GetObserverMode() != OBS_MODE_FREEZECAM ) )
  8942. {
  8943. CPlantedC4* pPlantedC4 = pKiller ? dynamic_cast< CPlantedC4* >( pKiller ) : NULL;
  8944. if ( pPlantedC4 == NULL )
  8945. {
  8946. // before we can replay, we need to freezecam for a little while (1-2 seconds) to let the player see the killer and output the stats
  8947. StartObserverMode( OBS_MODE_FREEZECAM );
  8948. }
  8949. }
  8950. else if(GetObserverMode() == OBS_MODE_FREEZECAM )
  8951. {
  8952. if ( m_bAbortFreezeCam && ( mp_forcecamera.GetInt() != OBS_ALLOW_NONE || CSGameRules()->IsWarmupPeriod() ) )
  8953. {
  8954. if ( bShouldRespawnNow )
  8955. {
  8956. // Respawn in gun game progressive
  8957. State_Transition( STATE_GUNGAME_RESPAWN );
  8958. }
  8959. else
  8960. {
  8961. State_Transition( STATE_OBSERVER_MODE );
  8962. }
  8963. }
  8964. }
  8965. }
  8966. // Don't transfer to observer state until the freeze cam is done
  8967. // Players in competitive mode may bypass this mode with a key press
  8968. if ( ( gpGlobals->curtime > fFreezeEnd ) ||
  8969. ( gpGlobals->curtime > fFreezeLock && ( m_nButtons & ~IN_SCORE ) && mp_deathcam_skippable.GetBool() ) ||
  8970. m_bIsRespawningForDMBonus )
  8971. {
  8972. if ( bShouldRespawnNow )
  8973. {
  8974. // Transition to respawn in gun game progressive
  8975. State_Transition( STATE_GUNGAME_RESPAWN );
  8976. }
  8977. else
  8978. {
  8979. State_Transition( STATE_OBSERVER_MODE );
  8980. }
  8981. }
  8982. }
  8983. bool CCSPlayer::StartHltvReplayEvent( const ClientReplayEventParams_t &params )
  8984. {
  8985. float flEventTime = params.m_flEventTime;
  8986. int nPrimaryTargetEntIndex = params.m_nPrimaryTargetEntIndex;
  8987. if ( params.m_nEventType == REPLAY_EVENT_DEATH )
  8988. {
  8989. flEventTime = GetDeathTime();
  8990. if ( flEventTime < gpGlobals->curtime - 10.0f )
  8991. return false;
  8992. if ( IsAlive() )
  8993. {
  8994. DevMsg( "Player is active, replaying last kill will interfere with the gameplay" );
  8995. return false;
  8996. }
  8997. nPrimaryTargetEntIndex = params.m_nPrimaryTargetEntIndex > 0 ? params.m_nPrimaryTargetEntIndex : m_nLastKillerIndex;
  8998. }
  8999. float flPostEventAnimTime = spec_replay_winddown_time.GetFloat();
  9000. static ConVarRef spec_replay_leadup_time( "spec_replay_leadup_time" );
  9001. float flPreEventReplayLength = spec_replay_leadup_time.GetFloat();
  9002. float flReplayStartTime = flEventTime - flPreEventReplayLength;
  9003. float flReplayEndTime = flEventTime + flPostEventAnimTime;
  9004. float flCurTime = gpGlobals->curtime;
  9005. float flDelay = flCurTime - flReplayStartTime;
  9006. if ( flDelay < 1.0f )
  9007. {
  9008. DevMsg( "Cannot replay with a delay of %.2f sec\n", flDelay );
  9009. return false;
  9010. }
  9011. HltvReplayParams_t hltvReplay;
  9012. hltvReplay.m_nPrimaryTargetEntIndex = nPrimaryTargetEntIndex;
  9013. hltvReplay.m_flDelay = flDelay;
  9014. hltvReplay.m_flStopAt = flReplayEndTime - flCurTime;
  9015. int nPlayerEntIndex = this->entindex();
  9016. if ( hltvReplay.m_nPrimaryTargetEntIndex > 0 // there is someone to observe in replay
  9017. && hltvReplay.m_flStopAt > 1.0f - flDelay // the replay should last a bit
  9018. )
  9019. {
  9020. m_bAbortFreezeCam = true;
  9021. if ( params.m_flSlowdownRate > 0.125f && params.m_flSlowdownRate < 8.0f && params.m_flSlowdownLength > 1.0f / 1024.0f )
  9022. {
  9023. hltvReplay.m_flSlowdownRate = params.m_flSlowdownRate;
  9024. hltvReplay.m_flSlowdownBeginAt = flEventTime - flCurTime - params.m_flSlowdownLength;
  9025. hltvReplay.m_flSlowdownEndAt = flEventTime - flCurTime;
  9026. }
  9027. // it only makes sense to replay if we have at least 1+ seconds of footage
  9028. return engine->StartClientHltvReplay( nPlayerEntIndex - 1, hltvReplay );
  9029. }
  9030. else
  9031. {
  9032. DevMsg( "Cannot replay: last killer %d, player %d, stop at %g, delay %g\n", ( int )nPrimaryTargetEntIndex, nPlayerEntIndex, hltvReplay.m_flStopAt, hltvReplay.m_flDelay );
  9033. return false;
  9034. }
  9035. }
  9036. void CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY()
  9037. {
  9038. // Remember when we died, so we can automatically put them into observer mode
  9039. // if they don't hit a key soon enough.
  9040. m_lifeState = LIFE_DEAD;
  9041. StopAnimation();
  9042. // Don't do this. The ragdoll system expects to be able to read from this player on
  9043. // the next update and will read it at the new origin if this is set.
  9044. // Since it is more complicated to redesign the ragdoll system to not need that data
  9045. // it is easier to cause a less obvious bug than popping ragdolls
  9046. //AddEffects( EF_NOINTERP );
  9047. }
  9048. void CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY()
  9049. {
  9050. // 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
  9051. // 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
  9052. if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND ) )
  9053. SetMoveType( MOVETYPE_NONE );
  9054. // if the player has been dead for one second longer than allowed by forcerespawn,
  9055. // forcerespawn isn't on. Send the player off to an intermission camera until they
  9056. // choose to respawn.
  9057. bool fAnyButtonDown = (m_nButtons & ~IN_SCORE ) != 0;
  9058. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
  9059. fAnyButtonDown = false;
  9060. // after a certain amount of time switch to observer mode even if they don't press a key.
  9061. else if (gpGlobals->curtime >= (m_flDeathTime + DEATH_ANIMATION_TIME + 3.0 ) )
  9062. {
  9063. fAnyButtonDown = true;
  9064. }
  9065. if ( fAnyButtonDown )
  9066. {
  9067. // if we use repsawn waves, its time to respawn and we are ABLE to respawn, then do so
  9068. // otherwise, just check to see if we are able to respawn
  9069. bool bShouldRespawnNow = IsAbleToInstantRespawn();
  9070. if ( mp_use_respawn_waves.GetBool() && CSGameRules() && CSGameRules()->GetNextRespawnWave( GetTeamNumber(), NULL ) > gpGlobals->curtime )
  9071. bShouldRespawnNow = false;
  9072. if ( bShouldRespawnNow )
  9073. {
  9074. // Early out transition to respawn when playing death animation
  9075. State_Transition( STATE_GUNGAME_RESPAWN );
  9076. }
  9077. else
  9078. {
  9079. State_Transition( STATE_OBSERVER_MODE );
  9080. }
  9081. }
  9082. }
  9083. void CCSPlayer::State_Enter_OBSERVER_MODE()
  9084. {
  9085. // do we have fadetoblack on? (need to fade their screen back in )
  9086. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
  9087. {
  9088. color32_s clr = { 0,0,0,255 };
  9089. UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
  9090. }
  9091. m_iDeathPostEffect = 0;
  9092. int observerMode = m_iObserverLastMode;
  9093. if ( IsNetClient() )
  9094. {
  9095. const char *pIdealMode = engine->GetClientConVarValue( entindex(), "cl_spec_mode" );
  9096. if ( pIdealMode )
  9097. {
  9098. int nIdealMode = atoi( pIdealMode );
  9099. if ( nIdealMode < OBS_MODE_IN_EYE )
  9100. {
  9101. nIdealMode = OBS_MODE_IN_EYE;
  9102. }
  9103. else if ( nIdealMode > OBS_MODE_ROAMING )
  9104. {
  9105. nIdealMode = OBS_MODE_ROAMING;
  9106. }
  9107. observerMode = nIdealMode;
  9108. }
  9109. }
  9110. StartObserverMode( observerMode );
  9111. PhysObjectSleep();
  9112. }
  9113. void CCSPlayer::State_Leave_OBSERVER_MODE()
  9114. {
  9115. #if CS_CONTROLLABLE_BOTS_ENABLED
  9116. m_bCanControlObservedBot = false;
  9117. #endif
  9118. }
  9119. void CCSPlayer::State_PreThink_OBSERVER_MODE()
  9120. {
  9121. // // check here first to see if we are on a team, our class isn't 0 ( that means we just spawn for the first time int he game) and our respawn wave lets us respawn
  9122. // // if we use repsawn waves, its time to respawn and we are ABLE to respawn, then do so
  9123. // // otherwise, just check to see if we are able to respawn
  9124. // bool bShouldRespawnNow = ( PlayerClass() != 0 && GetTeamNumber() > TEAM_SPECTATOR && mp_use_respawn_waves.GetBool() && CSGameRules() && CSGameRules()->GetNextRespawnWave( GetTeamNumber(), NULL ) <= gpGlobals->curtime ) && IsAbleToInstantRespawn();
  9125. // if ( bShouldRespawnNow )
  9126. // {
  9127. // State_Transition( STATE_GUNGAME_RESPAWN );
  9128. // return;
  9129. // }
  9130. // Make sure nobody has changed any of our state.
  9131. // Assert( GetMoveType() == MOVETYPE_FLY );
  9132. Assert( m_takedamage == DAMAGE_NO );
  9133. Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  9134. // Assert( IsEffectActive( EF_NODRAW ) );
  9135. // Must be dead.
  9136. Assert( m_lifeState == LIFE_DEAD );
  9137. Assert( pl.deadflag );
  9138. #if CS_CONTROLLABLE_BOTS_ENABLED
  9139. m_bCanControlObservedBot = false;
  9140. if ( GetObserverMode() >= OBS_MODE_IN_EYE )
  9141. {
  9142. CCSBot * pBot = ToCSBot( GetObserverTarget() );
  9143. if ( CanControlBot(pBot ) )
  9144. {
  9145. m_bCanControlObservedBot = true;
  9146. }
  9147. }
  9148. #endif
  9149. }
  9150. void CCSPlayer::State_Enter_GUNGAME_RESPAWN()
  9151. {
  9152. TryGungameRespawn();
  9153. }
  9154. void CCSPlayer::TryGungameRespawn()
  9155. {
  9156. int nPlayerEntIndex = this->entindex();
  9157. if ( engine->GetClientHltvReplayDelay( nPlayerEntIndex - 1 ) == 0 )
  9158. {
  9159. // no delay, we can actually respawn
  9160. if ( !m_bRespawning )
  9161. {
  9162. // Perform the respawn of the player in gun game progressive
  9163. m_bRespawning = true;
  9164. State_Transition( STATE_ACTIVE );
  9165. respawn( this, false );
  9166. m_nButtons = 0;
  9167. SetNextThink( TICK_NEVER_THINK );
  9168. int nTeamCheck = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  9169. //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  9170. if ( (CSGameRules()->IsPlayingCoopGuardian() && GetTeamNumber() == nTeamCheck) ||
  9171. CSGameRules()->IsPlayingCoopMission() && GetTeamNumber() == TEAM_CT)
  9172. {
  9173. GiveHealthAndArmorForGuardianMode( false );
  9174. }
  9175. }
  9176. }
  9177. }
  9178. void CCSPlayer::State_PreThink_GUNGAME_RESPAWN()
  9179. {
  9180. TryGungameRespawn();
  9181. }
  9182. void CCSPlayer::State_Enter_PICKINGCLASS()
  9183. {
  9184. if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the menu when making reslists
  9185. {
  9186. engine->ClientCommand( edict(), "joinclass\n" );
  9187. return;
  9188. }
  9189. // go to spec mode, if dying keep deathcam
  9190. if ( GetObserverMode() == OBS_MODE_DEATHCAM )
  9191. {
  9192. StartObserverMode( OBS_MODE_DEATHCAM );
  9193. }
  9194. m_iClass = (int )CS_CLASS_NONE;
  9195. PhysObjectSleep();
  9196. // show the class menu:
  9197. if ( ( GetTeamNumber() == TEAM_TERRORIST )
  9198. || ( GetTeamNumber() == TEAM_CT ) )
  9199. {
  9200. engine->ClientCommand( edict(), "joinclass\n" );
  9201. }
  9202. else
  9203. {
  9204. HandleCommand_JoinClass();
  9205. }
  9206. }
  9207. void CCSPlayer::State_Enter_ACTIVE()
  9208. {
  9209. SetMoveType( MOVETYPE_WALK );
  9210. RemoveSolidFlags( FSOLID_NOT_SOLID );
  9211. m_Local.m_iHideHUD = 0;
  9212. PhysObjectWake();
  9213. SetPendingTeamNum( GetTeamNumber() );
  9214. m_bRespawning = false;
  9215. }
  9216. void CCSPlayer::State_PreThink_ACTIVE()
  9217. {
  9218. // Calculate timeout for gun game immunity
  9219. ConVarRef mp_respawn_immunitytime( "mp_respawn_immunitytime" );
  9220. float flImmuneTime = mp_respawn_immunitytime.GetFloat();
  9221. if ( flImmuneTime > 0 || ( CSGameRules() && CSGameRules()->IsWarmupPeriod() ) )
  9222. {
  9223. if ( m_bGunGameImmunity )
  9224. {
  9225. if ( gpGlobals->curtime > m_fImmuneToGunGameDamageTime )
  9226. {
  9227. // Player immunity has timed out
  9228. ClearGunGameImmunity();
  9229. m_fJustLeftImmunityTime = gpGlobals->curtime + 2.0f;
  9230. }
  9231. // or if we've moved and there's more than 1s of immunity left. Check for 1s because the above case adds 1s.
  9232. else if ( IsAbleToInstantRespawn() && m_bHasMovedSinceSpawn && ( m_fImmuneToGunGameDamageTime - gpGlobals->curtime > 1.0f ) )
  9233. {
  9234. m_fImmuneToGunGameDamageTime = gpGlobals->curtime + 1.0f;
  9235. }
  9236. }
  9237. // track that we just left immunity
  9238. if( m_fJustLeftImmunityTime != 0.0f && gpGlobals->curtime > m_fJustLeftImmunityTime )
  9239. {
  9240. m_fJustLeftImmunityTime = 0.0f;
  9241. }
  9242. }
  9243. // track low health achievement
  9244. if( m_lowHealthGoalTime != 0.0f && gpGlobals->curtime > m_lowHealthGoalTime )
  9245. {
  9246. m_lowHealthGoalTime = 0.0f;
  9247. AwardAchievement(CSStillAlive );
  9248. }
  9249. // We only allow noclip here only because noclip is useful for debugging.
  9250. // It would be nice if the noclip command set some flag so we could tell that they
  9251. // did it intentionally.
  9252. if ( IsEFlagSet( EFL_NOCLIP_ACTIVE ) )
  9253. {
  9254. // Assert( GetMoveType() == MOVETYPE_NOCLIP );
  9255. }
  9256. else
  9257. {
  9258. // Assert( GetMoveType() == MOVETYPE_WALK );
  9259. }
  9260. Assert( !IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  9261. }
  9262. bool CCSPlayer::StartObserverMode( int mode )
  9263. {
  9264. if ( !BaseClass::StartObserverMode( mode ) )
  9265. return false;
  9266. // When you enter observer mode, you are no longer planting the bomb or crouch-jumping.
  9267. m_bDuckOverride = false;
  9268. m_duckUntilOnGround = false;
  9269. return true;
  9270. }
  9271. bool CCSPlayer::SetObserverTarget(CBaseEntity *target)
  9272. {
  9273. if ( target )
  9274. {
  9275. CCSPlayer *pPlayer = dynamic_cast<CCSPlayer*>( target );
  9276. if ( pPlayer )
  9277. pPlayer->RefreshCarriedHostage( false );
  9278. }
  9279. return BaseClass::SetObserverTarget(target);
  9280. }
  9281. void CCSPlayer::ValidateCurrentObserverTarget( void )
  9282. {
  9283. if ( !m_bForcedObserverMode )
  9284. {
  9285. CCSPlayer *pPlayer = ToCSPlayer( m_hObserverTarget.Get() );
  9286. if ( IsValidObserverTarget( pPlayer ) && ( pPlayer->IsTaunting() && pPlayer->IsThirdPersonTaunt() ) )
  9287. {
  9288. ForceObserverMode( OBS_MODE_CHASE );
  9289. }
  9290. }
  9291. BaseClass::ValidateCurrentObserverTarget();
  9292. }
  9293. void CCSPlayer::CheckObserverSettings( void )
  9294. {
  9295. BaseClass::CheckObserverSettings();
  9296. if ( m_bForcedObserverMode )
  9297. {
  9298. CCSPlayer *pPlayer = ToCSPlayer( m_hObserverTarget.Get() );
  9299. if ( IsValidObserverTarget( pPlayer ) && !( pPlayer->IsTaunting() && pPlayer->IsThirdPersonTaunt() ) )
  9300. {
  9301. SetObserverMode( m_iObserverLastMode ); // switch to last mode
  9302. m_bForcedObserverMode = false; // disable force mode
  9303. }
  9304. }
  9305. }
  9306. void CCSPlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
  9307. {
  9308. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
  9309. if ( pCSWeapon )
  9310. {
  9311. // For rifles, pistols, or the knife, drop our old weapon in this slot.
  9312. if ( pCSWeapon->GetSlot() == WEAPON_SLOT_RIFLE ||
  9313. pCSWeapon->GetSlot() == WEAPON_SLOT_PISTOL )
  9314. {
  9315. CBaseCombatWeapon *pDropWeapon = Weapon_GetSlot( pCSWeapon->GetSlot() );
  9316. if ( pDropWeapon )
  9317. {
  9318. CSWeaponDrop( pDropWeapon, false, true );
  9319. }
  9320. }
  9321. else if( pCSWeapon->GetWeaponType() == WEAPONTYPE_GRENADE || pCSWeapon->GetWeaponType() == WEAPONTYPE_STACKABLEITEM )
  9322. {
  9323. //if we already have this weapon, just add the ammo and destroy it
  9324. if( Weapon_OwnsThisType( pCSWeapon->GetClassname() ) )
  9325. {
  9326. Weapon_EquipAmmoOnly( pWeapon );
  9327. UTIL_Remove( pCSWeapon );
  9328. RecalculateCurrentEquipmentValue();
  9329. return;
  9330. }
  9331. }
  9332. pCSWeapon->SetSolidFlags( FSOLID_NOT_SOLID );
  9333. pCSWeapon->SetOwnerEntity( this );
  9334. }
  9335. BaseClass::Weapon_Equip( pWeapon );
  9336. // old players don't know how to unhide their world models a little bit into their deploys,
  9337. // because old players don't have deploy animations at all.
  9338. if ( !m_bUseNewAnimstate && pWeapon && pWeapon->GetWeaponWorldModel() )
  9339. {
  9340. pWeapon->ShowWeaponWorldModel( true );
  9341. }
  9342. RecalculateCurrentEquipmentValue();
  9343. }
  9344. bool CCSPlayer::Weapon_CanUse( CBaseCombatWeapon *pBaseWeapon )
  9345. {
  9346. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  9347. if ( pWeapon )
  9348. {
  9349. // we don't want bots picking up items for players in the world
  9350. if ( IsBot() && CSGameRules() && CSGameRules()->IsPlayingCoopMission() )
  9351. {
  9352. if ( pWeapon->IsConstrained() || pWeapon->IsA( WEAPON_TAGRENADE ) )
  9353. return false;
  9354. }
  9355. if ( pWeapon->IsA(WEAPON_TASER) && !pWeapon->HasAnyAmmo() )
  9356. return false;
  9357. if ( CanAcquire( pWeapon->GetCSWeaponID(), AcquireMethod::PickUp ) != AcquireResult::Allowed )
  9358. return false;
  9359. bool bAllowCustomKnifeSpawns = false;
  9360. if ( !bAllowCustomKnifeSpawns && pWeapon->IsMeleeWeapon() && pWeapon->GetEconItemView() &&
  9361. !( pWeapon->GetEconItemView()->GetItemDefinition()->IsDefaultSlotItem() || pWeapon->GetEconItemView()->GetItemID() != 0 || FClassnameIs( pWeapon, "weapon_knifegg" ) ) )
  9362. {
  9363. return false;
  9364. }
  9365. }
  9366. return true;
  9367. }
  9368. bool CCSPlayer::BumpWeapon( CBaseCombatWeapon *pBaseWeapon )
  9369. {
  9370. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  9371. if ( !pWeapon )
  9372. {
  9373. Assert( !pWeapon );
  9374. pBaseWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
  9375. pBaseWeapon->AddEffects( EF_NODRAW );
  9376. Weapon_Equip( pBaseWeapon );
  9377. return true;
  9378. }
  9379. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  9380. // Can I have this weapon type?
  9381. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  9382. {
  9383. extern int gEvilImpulse101;
  9384. if ( gEvilImpulse101 || CSGameRules()->IsPlayingGunGameDeathmatch() )
  9385. {
  9386. UTIL_Remove( pWeapon );
  9387. }
  9388. return false;
  9389. }
  9390. // Even if we already have a grenade in this slot, we can pickup another one if we don't already
  9391. // own this type of grenade.
  9392. bool bPickupGrenade = ( pWeapon->GetWeaponType() == WEAPONTYPE_GRENADE );
  9393. bool bStackableItem = ( pWeapon->GetWeaponType() == WEAPONTYPE_STACKABLEITEM );
  9394. /*
  9395. // ----------------------------------------
  9396. // If I already have it just take the ammo
  9397. // ----------------------------------------
  9398. if ( !bPickupGrenade && Weapon_SlotOccupied( pWeapon ) )
  9399. {
  9400. Weapon_EquipAmmoOnly( pWeapon );
  9401. // Only remove me if I have no ammo left
  9402. // Can't just check HasAnyAmmo because if I don't use clips, I want to be removed,
  9403. if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() )
  9404. return false;
  9405. UTIL_Remove( pWeapon );
  9406. return false;
  9407. }
  9408. */
  9409. if ( HasShield() && pWeapon->CanBeUsedWithShield() == false )
  9410. return false;
  9411. bool bPickupTaser = ( pWeapon->IsA( WEAPON_TASER ) );
  9412. if ( bPickupTaser )
  9413. {
  9414. CBaseCombatWeapon *pOwnedTaser = CSWeapon_OwnsThisType( pWeapon->GetEconItemView() );
  9415. if ( pOwnedTaser )
  9416. return false;
  9417. }
  9418. bool bPickupC4 = ( pWeapon->GetWeaponType() == WEAPONTYPE_C4 );
  9419. if ( bPickupC4 )
  9420. {
  9421. // we're only allowed to pick up one c4 at a time
  9422. CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
  9423. if ( pC4 )
  9424. return false;
  9425. // see if we're trying to pick up the bomb without being able to "see" it
  9426. // prevent picking it up through a thin wall
  9427. float flDist = (pWeapon->GetAbsOrigin() - GetAbsOrigin()).AsVector2D().Length();
  9428. if ( flDist > 34 )
  9429. {
  9430. trace_t tr;
  9431. UTIL_TraceLine( pWeapon->GetAbsOrigin(), EyePosition(), MASK_VISIBLE, this, COLLISION_GROUP_DEBRIS, &tr );
  9432. if ( tr.fraction < 1.0 )
  9433. return false;
  9434. }
  9435. }
  9436. // don't let AFK players catch the bomb
  9437. if ( bPickupC4 && !m_bHasMovedSinceSpawn && CSGameRules()->GetRoundElapsedTime() > sv_spawn_afk_bomb_drop_time.GetFloat()
  9438. && !CSGameRules()->IsPlayingCoopMission() )
  9439. {
  9440. return false;
  9441. }
  9442. // bool bPickupCarriableItem = ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_CARRIABLEITEM );
  9443. // if ( bPickupCarriableItem && Weapon_SlotOccupied( pWeapon ) )
  9444. // {
  9445. // CBaseCarribleItem *pPickupItem = static_cast<CBaseCarribleItem*>(pWeapon);
  9446. // if ( pPickupItem )
  9447. // {
  9448. // CBaseCarribleItem *pOwnedItem = static_cast<CBaseCarribleItem*>(Weapon_OwnsThisType( pPickupItem->GetClassname(), pPickupItem->GetSubType() ));
  9449. // if ( pOwnedItem && pOwnedItem->GetCurrentItems() < pOwnedItem->GetMaxItems() )
  9450. // {
  9451. // pOwnedItem->AddAmmo( pPickupItem->GetCurrentItems() );
  9452. // UTIL_Remove( pPickupItem );
  9453. // }
  9454. //
  9455. // return false;
  9456. // }
  9457. // }
  9458. if( bPickupC4 || bStackableItem || bPickupGrenade || bPickupTaser || /*bPickupCarriableItem || */ !Weapon_SlotOccupied( pWeapon ) )
  9459. {
  9460. // we have to do this here because picking up weapons placed in the world don't have their clips set
  9461. // TODO: give the weapon a clip on spawn and not when picked up!
  9462. if ( !pWeapon->GetPreviousOwner() )
  9463. StockPlayerAmmo( pWeapon );
  9464. SetPickedUpWeaponThisRound( true );
  9465. pWeapon->CheckRespawn();
  9466. pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
  9467. pWeapon->AddEffects( EF_NODRAW );
  9468. CCSPlayer* donor = pWeapon->GetDonor();
  9469. if (donor )
  9470. {
  9471. CCS_GameStats.Event_PlayerDonatedWeapon(donor );
  9472. pWeapon->SetDonor(NULL );
  9473. }
  9474. Weapon_Equip( pWeapon );
  9475. // Made obsolete when ammo was moved from player to weapon
  9476. // int iExtraAmmo = pWeapon->GetExtraAmmoCount();
  9477. //
  9478. // if( iExtraAmmo /*&& !bPickupGrenade*/ /*&& !bPickupCarriableItem*/ )
  9479. // {
  9480. // //Find out the index of the ammo
  9481. // int iAmmoIndex = pWeapon->GetPrimaryAmmoType();
  9482. //
  9483. // if( iAmmoIndex != -1 )
  9484. // {
  9485. // //Remove the extra ammo from the weapon
  9486. // pWeapon->SetExtraAmmoCount(0 );
  9487. //
  9488. // //Give it to the player
  9489. // SetAmmoCount( iExtraAmmo, iAmmoIndex );
  9490. // }
  9491. // }
  9492. bool bIsSilentPickup = ShouldPickupItemSilently( this );
  9493. IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
  9494. if( event )
  9495. {
  9496. const char *weaponName = pWeapon->GetClassname();
  9497. if ( IsWeaponClassname( weaponName ) )
  9498. {
  9499. weaponName += WEAPON_CLASSNAME_PREFIX_LENGTH;
  9500. }
  9501. event->SetInt( "userid", GetUserID() );
  9502. event->SetString( "item", weaponName );
  9503. event->SetBool( "silent", bIsSilentPickup );
  9504. gameeventmanager->FireEvent( event );
  9505. }
  9506. if ( !bIsSilentPickup )
  9507. EmitSound( "Player.PickupWeapon" );
  9508. return true;
  9509. }
  9510. return false;
  9511. }
  9512. void CCSPlayer::ResetStamina( void )
  9513. {
  9514. m_flStamina = 0.0f;
  9515. }
  9516. void CCSPlayer::RescueZoneTouch( inputdata_t &inputdata )
  9517. {
  9518. m_bInHostageRescueZone = true;
  9519. if ( GetTeamNumber() == TEAM_CT && !(m_iDisplayHistoryBits & DHF_IN_RESCUE_ZONE ) )
  9520. {
  9521. HintMessage( "#Hint_hostage_rescue_zone", false );
  9522. m_iDisplayHistoryBits |= DHF_IN_RESCUE_ZONE;
  9523. }
  9524. // if the player is carrying a hostage when he touches the rescue zone, pass the touch input to it
  9525. if ( m_hCarriedHostage && m_hCarriedHostage.Get() )
  9526. {
  9527. variant_t emptyVariant;
  9528. m_hCarriedHostage.Get()->AcceptInput( "OnRescueZoneTouch", NULL, NULL, emptyVariant, 0 );
  9529. }
  9530. }
  9531. //------------------------------------------------------------------------------------------
  9532. CON_COMMAND_F( timeleft, "prints the time remaining in the match", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  9533. {
  9534. CCSPlayer *pPlayer = ToCSPlayer( UTIL_GetCommandClient() );
  9535. if ( pPlayer && pPlayer->m_iNextTimeCheck >= gpGlobals->curtime )
  9536. {
  9537. return; // rate limiting
  9538. }
  9539. int iTimeRemaining = (int )CSGameRules()->GetMapRemainingTime();
  9540. if ( iTimeRemaining < 0 )
  9541. {
  9542. if ( pPlayer )
  9543. {
  9544. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_no_timelimit" );
  9545. }
  9546. else
  9547. {
  9548. Msg( "* No Time Limit *\n" );
  9549. }
  9550. }
  9551. else if ( iTimeRemaining == 0 )
  9552. {
  9553. if ( pPlayer )
  9554. {
  9555. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_last_round" );
  9556. }
  9557. else
  9558. {
  9559. Msg( "* Last Round *\n" );
  9560. }
  9561. }
  9562. else
  9563. {
  9564. int iMinutes, iSeconds;
  9565. iMinutes = iTimeRemaining / 60;
  9566. iSeconds = iTimeRemaining % 60;
  9567. char minutes[8];
  9568. char seconds[8];
  9569. Q_snprintf( minutes, sizeof(minutes ), "%d", iMinutes );
  9570. Q_snprintf( seconds, sizeof(seconds ), "%2.2d", iSeconds );
  9571. if ( pPlayer )
  9572. {
  9573. ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_timelimit", minutes, seconds );
  9574. }
  9575. else
  9576. {
  9577. Msg( "Time Remaining: %s:%s\n", minutes, seconds );
  9578. }
  9579. }
  9580. if ( pPlayer )
  9581. {
  9582. pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
  9583. }
  9584. }
  9585. //------------------------------------------------------------------------------------------
  9586. /**
  9587. * Emit given sound that only we can hear
  9588. */
  9589. void CCSPlayer::EmitPrivateSound( const char *soundName )
  9590. {
  9591. CSoundParameters params;
  9592. if (!GetParametersForSound( soundName, params, NULL ) )
  9593. return;
  9594. CSingleUserRecipientFilter filter( this );
  9595. EmitSound( filter, entindex(), soundName );
  9596. }
  9597. //=====================
  9598. //==============================================
  9599. //AutoBuy - do the work of deciding what to buy
  9600. //==============================================
  9601. void CCSPlayer::AutoBuy( const char *autobuyString )
  9602. {
  9603. if ( !IsInBuyZone() )
  9604. {
  9605. EmitPrivateSound( "BuyPreset.CantBuy" );
  9606. return;
  9607. }
  9608. if ( !autobuyString || !*autobuyString )
  9609. {
  9610. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  9611. return;
  9612. }
  9613. bool boughtPrimary = false, boughtSecondary = false;
  9614. m_bIsInAutoBuy = true;
  9615. ParseAutoBuyString(autobuyString, boughtPrimary, boughtSecondary );
  9616. m_bIsInAutoBuy = false;
  9617. m_bAutoReload = true;
  9618. //TODO ?: stripped out all the attempts to buy career weapons.
  9619. // as we're not porting cs:cz, these were skipped
  9620. }
  9621. int CCSPlayer::GetAccountBalance( void )
  9622. {
  9623. if ( CSGameRules() && CSGameRules()->IsPlayingGunGameDeathmatch() )
  9624. return 99999;
  9625. return m_iAccount;
  9626. }
  9627. void CCSPlayer::ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary )
  9628. {
  9629. char command[32];
  9630. int nBuffSize = sizeof(command ) - 1; // -1 to leave space for the NUL at the end of the string
  9631. const char *c = string;
  9632. if (c == NULL )
  9633. {
  9634. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  9635. return;
  9636. }
  9637. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  9638. // loop through the string of commands, trying each one in turn.
  9639. while (*c != 0 )
  9640. {
  9641. int i = 0;
  9642. // copy the next word into the command buffer.
  9643. while ((*c != 0 ) && (*c != ' ' ) && (i < nBuffSize))
  9644. {
  9645. command[i] = *(c );
  9646. ++c;
  9647. ++i;
  9648. }
  9649. if (*c == ' ' )
  9650. {
  9651. ++c; // skip the space.
  9652. }
  9653. command[i] = 0; // terminate the string.
  9654. // clear out any spaces.
  9655. i = 0;
  9656. while (command[i] != 0 )
  9657. {
  9658. if (command[i] == ' ' )
  9659. {
  9660. command[i] = 0;
  9661. break;
  9662. }
  9663. ++i;
  9664. }
  9665. // make sure we actually have a command.
  9666. if (strlen(command ) == 0 )
  9667. {
  9668. continue;
  9669. }
  9670. AutoBuyInfoStruct * commandInfo = GetAutoBuyCommandInfo(command );
  9671. if (ShouldExecuteAutoBuyCommand(commandInfo, boughtPrimary, boughtSecondary ) )
  9672. {
  9673. BuyResult_e result = HandleCommand_Buy( command, commandInfo->m_LoadoutPosition );
  9674. overallResult = CombineBuyResults( overallResult, result );
  9675. // check to see if we actually bought a primary or secondary weapon this time.
  9676. PostAutoBuyCommandProcessing(commandInfo, boughtPrimary, boughtSecondary );
  9677. }
  9678. }
  9679. if ( overallResult == BUY_CANT_AFFORD )
  9680. {
  9681. EmitPrivateSound( "BuyPreset.CantBuy" );
  9682. }
  9683. else if ( overallResult == BUY_ALREADY_HAVE )
  9684. {
  9685. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  9686. }
  9687. }
  9688. BuyResult_e CCSPlayer::CombineBuyResults( BuyResult_e prevResult, BuyResult_e newResult )
  9689. {
  9690. if ( newResult == BUY_BOUGHT )
  9691. {
  9692. prevResult = BUY_BOUGHT;
  9693. }
  9694. else if ( prevResult != BUY_BOUGHT &&
  9695. (newResult == BUY_CANT_AFFORD || newResult == BUY_INVALID_ITEM || newResult == BUY_PLAYER_CANT_BUY ) )
  9696. {
  9697. prevResult = BUY_CANT_AFFORD;
  9698. }
  9699. return prevResult;
  9700. }
  9701. //==============================================
  9702. //PostAutoBuyCommandProcessing
  9703. //==============================================
  9704. void CCSPlayer::PostAutoBuyCommandProcessing(const AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary )
  9705. {
  9706. if (commandInfo == NULL )
  9707. {
  9708. return;
  9709. }
  9710. CBaseCombatWeapon *pPrimary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  9711. CBaseCombatWeapon *pSecondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  9712. if ((pPrimary != NULL ) && (stricmp(pPrimary->GetClassname(), commandInfo->m_classname ) == 0))
  9713. {
  9714. // I just bought the gun I was trying to buy.
  9715. boughtPrimary = true;
  9716. }
  9717. else if ((pPrimary == NULL ) && ((commandInfo->m_class & AUTOBUYCLASS_SHIELD ) == AUTOBUYCLASS_SHIELD) && HasShield())
  9718. {
  9719. // the shield is a primary weapon even though it isn't a "real" weapon.
  9720. boughtPrimary = true;
  9721. }
  9722. else if ((pSecondary != NULL ) && (stricmp(pSecondary->GetClassname(), commandInfo->m_classname ) == 0))
  9723. {
  9724. // I just bought the pistol I was trying to buy.
  9725. boughtSecondary = true;
  9726. }
  9727. }
  9728. bool CCSPlayer::ShouldExecuteAutoBuyCommand(const AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary )
  9729. {
  9730. if (commandInfo == NULL )
  9731. {
  9732. return false;
  9733. }
  9734. if ((boughtPrimary ) && ((commandInfo->m_class & AUTOBUYCLASS_PRIMARY ) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
  9735. {
  9736. // this is a primary weapon and we already have one.
  9737. return false;
  9738. }
  9739. if ((boughtSecondary ) && ((commandInfo->m_class & AUTOBUYCLASS_SECONDARY ) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
  9740. {
  9741. // this is a secondary weapon and we already have one.
  9742. return false;
  9743. }
  9744. if( commandInfo->m_class & AUTOBUYCLASS_ARMOR && ArmorValue() >= 100 )
  9745. {
  9746. return false;
  9747. }
  9748. return true;
  9749. }
  9750. AutoBuyInfoStruct *CCSPlayer::GetAutoBuyCommandInfo(const char *command )
  9751. {
  9752. int i = 0;
  9753. AutoBuyInfoStruct *ret = NULL;
  9754. AutoBuyInfoStruct *temp = &(g_autoBuyInfo[i] );
  9755. // loop through all the commands till we find the one that matches.
  9756. while ((ret == NULL ) && (temp->m_class != (AutoBuyClassType )0))
  9757. {
  9758. temp = &(g_autoBuyInfo[i] );
  9759. ++i;
  9760. if (stricmp(temp->m_command, command ) == 0 )
  9761. {
  9762. ret = temp;
  9763. }
  9764. }
  9765. return ret;
  9766. }
  9767. //==============================================
  9768. //PostAutoBuyCommandProcessing
  9769. //- reorders the tokens in autobuyString based on the order of tokens in the priorityString.
  9770. //==============================================
  9771. void CCSPlayer::PrioritizeAutoBuyString(char *autobuyString, const char *priorityString )
  9772. {
  9773. char newString[256];
  9774. int newStringPos = 0;
  9775. char priorityToken[32];
  9776. if ((priorityString == NULL ) || (autobuyString == NULL ))
  9777. {
  9778. return;
  9779. }
  9780. const char *priorityChar = priorityString;
  9781. while (*priorityChar != 0 )
  9782. {
  9783. int i = 0;
  9784. // get the next token from the priority string.
  9785. while ((*priorityChar != 0 ) && (*priorityChar != ' ' ))
  9786. {
  9787. priorityToken[i] = *priorityChar;
  9788. ++i;
  9789. ++priorityChar;
  9790. }
  9791. priorityToken[i] = 0;
  9792. // skip spaces
  9793. while (*priorityChar == ' ' )
  9794. {
  9795. ++priorityChar;
  9796. }
  9797. if (strlen(priorityToken ) == 0 )
  9798. {
  9799. continue;
  9800. }
  9801. // see if the priority token is in the autobuy string.
  9802. // if it is, copy that token to the new string and blank out
  9803. // that token in the autobuy string.
  9804. char *autoBuyPosition = strstr(autobuyString, priorityToken );
  9805. if (autoBuyPosition != NULL )
  9806. {
  9807. while ((*autoBuyPosition != 0 ) && (*autoBuyPosition != ' ' ))
  9808. {
  9809. newString[newStringPos] = *autoBuyPosition;
  9810. *autoBuyPosition = ' ';
  9811. ++newStringPos;
  9812. ++autoBuyPosition;
  9813. }
  9814. newString[newStringPos++] = ' ';
  9815. }
  9816. }
  9817. // now just copy anything left in the autobuyString to the new string in the order it's in already.
  9818. char *autobuyPosition = autobuyString;
  9819. while (*autobuyPosition != 0 )
  9820. {
  9821. // skip spaces
  9822. while (*autobuyPosition == ' ' )
  9823. {
  9824. ++autobuyPosition;
  9825. }
  9826. // copy the token over to the new string.
  9827. while ((*autobuyPosition != 0 ) && (*autobuyPosition != ' ' ))
  9828. {
  9829. newString[newStringPos] = *autobuyPosition;
  9830. ++newStringPos;
  9831. ++autobuyPosition;
  9832. }
  9833. // add a space at the end.
  9834. newString[newStringPos++] = ' ';
  9835. }
  9836. // terminate the string. Trailing spaces shouldn't matter.
  9837. newString[newStringPos] = 0;
  9838. Q_snprintf(autobuyString, sizeof(autobuyString ), "%s", newString );
  9839. }
  9840. bool CCSPlayer::AttemptToBuyDMBonusWeapon( void )
  9841. {
  9842. if ( !CSGameRules() || !( CSGameRules()->IsDMBonusActive() ) )
  9843. return false;
  9844. loadout_positions_t unPosition = CSGameRules()->GetDMBonusWeaponLoadoutSlot();
  9845. const CBaseCombatWeapon *pHaveWeapon = Weapon_GetPosition( unPosition );
  9846. // If we already have the bonus weapon, switch to it.
  9847. if ( pHaveWeapon )
  9848. {
  9849. switch( pHaveWeapon->GetSlot() )
  9850. {
  9851. case WEAPON_SLOT_RIFLE:
  9852. engine->ClientCommand( edict(), "slot1\n");
  9853. break;
  9854. case WEAPON_SLOT_PISTOL:
  9855. engine->ClientCommand( edict(), "slot2\n");
  9856. break;
  9857. case WEAPON_SLOT_KNIFE:
  9858. engine->ClientCommand( edict(), "slot3\n");
  9859. break;
  9860. }
  9861. m_bIsRespawningForDMBonus = false;
  9862. m_bHasUsedDMBonusRespawn = true;
  9863. return true;
  9864. }
  9865. // Otherwise, if we don't own it but can buy it, buy it.
  9866. if ( CanPlayerBuy( false ) )
  9867. {
  9868. CEconItemView* pItem = Inventory()->GetItemInLoadout( GetTeamNumber(), unPosition );
  9869. BuyResult_e buyresult = HandleCommand_Buy( pItem->GetItemDefinition()->GetDefinitionName(), unPosition, false );
  9870. if ( buyresult == BUY_BOUGHT )
  9871. {
  9872. CSWeaponID wid = WeaponIdFromString( pItem->GetStaticData()->GetItemClass() );
  9873. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( wid );
  9874. int iSlot = pWeaponInfo->GetBucketSlot( pItem );
  9875. switch ( iSlot )
  9876. {
  9877. case WEAPON_SLOT_RIFLE:
  9878. engine->ClientCommand( edict(), "slot1\n" );
  9879. break;
  9880. case WEAPON_SLOT_PISTOL:
  9881. engine->ClientCommand( edict(), "slot2\n" );
  9882. break;
  9883. case WEAPON_SLOT_KNIFE:
  9884. engine->ClientCommand( edict(), "slot3\n" );
  9885. break;
  9886. }
  9887. }
  9888. m_bIsRespawningForDMBonus = false;
  9889. m_bHasUsedDMBonusRespawn = true;
  9890. return true;
  9891. }
  9892. // otherwise respawn, which will end up in the 'buy it' case above.
  9893. if ( !m_bHasUsedDMBonusRespawn )
  9894. {
  9895. m_bIsRespawningForDMBonus = true;
  9896. m_bHasUsedDMBonusRespawn = true;
  9897. RecordRebuyStructLastRound();
  9898. ForceRespawn();
  9899. return true;
  9900. }
  9901. return false;
  9902. }
  9903. //==============================================================
  9904. // ReBuy
  9905. // system for attempting to buy the weapons you had last round
  9906. //==============================================================
  9907. void CCSPlayer::AddToRebuy( CSWeaponID weaponId, int nPos )
  9908. {
  9909. if ( weaponId == ITEM_NVG )
  9910. {
  9911. m_rebuyStruct.SetNightVision( true );
  9912. return;
  9913. }
  9914. if ( weaponId == ITEM_KEVLAR )
  9915. {
  9916. m_rebuyStruct.SetArmor( 1 );
  9917. return;
  9918. }
  9919. if ( weaponId == ITEM_ASSAULTSUIT )
  9920. {
  9921. m_rebuyStruct.SetArmor( 2 );
  9922. return;
  9923. }
  9924. if ( weaponId == ITEM_DEFUSER )
  9925. {
  9926. m_rebuyStruct.SetDefuser( true );
  9927. return;
  9928. }
  9929. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( weaponId );
  9930. // TODO: Add special handling for equipment without info data?
  9931. if ( pWeaponInfo == NULL )
  9932. return;
  9933. switch ( pWeaponInfo->iSlot )
  9934. {
  9935. case WEAPON_SLOT_RIFLE:
  9936. m_rebuyStruct.SetPrimary( nPos );
  9937. break;
  9938. case WEAPON_SLOT_PISTOL:
  9939. m_rebuyStruct.SetSecondary( nPos );
  9940. break;
  9941. case WEAPON_SLOT_KNIFE:
  9942. m_rebuyStruct.SetTertiary( weaponId );
  9943. break;
  9944. case WEAPON_SLOT_GRENADES:
  9945. AddToGrenadeRebuy( weaponId );
  9946. break;
  9947. case WEAPON_SLOT_C4:
  9948. break;
  9949. default:
  9950. Error( "Unhandled weapon slot (%i) in AddToRebuy\n", pWeaponInfo->iSlot );
  9951. break;
  9952. }
  9953. }
  9954. void CCSPlayer::AddToGrenadeRebuy( CSWeaponID weaponId )
  9955. {
  9956. int iQueueSize = MIN( ammo_grenade_limit_total.GetInt(), m_rebuyStruct.numGrenades() );
  9957. // see if it's already in the list
  9958. for ( int i = 0; i < iQueueSize; ++i )
  9959. {
  9960. if ( m_rebuyStruct.GetGrenade( i ) == weaponId )
  9961. return;
  9962. }
  9963. // shift the list down
  9964. for ( int i = m_rebuyStruct.numGrenades() - 1; i > 0; --i )
  9965. {
  9966. m_rebuyStruct.SetGrenade( i, m_rebuyStruct.GetGrenade( i - 1 ) );
  9967. }
  9968. // add it to the front
  9969. m_rebuyStruct.SetGrenade( 0, weaponId );
  9970. }
  9971. void CCSPlayer::Rebuy( const char *rebuyString )
  9972. {
  9973. if ( !IsInBuyZone() )
  9974. {
  9975. EmitPrivateSound( "BuyPreset.CantBuy" );
  9976. return;
  9977. }
  9978. if ( !rebuyString || !*rebuyString )
  9979. {
  9980. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  9981. return;
  9982. }
  9983. m_bIsInRebuy = true;
  9984. BuyResult_e overallResult = BUY_ALREADY_HAVE;
  9985. char token[256];
  9986. rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );
  9987. while (rebuyString != NULL )
  9988. {
  9989. BuyResult_e result = BUY_ALREADY_HAVE;
  9990. if ( Q_strcasecmp( token, "PrimaryWeapon" ) == 0 )
  9991. {
  9992. result = RebuyPrimaryWeapon();
  9993. }
  9994. else if ( Q_strcasecmp(token, "SecondaryWeapon" ) == 0 )
  9995. {
  9996. result = RebuySecondaryWeapon();
  9997. }
  9998. else if ( Q_strcasecmp(token, "Taser" ) == 0 ) // TODO[pmf]: handle this better
  9999. {
  10000. result = RebuyTaser();
  10001. }
  10002. else if ( Q_strcasecmp(token, "Armor" ) == 0 )
  10003. {
  10004. result = RebuyArmor();
  10005. }
  10006. else if ( Q_strcasecmp(token, "Defuser" ) == 0 )
  10007. {
  10008. result = RebuyDefuser();
  10009. }
  10010. else if ( Q_strcasecmp(token, "NightVision" ) == 0 )
  10011. {
  10012. result = RebuyNightVision();
  10013. }
  10014. else
  10015. {
  10016. CSWeaponID weaponId = AliasToWeaponID( token );
  10017. if ( weaponId != WEAPON_NONE )
  10018. result = RebuyGrenade( weaponId );
  10019. }
  10020. overallResult = CombineBuyResults( overallResult, result );
  10021. rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );
  10022. }
  10023. m_bIsInRebuy = false;
  10024. // after we're done buying, the user is done with their equipment purchasing experience.
  10025. // so we are effectively out of the buy zone.
  10026. // if (TheTutor != NULL )
  10027. // {
  10028. // TheTutor->OnEvent(EVENT_PLAYER_LEFT_BUY_ZONE );
  10029. // }
  10030. m_bAutoReload = true;
  10031. if ( overallResult == BUY_CANT_AFFORD )
  10032. {
  10033. EmitPrivateSound( "BuyPreset.CantBuy" );
  10034. }
  10035. else if ( overallResult == BUY_ALREADY_HAVE )
  10036. {
  10037. EmitPrivateSound( "BuyPreset.AlreadyBought" );
  10038. }
  10039. }
  10040. BuyResult_e CCSPlayer::RebuyPrimaryWeapon()
  10041. {
  10042. CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  10043. if (primary != NULL )
  10044. return BUY_ALREADY_HAVE; // don't drop primary weapons via rebuy - if the player picked up a different weapon, he wants to keep it.
  10045. int nPos = m_rebuyStructLastRound.GetPrimary();
  10046. if ( nPos != 0 )
  10047. {
  10048. return HandleCommand_Buy( "", nPos );
  10049. }
  10050. return BUY_INVALID_ITEM;
  10051. }
  10052. BuyResult_e CCSPlayer::RebuySecondaryWeapon()
  10053. {
  10054. CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  10055. if (pistol != NULL && !m_bUsingDefaultPistol )
  10056. return BUY_ALREADY_HAVE; // don't drop pistols via rebuy if we've bought one other than the default pistol
  10057. int nPos = m_rebuyStructLastRound.GetSecondary();
  10058. if ( nPos != 0 )
  10059. {
  10060. // skip pistol rebuy if the deathmatch bonus weapon is the default pistol
  10061. if ( m_bIsRespawningForDMBonus &&
  10062. ( CSGameRules()->GetDMBonusWeaponLoadoutSlot() == LOADOUT_POSITION_SECONDARY0 ) )
  10063. {
  10064. return BUY_BOUGHT;
  10065. }
  10066. else
  10067. {
  10068. return HandleCommand_Buy( "", nPos );
  10069. }
  10070. }
  10071. return BUY_INVALID_ITEM;
  10072. }
  10073. BuyResult_e CCSPlayer::RebuyTaser()
  10074. {
  10075. if ( m_rebuyStructLastRound.GetTertiary() == WEAPON_TASER )
  10076. return HandleCommand_Buy( "taser", LOADOUT_POSITION_EQUIPMENT2 );
  10077. return BUY_INVALID_ITEM;
  10078. }
  10079. BuyResult_e CCSPlayer::RebuyGrenade( CSWeaponID weaponId )
  10080. {
  10081. int iQueueSize = MIN( ammo_grenade_limit_total.GetInt(), m_rebuyStructLastRound.numGrenades() );
  10082. // is it in the rebuy list
  10083. for ( int i = 0; i < iQueueSize; ++i )
  10084. {
  10085. if ( m_rebuyStructLastRound.GetGrenade( i ) == weaponId )
  10086. {
  10087. int nPos = -1;
  10088. char wpnClassName[ MAX_WEAPON_STRING ];
  10089. wpnClassName[ 0 ] = '\0';
  10090. V_sprintf_safe( wpnClassName, "weapon_%s", WeaponIDToAlias( weaponId ) );
  10091. const CCStrike15ItemDefinition * pItemDef = dynamic_cast< const CCStrike15ItemDefinition * >( GetItemSchema()->GetItemDefinitionByName( wpnClassName ) );
  10092. Assert( pItemDef );
  10093. nPos = pItemDef->GetLoadoutSlot( GetTeamNumber() );
  10094. return HandleCommand_Buy( WeaponIDToAlias( weaponId ), nPos );
  10095. }
  10096. }
  10097. return BUY_INVALID_ITEM;
  10098. }
  10099. BuyResult_e CCSPlayer::RebuyDefuser()
  10100. {
  10101. if ( m_rebuyStructLastRound.GetDefuser() )
  10102. {
  10103. if ( HasDefuser() )
  10104. return BUY_ALREADY_HAVE;
  10105. else
  10106. return HandleCommand_Buy( "defuser" );
  10107. }
  10108. return BUY_INVALID_ITEM;
  10109. }
  10110. BuyResult_e CCSPlayer::RebuyNightVision()
  10111. {
  10112. if ( m_rebuyStructLastRound.GetNightVision() )
  10113. {
  10114. if ( m_bHasNightVision )
  10115. return BUY_ALREADY_HAVE;
  10116. else
  10117. return HandleCommand_Buy( "nvgs" );
  10118. }
  10119. return BUY_INVALID_ITEM;
  10120. }
  10121. BuyResult_e CCSPlayer::RebuyArmor()
  10122. {
  10123. if (m_rebuyStructLastRound.GetArmor() > 0 )
  10124. {
  10125. int armor = 0;
  10126. if( m_bHasHelmet )
  10127. armor = 2;
  10128. else if( ArmorValue() > 0 )
  10129. armor = 1;
  10130. if( armor < m_rebuyStructLastRound.GetArmor() )
  10131. {
  10132. if (m_rebuyStructLastRound.GetArmor() == 1 )
  10133. {
  10134. return HandleCommand_Buy("vest" );
  10135. }
  10136. else
  10137. {
  10138. return HandleCommand_Buy("vesthelm" );
  10139. }
  10140. }
  10141. }
  10142. return BUY_ALREADY_HAVE;
  10143. }
  10144. static void BuyRandom( void )
  10145. {
  10146. CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() );
  10147. if ( !player )
  10148. return;
  10149. player->BuyRandom();
  10150. }
  10151. static ConCommand buyrandom( "buyrandom", BuyRandom, "Buy random primary and secondary. Primarily for deathmatch where cost is not an issue.", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS );
  10152. void CCSPlayer::BuyRandom( void )
  10153. {
  10154. if ( !IsInBuyZone() )
  10155. {
  10156. EmitPrivateSound( "BuyPreset.CantBuy" );
  10157. return;
  10158. }
  10159. m_bIsInAutoBuy = true;
  10160. // Make lists of primary and secondary weapons.
  10161. CUtlVector< int > primaryweapons;
  10162. CUtlVector< int > secondaryweapons;
  10163. for ( int w = WEAPON_FIRST; w < WEAPON_LAST; w++ )
  10164. {
  10165. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( (CSWeaponID)w );
  10166. if ( pWeaponInfo )
  10167. {
  10168. bool isRifle = pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE;
  10169. bool isPistol = pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL;
  10170. bool isTeamAppropriate = ( ( pWeaponInfo->GetUsedByTeam() == GetTeamNumber() ) ||
  10171. ( pWeaponInfo->GetUsedByTeam() == TEAM_UNASSIGNED ) );
  10172. if ( isRifle && isTeamAppropriate )
  10173. {
  10174. primaryweapons.AddToTail( w );
  10175. }
  10176. else if ( isPistol && isTeamAppropriate )
  10177. {
  10178. secondaryweapons.AddToTail( w );
  10179. }
  10180. // Msg( "%i, %s, %s, %i\n", w, pWeaponInfo->szClassName, ( isRifle ? "primary" : ( isPistol ? "secondary" : "other" ) ), isTeamAppropriate );
  10181. }
  10182. // else
  10183. // {
  10184. // Msg( "%i, %s\n", w, "*********DOESN'T EXIST" );
  10185. // }
  10186. }
  10187. // randomly pick one of each.
  10188. int primaryToBuy = random->RandomInt( 1, primaryweapons.Count() );
  10189. int secondaryToBuy = random->RandomInt( 1, secondaryweapons.Count() );
  10190. // Msg( "random pick: p: %i, s: %i", primaryweapons[primaryToBuy], secondaryweapons[secondaryToBuy] );
  10191. // buy
  10192. // TODO: get itemid
  10193. HandleCommand_Buy( WeaponIDToAlias( primaryweapons[primaryToBuy - 1] ) );
  10194. HandleCommand_Buy( WeaponIDToAlias( secondaryweapons[secondaryToBuy - 1] ) );
  10195. m_bIsInAutoBuy = false;
  10196. }
  10197. bool CCSPlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps )
  10198. {
  10199. // High priority entities go through a different use code path requiring
  10200. // other conditions like distance and view angles to be satisfied
  10201. CConfigurationForHighPriorityUseEntity_t cfgUseEntity;
  10202. if ( GetUseConfigurationForHighPriorityUseEntity( pEntity, cfgUseEntity ) )
  10203. return false;
  10204. CWeaponCSBase *pCSWepaon = dynamic_cast<CWeaponCSBase*>(pEntity );
  10205. if( pCSWepaon )
  10206. {
  10207. // we can't USE dropped weapons
  10208. return true;
  10209. }
  10210. CBaseCSGrenadeProjectile *pGrenade = dynamic_cast<CBaseCSGrenadeProjectile*>(pEntity );
  10211. if ( pGrenade )
  10212. {
  10213. // we can't USE thrown grenades
  10214. }
  10215. CPropVehicle *pVehicle = dynamic_cast<CPropVehicle*>(pEntity );
  10216. if ( pVehicle )
  10217. {
  10218. return true;
  10219. }
  10220. return BaseClass::IsUseableEntity( pEntity, requiredCaps );
  10221. }
  10222. CBaseEntity *CCSPlayer::FindUseEntity()
  10223. {
  10224. CBaseEntity *entity = NULL;
  10225. // Check to see if the bomb is close enough to use before attempting to use anything else.
  10226. entity = GetUsableHighPriorityEntity();
  10227. if ( entity== NULL && !CSGameRules()->IsPlayingGunGame() && !CSGameRules()->IsPlayingTraining() )
  10228. {
  10229. Vector aimDir;
  10230. AngleVectors( EyeAngles(), &aimDir );
  10231. trace_t result;
  10232. UTIL_TraceLine( EyePosition(), EyePosition() + MAX_WEAPON_NAME_POPUP_RANGE * aimDir, MASK_ALL, this, COLLISION_GROUP_NONE, &result );
  10233. if ( result.DidHitNonWorldEntity() && result.m_pEnt->IsBaseCombatWeapon() )
  10234. {
  10235. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >( result.m_pEnt );
  10236. CSWeaponType nType = pWeapon->GetWeaponType();
  10237. if ( CSGameRules()->IsPlayingCoopMission() || IsPrimaryOrSecondaryWeapon( nType ) )
  10238. {
  10239. entity = pWeapon;
  10240. }
  10241. }
  10242. }
  10243. if ( entity == NULL )
  10244. {
  10245. entity = BaseClass::FindUseEntity();
  10246. }
  10247. return entity;
  10248. }
  10249. void CCSPlayer::StockPlayerAmmo( CBaseCombatWeapon *pNewWeapon )
  10250. {
  10251. // this function not only gives extra ammo, but also the needed default ammo for the default clip
  10252. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >( pNewWeapon );
  10253. if ( pWeapon )
  10254. {
  10255. if ( pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE )
  10256. return;
  10257. int nAmmo = pWeapon->GetPrimaryAmmoType();
  10258. if ( nAmmo != -1 )
  10259. {
  10260. if ( !CSGameRules()->IsPlayingTraining() )
  10261. pWeapon->SetReserveAmmoCount( AMMO_POSITION_PRIMARY, 9999 );
  10262. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  10263. }
  10264. return;
  10265. }
  10266. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  10267. if ( pWeapon )
  10268. {
  10269. int nAmmo = pWeapon->GetPrimaryAmmoType();
  10270. if ( nAmmo != -1 )
  10271. {
  10272. if ( !CSGameRules()->IsPlayingTraining() )
  10273. pWeapon->SetReserveAmmoCount( AMMO_POSITION_PRIMARY, 9999 );
  10274. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  10275. }
  10276. }
  10277. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  10278. if ( pWeapon )
  10279. {
  10280. int nAmmo = pWeapon->GetPrimaryAmmoType();
  10281. if ( nAmmo != -1 )
  10282. {
  10283. if ( !CSGameRules()->IsPlayingTraining() )
  10284. pWeapon->SetReserveAmmoCount( AMMO_POSITION_PRIMARY, 9999 );
  10285. pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
  10286. }
  10287. }
  10288. }
  10289. void CCSPlayer::FindMatchingWeaponsForTeamLoadout( const char *pchName, int nTeam, bool bMustBeTeamSpecific, CUtlVector< CEconItemView* > &matchingWeapons )
  10290. {
  10291. /** Removed for partner depot **/
  10292. }
  10293. CBaseEntity *CCSPlayer::GiveNamedItem( const char *pchName, int iSubType /*= 0*/, CEconItemView *pScriptItem /*= NULL*/, bool bForce /*= false*/ )
  10294. {
  10295. if ( !pchName || !pchName[0] )
  10296. return NULL;
  10297. CBaseEntity *pItem = NULL;
  10298. if ( ( !pScriptItem || !pScriptItem->IsValid() ) && !( CSGameRules() && CSGameRules()->IsPlayingTraining() ) )
  10299. {
  10300. CUtlVector< CEconItemView* > matchingWeapons;
  10301. FindMatchingWeaponsForTeamLoadout( pchName, GetTeamNumber(), false, matchingWeapons );
  10302. if ( matchingWeapons.Count() )
  10303. {
  10304. pScriptItem = matchingWeapons[ RandomInt( 0, matchingWeapons.Count() - 1 ) ];
  10305. }
  10306. else if ( CSGameRules() && CSGameRules()->IsPlayingGunGame() )
  10307. {
  10308. // In gun game it can give the painted version of team specific items
  10309. FindMatchingWeaponsForTeamLoadout( pchName, ( GetTeamNumber() != TEAM_TERRORIST ? TEAM_TERRORIST : TEAM_CT ), true, matchingWeapons );
  10310. if ( matchingWeapons.Count() )
  10311. {
  10312. pScriptItem = matchingWeapons[ RandomInt( 0, matchingWeapons.Count() - 1 ) ];
  10313. }
  10314. }
  10315. }
  10316. #if !defined( NO_STEAM_GAMECOORDINATOR )
  10317. if ( pScriptItem && pScriptItem->IsValid() )
  10318. {
  10319. // Generate a weapon directly from that item
  10320. pItem = ItemGeneration()->GenerateItemFromScriptData( pScriptItem, GetLocalOrigin(), vec3_angle, pScriptItem->GetStaticData()->GetItemClass() );
  10321. }
  10322. else
  10323. {
  10324. // Generate a base item of the specified type
  10325. CItemSelectionCriteria criteria;
  10326. criteria.SetQuality( AE_NORMAL );
  10327. criteria.BAddCondition( "name", k_EOperator_String_EQ, pchName, true );
  10328. pItem = ItemGeneration()->GenerateRandomItem( &criteria, GetAbsOrigin(), vec3_angle );
  10329. if ( !pItem )
  10330. {
  10331. criteria.SetQuality( AE_UNIQUE );
  10332. pItem = ItemGeneration()->GenerateRandomItem( &criteria, GetAbsOrigin(), vec3_angle );
  10333. }
  10334. }
  10335. #endif
  10336. if ( pItem == NULL )
  10337. {
  10338. // Trap for guns that 'exist' but never shipped.
  10339. // Doing this here rather than removing the entities
  10340. // so we don't disrupt demos.
  10341. if ( V_strcmp( pchName, "weapon_galil" ) &&
  10342. V_strcmp( pchName, "weapon_mp5navy" ) &&
  10343. V_strcmp( pchName, "weapon_p228" ) &&
  10344. V_strcmp( pchName, "weapon_scar17" ) &&
  10345. V_strcmp( pchName, "weapon_scout" ) &&
  10346. V_strcmp( pchName, "weapon_sg550" ) &&
  10347. V_strcmp( pchName, "weapon_tmp" ) &&
  10348. V_strcmp( pchName, "weapon_usp" ) )
  10349. {
  10350. pItem = CreateEntityByName( pchName );
  10351. }
  10352. if ( pItem == NULL )
  10353. {
  10354. Msg( "NULL Ent in GiveNamedItem!\n" );
  10355. return NULL;
  10356. }
  10357. // Msg( "%s is missing an item definition in the schema.\n", pchName );
  10358. }
  10359. Vector pos = ( GetLocalOrigin() + Weapon_ShootPosition() ) * 0.5f;
  10360. QAngle angles;
  10361. MDLCACHE_CRITICAL_SECTION();
  10362. int weaponBoneAttachment = LookupAttachment( "weapon_hand_R" );
  10363. if ( weaponBoneAttachment == 0 )
  10364. weaponBoneAttachment = LookupAttachment( "weapon_bone" );
  10365. if ( weaponBoneAttachment == 0 || !GetAttachment( weaponBoneAttachment, pos, angles ) )
  10366. {
  10367. Warning("Missing weapon hand bone attachment for player model.\n");
  10368. }
  10369. pItem->SetLocalOrigin( pos );
  10370. pItem->AddSpawnFlags( SF_NORESPAWN );
  10371. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( pItem );
  10372. if ( pWeapon )
  10373. {
  10374. if ( iSubType )
  10375. {
  10376. pWeapon->SetSubType( iSubType );
  10377. }
  10378. }
  10379. // is this item prohibited by mp_weapons_disallowed
  10380. if ( CSGameRules() )
  10381. {
  10382. int nDefIndex = 0;
  10383. if ( pScriptItem )
  10384. {
  10385. nDefIndex = pScriptItem->GetItemDefinition()->GetDefinitionIndex();
  10386. }
  10387. else
  10388. {
  10389. CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pItem->GetClassname() );
  10390. if ( pItemDef )
  10391. {
  10392. nDefIndex = pItemDef->GetDefinitionIndex();
  10393. }
  10394. }
  10395. if ( nDefIndex != 0 )
  10396. {
  10397. for ( int i = 0; i < MAX_PROHIBITED_ITEMS; i++ )
  10398. {
  10399. if ( CSGameRules()->m_arrProhibitedItemIndices[ i ] == nDefIndex )
  10400. return NULL;
  10401. }
  10402. }
  10403. }
  10404. DispatchSpawn( pItem );
  10405. m_bIsBeingGivenItem = true;
  10406. if ( pItem != NULL && !(pItem->IsMarkedForDeletion() ) )
  10407. {
  10408. CItem *pItemEnt = dynamic_cast< CItem* >( pItem );
  10409. if ( pItemEnt )
  10410. {
  10411. pItemEnt->ItemForceTouch( this );
  10412. }
  10413. else
  10414. {
  10415. pItem->Touch( this );
  10416. }
  10417. }
  10418. m_bIsBeingGivenItem = false;
  10419. // this function not only gives extra ammo, but also the needed default ammo for the default clip
  10420. StockPlayerAmmo( pWeapon );
  10421. if ( StringHasPrefix( pchName, "weapon_molotov" ) )
  10422. {
  10423. // Set up molotov use time since the molotov cannot be used right away
  10424. ConVarRef mp_molotovusedelay( "mp_molotovusedelay" );
  10425. m_fMolotovUseTime = gpGlobals->curtime + mp_molotovusedelay.GetFloat();
  10426. }
  10427. // Send a game event for getting the c4
  10428. if ( StringHasPrefix( pchName, "weapon_c4" ) )
  10429. {
  10430. IGameEvent *event = gameeventmanager->CreateEvent( "player_given_c4" );
  10431. if ( event )
  10432. {
  10433. event->SetInt( "userid", GetUserID() );
  10434. gameeventmanager->FireEvent( event );
  10435. }
  10436. }
  10437. #if WEARABLE_VEST_IFF_KEVLAR
  10438. if ( ( StringHasPrefix( pchName, "item_kevlar" ) ) ||
  10439. ( StringHasPrefix( pchName, "item_heavyassaultsuit" ) ) ||
  10440. ( StringHasPrefix( pchName, "item_assaultsuit" ) ) )
  10441. {
  10442. GiveWearableFromSlot( LOADOUT_POSITION_VEST );
  10443. }
  10444. #endif
  10445. return pItem;
  10446. }
  10447. bool CCSPlayer::CanUseGrenade( CSWeaponID nID )
  10448. {
  10449. if ( nID == WEAPON_MOLOTOV )
  10450. {
  10451. if ( gpGlobals->curtime < m_fMolotovUseTime )
  10452. {
  10453. // Can't use molotov until timer elapses
  10454. return false;
  10455. }
  10456. }
  10457. return true;
  10458. }
  10459. void CCSPlayer::DoAnimStateEvent( PlayerAnimEvent_t evt )
  10460. {
  10461. m_PlayerAnimState->DoAnimationEvent( evt );
  10462. }
  10463. void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  10464. {
  10465. if ( m_bUseNewAnimstate )
  10466. {
  10467. // run the event on the server
  10468. m_PlayerAnimStateCSGO->DoAnimationEvent( event, nData );
  10469. return;
  10470. }
  10471. else
  10472. {
  10473. if ( event == PLAYERANIMEVENT_THROW_GRENADE )
  10474. {
  10475. // Grenade throwing has to synchronize exactly with the player's grenade weapon going away,
  10476. // and events get delayed a bit, so we let CCSPlayerAnimState pickup the change to this
  10477. // variable.
  10478. m_iThrowGrenadeCounter = (m_iThrowGrenadeCounter+1 ) % (1<<THROWGRENADE_COUNTER_BITS );
  10479. }
  10480. else
  10481. {
  10482. m_PlayerAnimState->DoAnimationEvent( event, nData );
  10483. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  10484. }
  10485. }
  10486. }
  10487. //-----------------------------------------------------------------------------
  10488. //-----------------------------------------------------------------------------
  10489. int CCSPlayer::FlashlightIsOn( void )
  10490. {
  10491. return IsEffectActive( EF_DIMLIGHT );
  10492. }
  10493. extern ConVar flashlight;
  10494. //-----------------------------------------------------------------------------
  10495. //-----------------------------------------------------------------------------
  10496. bool CCSPlayer::FlashlightTurnOn( bool playSound /*= false*/ )
  10497. {
  10498. if( flashlight.GetInt() > 0 && IsAlive() )
  10499. {
  10500. AddEffects( EF_DIMLIGHT );
  10501. EmitSound( "Player.FlashlightOn" );
  10502. }
  10503. else
  10504. {
  10505. engine->ClientCommand( edict(), "+lookatweapon\n" );
  10506. engine->ClientCommand( edict(), "-lookatweapon\n" );
  10507. }
  10508. return true;
  10509. }
  10510. //-----------------------------------------------------------------------------
  10511. //-----------------------------------------------------------------------------
  10512. void CCSPlayer::FlashlightTurnOff( bool playSound /*= false*/ )
  10513. {
  10514. RemoveEffects( EF_DIMLIGHT );
  10515. if( IsAlive() )
  10516. {
  10517. EmitSound( "Player.FlashlightOff" );
  10518. }
  10519. }
  10520. void CCSPlayer::SetViewModelArms( const char *armsModel )
  10521. {
  10522. V_strncpy( m_szArmsModel.GetForModify(), armsModel, MAX_MODEL_STRING_SIZE );
  10523. }
  10524. void CCSPlayer::ReportCustomClothingModels( void )
  10525. {
  10526. if ( !CSInventoryManager() )
  10527. return;
  10528. for ( int nSlot = LOADOUT_POSITION_FIRST_COSMETIC; nSlot <= LOADOUT_POSITION_LAST_COSMETIC; ++nSlot )
  10529. {
  10530. #if !defined(NO_STEAM)
  10531. if ( steamgameserverapicontext->SteamGameServer() )
  10532. {
  10533. CSteamID steamIDForPlayer;
  10534. if ( GetSteamID( &steamIDForPlayer ) )
  10535. {
  10536. CEconItemView *pItemData = CSInventoryManager()->GetItemInLoadoutForTeam( GetTeamNumber(), nSlot, &steamIDForPlayer );
  10537. const char *pszItemName = pItemData->GetStaticData()->GetDefinitionName();
  10538. DevMsg( " %s: %s \n", g_szLoadoutStrings[ nSlot ], pszItemName );
  10539. }
  10540. }
  10541. }
  10542. #endif
  10543. }
  10544. bool CCSPlayer::HandleDropWeapon( CBaseCombatWeapon *pWeapon, bool bSwapping )
  10545. {
  10546. CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon ? pWeapon : GetActiveWeapon() );
  10547. if( pCSWeapon )
  10548. {
  10549. /*
  10550. CBaseCarribleItem *pItem = dynamic_cast< CBaseCarribleItem * >( pCSWeapon );
  10551. if ( pItem )
  10552. {
  10553. pItem->DropItem();
  10554. // decrement the ammo
  10555. pItem->DecrementAmmo( this );
  10556. // if that was the last item, delete this one
  10557. if ( pItem->GetCurrentItems() <= 0 )
  10558. {
  10559. CSWeaponDrop( pItem, true, true );
  10560. UTIL_Remove( pItem );
  10561. UpdateAddonBits();
  10562. }
  10563. return false;
  10564. }
  10565. */
  10566. if ( mp_death_drop_c4.GetBool() == 0 && pCSWeapon->IsA( WEAPON_C4 ) )
  10567. return true;
  10568. if ( mp_death_drop_gun.GetInt() == 0 && !pCSWeapon->IsA( WEAPON_C4 ) )
  10569. return true;
  10570. // [dwenger] Determine value of dropped item.
  10571. if ( !pCSWeapon->IsAPriorOwner( this ) )
  10572. {
  10573. pCSWeapon->AddPriorOwner( this );
  10574. CCS_GameStats.IncrementStat(this, CSTAT_ITEMS_DROPPED_VALUE, pCSWeapon->GetWeaponPrice() );
  10575. }
  10576. CEconItemView *pItem = pCSWeapon->GetEconItemView();
  10577. if ( pCSWeapon->IsA( WEAPON_HEALTHSHOT ) )
  10578. {
  10579. CItem_Healthshot* pHealth = dynamic_cast< CItem_Healthshot* >( pCSWeapon );
  10580. if ( pHealth )
  10581. {
  10582. pHealth->DropHealthshot();
  10583. ClientPrint( this, HUD_PRINTCENTER, "#SFUI_Notice_YouDroppedWeapon", pCSWeapon->GetPrintName() );
  10584. }
  10585. return true;
  10586. }
  10587. CSWeaponType type = pCSWeapon->GetWeaponType();
  10588. switch ( type )
  10589. {
  10590. // Only certail weapons can be dropped when drop is initiated by player
  10591. case WEAPONTYPE_PISTOL:
  10592. case WEAPONTYPE_SUBMACHINEGUN:
  10593. case WEAPONTYPE_RIFLE:
  10594. case WEAPONTYPE_SHOTGUN:
  10595. case WEAPONTYPE_SNIPER_RIFLE:
  10596. case WEAPONTYPE_MACHINEGUN:
  10597. case WEAPONTYPE_C4:
  10598. {
  10599. if (CSGameRules()->GetCanDonateWeapon() && !pCSWeapon->GetDonated() )
  10600. {
  10601. pCSWeapon->SetDonated(true );
  10602. pCSWeapon->SetDonor(this );
  10603. }
  10604. CSWeaponDrop( pCSWeapon, true, true );
  10605. if ( IsAlive() && !bSwapping )
  10606. ClientPrint( this, HUD_PRINTCENTER, "#SFUI_Notice_YouDroppedWeapon", ( pItem ? pItem->GetItemDefinition()->GetItemBaseName() : pCSWeapon->GetPrintName() ) );
  10607. }
  10608. break;
  10609. default:
  10610. {
  10611. // let dedicated servers optionally allow droppable knives
  10612. if ( type == WEAPONTYPE_KNIFE && mp_drop_knife_enable.GetBool( ) )
  10613. {
  10614. if ( CSGameRules( )->GetCanDonateWeapon( ) && !pCSWeapon->GetDonated( ) )
  10615. {
  10616. pCSWeapon->SetDonated( true );
  10617. pCSWeapon->SetDonor( this );
  10618. }
  10619. CSWeaponDrop( pCSWeapon, true, true );
  10620. if ( IsAlive( ) && !bSwapping )
  10621. ClientPrint( this, HUD_PRINTCENTER, "#SFUI_Notice_YouDroppedWeapon", ( pItem ? pItem->GetItemDefinition( )->GetItemBaseName( ) : pCSWeapon->GetPrintName( ) ) );
  10622. }
  10623. else if ( IsAlive( ) && !bSwapping )
  10624. {
  10625. ClientPrint( this, HUD_PRINTCENTER, "#SFUI_Notice_CannotDropWeapon", ( pItem ? pItem->GetItemDefinition( )->GetItemBaseName( ) : pCSWeapon->GetPrintName( ) ) );
  10626. }
  10627. }
  10628. break;
  10629. }
  10630. return true;
  10631. }
  10632. return false;
  10633. }
  10634. void CCSPlayer::DestroyWeapon( CBaseCombatWeapon *pWeapon )
  10635. {
  10636. if ( pWeapon )
  10637. {
  10638. pWeapon->DestroyItem();
  10639. }
  10640. }
  10641. void CCSPlayer::DestroyWeapons( bool bDropC4 /* = true */ )
  10642. {
  10643. // Destroy the Defuser
  10644. if( HasDefuser() && mp_death_drop_defuser.GetBool() )
  10645. {
  10646. RemoveDefuser();
  10647. }
  10648. CBaseCombatWeapon *pWeapon = NULL;
  10649. // Destroy the primary weapon if it exists
  10650. pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
  10651. DestroyWeapon( pWeapon );
  10652. // Destroy the secondary weapon if it exists
  10653. pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
  10654. DestroyWeapon( pWeapon );
  10655. CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
  10656. if ( CSGameRules()->IsPlayingCoopMission() )
  10657. {
  10658. if ( pC4 )
  10659. DestroyWeapon( pC4 );
  10660. }
  10661. // Destroy any grenades
  10662. const char* GrenadePriorities[] =
  10663. {
  10664. "weapon_molotov",
  10665. "weapon_incgrenade",
  10666. "weapon_smokegrenade",
  10667. "weapon_hegrenade",
  10668. "weapon_flashbang",
  10669. "weapon_tagrenade",
  10670. "weapon_decoy",
  10671. };
  10672. CBaseCSGrenade *pGrenade = NULL;
  10673. for ( int i = 0; i < ARRAYSIZE(GrenadePriorities ); ++i )
  10674. {
  10675. pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType(GrenadePriorities[i] ) );
  10676. if ( pGrenade && pGrenade->HasAmmo() )
  10677. {
  10678. pGrenade->DestroyItem();
  10679. }
  10680. }
  10681. if ( bDropC4 && pC4 )
  10682. {
  10683. // Drop the C4
  10684. SetBombDroppedTime( gpGlobals->curtime );
  10685. CSWeaponDrop( pC4, false, true );
  10686. }
  10687. }
  10688. //Drop the appropriate weapons:
  10689. // Defuser if we have one
  10690. // C4 if we have one
  10691. // The best weapon we have, first check primary,
  10692. // then secondary and drop the best one
  10693. void CCSPlayer::DropWeapons( bool fromDeath, bool killedByEnemy )
  10694. {
  10695. CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
  10696. if ( pC4 )
  10697. {
  10698. if ( mp_death_drop_c4.GetBool() == 1 )
  10699. {
  10700. SetBombDroppedTime( gpGlobals->curtime );
  10701. CSWeaponDrop( pC4, false, true );
  10702. if ( fromDeath )
  10703. {
  10704. if ( killedByEnemy )
  10705. {
  10706. ( static_cast< CC4* > ( pC4 ) )->SetDroppedFromDeath( true );
  10707. }
  10708. }
  10709. }
  10710. }
  10711. if( HasDefuser() && mp_death_drop_defuser.GetBool() )
  10712. {
  10713. if ( !CSGameRules()->IsWarmupPeriod() )
  10714. {
  10715. //Drop an item_defuser
  10716. Vector vForward, vRight;
  10717. AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
  10718. CBaseAnimating *pDefuser = NULL;
  10719. if ( CSGameRules()->IsHostageRescueMap() )
  10720. pDefuser = (CBaseAnimating * )CBaseEntity::Create( "item_cutters", WorldSpaceCenter(), GetLocalAngles(), NULL );
  10721. else
  10722. pDefuser = (CBaseAnimating * )CBaseEntity::Create( "item_defuser", WorldSpaceCenter(), GetLocalAngles(), NULL );
  10723. pDefuser->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );
  10724. }
  10725. RemoveDefuser();
  10726. }
  10727. if ( HasShield() )
  10728. {
  10729. DropShield();
  10730. }
  10731. if ( mp_death_drop_gun.GetInt() != 0 )
  10732. {
  10733. CWeaponCSBase* pWeapon = NULL;
  10734. if ( mp_death_drop_gun.GetInt() == 2 )
  10735. {
  10736. pWeapon = GetActiveCSWeapon();
  10737. if ( pWeapon && !(pWeapon->GetSlot() == WEAPON_SLOT_PISTOL || pWeapon->GetSlot() == WEAPON_SLOT_RIFLE ) )
  10738. {
  10739. pWeapon = NULL;
  10740. }
  10741. }
  10742. if ( pWeapon == NULL )
  10743. {
  10744. //drop the best weapon we have
  10745. if( !DropWeaponSlot( WEAPON_SLOT_RIFLE, true ) )
  10746. DropWeaponSlot( WEAPON_SLOT_PISTOL, true );
  10747. }
  10748. }
  10749. // wills: note - this may seem counter-intuitive below,
  10750. // but the player can only play grenade-related animations
  10751. // (like throwing) while 'holding' the grenade WEAPON. This means
  10752. // it's possible to still be holding the grenade WEAPON even
  10753. // after the actual grenade itself is flying away, so the
  10754. // player's throw anim can smoothly finish. That's why we need
  10755. // to check if the grenade has emitted a projectile; we don't
  10756. // want to drop a duplicate of the thrown grenade if we're killed
  10757. // AFTER the grenade is in flight but BEFORE the throw anim is over.
  10758. bool bGrenadeDropped = false;
  10759. // drop any live grenades so they explode
  10760. CBaseCSGrenade *pGrenade = dynamic_cast< CBaseCSGrenade * >(GetActiveCSWeapon() );
  10761. if ( pGrenade && pGrenade->HasEmittedProjectile() )
  10762. pGrenade = NULL; // the currently active grenade weapon, while active, is NOT eligible to drop because it has thrown a projectile into the world.
  10763. if ( pGrenade )
  10764. {
  10765. if ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() )
  10766. {
  10767. // NOTE[pmf]: Molotov is excluded from this list. Consider making this a weapon property
  10768. if (
  10769. pGrenade->ClassMatches("weapon_hegrenade" ) ||
  10770. pGrenade->ClassMatches("weapon_flashbang" ) ||
  10771. pGrenade->ClassMatches("weapon_smokegrenade" ) ||
  10772. pGrenade->ClassMatches("weapon_decoy" ) )
  10773. {
  10774. pGrenade->DropGrenade();
  10775. pGrenade->DecrementAmmo( this );
  10776. bGrenadeDropped = true;
  10777. }
  10778. }
  10779. if ( mp_death_drop_grenade.GetInt() == 2 && !bGrenadeDropped )
  10780. {
  10781. // drop currently active grenade
  10782. bGrenadeDropped = CSWeaponDrop(pGrenade, false );
  10783. }
  10784. }
  10785. if ( mp_death_drop_grenade.GetInt() == 3 )
  10786. {
  10787. for ( int i = 0; i < MAX_WEAPONS; ++i )
  10788. {
  10789. CBaseCSGrenade *pCurGrenade = dynamic_cast< CBaseCSGrenade * >( GetWeapon( i ) );
  10790. if ( pCurGrenade && pCurGrenade->HasAmmo() && !pCurGrenade->HasEmittedProjectile() )
  10791. {
  10792. bGrenadeDropped = CSWeaponDrop( pCurGrenade, false );
  10793. }
  10794. }
  10795. }
  10796. else if ( mp_death_drop_grenade.GetInt() != 0 && !bGrenadeDropped )
  10797. {
  10798. // drop the "best" grenade remaining according to the following priorities
  10799. const char* GrenadePriorities[] =
  10800. {
  10801. "weapon_molotov", // first slot might get overridden by player last held grenade type below
  10802. "weapon_molotov",
  10803. "weapon_incgrenade",
  10804. "weapon_smokegrenade",
  10805. "weapon_hegrenade",
  10806. "weapon_flashbang",
  10807. "weapon_tagrenade",
  10808. "weapon_decoy",
  10809. };
  10810. switch ( m_nPreferredGrenadeDrop )
  10811. {
  10812. case WEAPON_FLASHBANG: GrenadePriorities[0] = "weapon_flashbang"; break;
  10813. case WEAPON_MOLOTOV: GrenadePriorities[0] = "weapon_molotov"; break;
  10814. case WEAPON_INCGRENADE: GrenadePriorities[0] = "weapon_incgrenade"; break;
  10815. case WEAPON_HEGRENADE: GrenadePriorities[0] = "weapon_hegrenade"; break;
  10816. case WEAPON_SMOKEGRENADE: GrenadePriorities[0] = "weapon_smokegrenade"; break;
  10817. case WEAPON_DECOY: GrenadePriorities[0] = "weapon_decoy"; break;
  10818. case WEAPON_TAGRENADE: GrenadePriorities[0] = "weapon_tagrenade"; break;
  10819. }
  10820. m_nPreferredGrenadeDrop = 0; // after we drop a preferred grenade make sure we reset the field
  10821. for ( int i = 0; ( i < ARRAYSIZE(GrenadePriorities ) ) && !bGrenadeDropped; ++i )
  10822. {
  10823. pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType(GrenadePriorities[i] ) );
  10824. if ( pGrenade && pGrenade->HasAmmo() && !pGrenade->HasEmittedProjectile() )
  10825. {
  10826. bGrenadeDropped = CSWeaponDrop(pGrenade, false );
  10827. }
  10828. }
  10829. }
  10830. /*
  10831. CBaseCarribleItem *pItem = dynamic_cast< CBaseCarribleItem * >( Weapon_OwnsThisType( "weapon_carriableitem" ) );
  10832. if ( pItem && pItem->HasAmmo() )
  10833. {
  10834. for ( int i = 1; i < pItem->Clip1(); ++i )
  10835. {
  10836. pItem->DropItem();
  10837. pItem->DecrementAmmo( this );
  10838. }
  10839. // drop the remaining item
  10840. CSWeaponDrop( pItem, false );
  10841. }
  10842. */
  10843. if ( m_hCarriedHostage != NULL && GetNumFollowers() > 0 )
  10844. {
  10845. CHostage *pHostage = dynamic_cast< CHostage * >( m_hCarriedHostage.Get() );
  10846. if ( pHostage )
  10847. pHostage->DropHostage( GetAbsOrigin() );
  10848. }
  10849. }
  10850. //-----------------------------------------------------------------------------
  10851. // Purpose: Put the player in the specified team
  10852. //-----------------------------------------------------------------------------
  10853. void CCSPlayer::ChangeTeam( int iTeamNum )
  10854. {
  10855. if ( !CSGameRules() )
  10856. return;
  10857. if ( !GetGlobalTeam( iTeamNum ) )
  10858. {
  10859. Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
  10860. return;
  10861. }
  10862. int iOldTeam = GetTeamNumber();
  10863. // if this is our current team, just abort
  10864. if ( iTeamNum == iOldTeam )
  10865. return;
  10866. /*
  10867. // [wills] Trying to squash T-pose orphaned wearables. Note: it isn't great to remove wearables all over the place,
  10868. // since it may trigger an unnecessary texture re-composite, which is potentially costly.
  10869. RemoveAllWearables();
  10870. */
  10871. if ( IsBot() && ( iTeamNum == TEAM_UNASSIGNED || iTeamNum == TEAM_SPECTATOR ) )
  10872. {
  10873. // Destroy weapons since bot is going away
  10874. DestroyWeapons();
  10875. }
  10876. else
  10877. {
  10878. // [tj] Added a parameter so we know if it was death that caused the drop
  10879. // Drop Our best weapon
  10880. DropWeapons(false, false );
  10881. }
  10882. // [tj] Clear out dominations
  10883. RemoveNemesisRelationships();
  10884. // Always allow a change to spectator, and don't count it as one of our team changes.
  10885. // We now store the old team, so if a player changes once to one team, then to spectator,
  10886. // they won't be able to change back to their old old team, but will still be able to join
  10887. // the team they initially changed to.
  10888. if( iTeamNum != TEAM_SPECTATOR )
  10889. {
  10890. // set the play time for the round since we won't be logging it after changing teams
  10891. CCS_GameStats.IncrementStat( this, CSSTAT_PLAYTIME, (int )CSGameRules()->GetRoundElapsedTime(), true );
  10892. m_bTeamChanged = true;
  10893. m_bJustBecameSpectator = false;
  10894. }
  10895. else
  10896. {
  10897. m_iOldTeam = iOldTeam;
  10898. m_bJustBecameSpectator = true;
  10899. }
  10900. // do the team change:
  10901. BaseClass::ChangeTeam( iTeamNum );
  10902. if ( !IsBot() )
  10903. {
  10904. if ( GetTeamNumber() != TEAM_UNASSIGNED )
  10905. {
  10906. m_iLastTeam = GetTeamNumber();
  10907. }
  10908. }
  10909. //reset class
  10910. m_iClass = (int )CS_CLASS_NONE;
  10911. // reset addons updates
  10912. m_iAddonBits = 0;
  10913. // update client state
  10914. if ( iTeamNum == TEAM_UNASSIGNED )
  10915. {
  10916. // Never let a player sit idle as TEAM_UNASSIGNED. Start the auto team select timer.
  10917. State_Transition( STATE_OBSERVER_MODE );
  10918. ResetForceTeamThink();
  10919. SetContextThink( &CBasePlayer::PlayerForceTeamThink, TICK_NEVER_THINK, CS_FORCE_TEAM_THINK_CONTEXT );
  10920. }
  10921. else if ( iTeamNum == TEAM_SPECTATOR )
  10922. {
  10923. //Reset money
  10924. ResetAccount();
  10925. RemoveAllItems( true );
  10926. State_Transition( STATE_OBSERVER_MODE );
  10927. }
  10928. else // active player
  10929. {
  10930. if ( iOldTeam == TEAM_SPECTATOR )
  10931. {
  10932. // If they're switching from being a spectator to ingame player
  10933. // [tj] Changed this so players either retain their existing money or,
  10934. // if they have less than the default, give them the default.
  10935. int startMoney = CSGameRules()->GetStartMoney();
  10936. if ( startMoney > GetAccountBalance() )
  10937. {
  10938. InitializeAccount( startMoney );
  10939. }
  10940. }
  10941. // bots get to this state on TEAM_UNASSIGNED, yet they are marked alive. Don't kill them.
  10942. else if ( iOldTeam != TEAM_UNASSIGNED && !IsDead() )
  10943. {
  10944. // Kill player if switching teams while alive
  10945. CommitSuicide();
  10946. }
  10947. // Put up the class selection menu.
  10948. HandleCommand_JoinClass();
  10949. // State_Transition( STATE_PICKINGCLASS );
  10950. }
  10951. // Initialize the player counts now that a player has switched teams
  10952. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  10953. CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  10954. if ( !IsBot() )
  10955. {
  10956. // Fire a generic team change event to say that someone changed teams.
  10957. // This is caught by the client, who then notifies their matchmaking framework
  10958. // with the proper data to update matchmaking properties based on team distribution.
  10959. IGameEvent *event = gameeventmanager->CreateEvent( "switch_team" );
  10960. if ( event )
  10961. {
  10962. int numPlayers = GetGlobalTeam( TEAM_UNASSIGNED )->GetNumPlayers();
  10963. int numSpectators = GetGlobalTeam( TEAM_SPECTATOR )->GetNumPlayers();
  10964. int numCTs = 0, numTs = 0;
  10965. CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST );
  10966. for ( int i=0; i<pTeam->GetNumPlayers(); ++i )
  10967. {
  10968. if ( !pTeam->GetPlayer( i )->IsBot() )
  10969. {
  10970. ++numPlayers;
  10971. ++numTs;
  10972. }
  10973. }
  10974. pTeam = GetGlobalTeam( TEAM_CT );
  10975. for ( int i=0; i<pTeam->GetNumPlayers(); ++i )
  10976. {
  10977. if ( !pTeam->GetPlayer( i )->IsBot() )
  10978. {
  10979. ++numPlayers;
  10980. ++numCTs;
  10981. }
  10982. }
  10983. event->SetInt( "numPlayers", numPlayers );
  10984. event->SetInt( "numSpectators", numSpectators );
  10985. CCSGameRules* pGameRules = CSGameRules();
  10986. event->SetInt( "numTSlotsFree", pGameRules->MaxNumPlayersOnTerrTeam() - numTs );
  10987. event->SetInt( "numCTSlotsFree", pGameRules->MaxNumPlayersOnCTTeam() - numCTs );
  10988. // let the client know the average skill rank
  10989. event->SetInt( "timeout", 0 );
  10990. gameeventmanager->FireEvent( event );
  10991. }
  10992. }
  10993. CSGameRules()->UpdateTeamClanNames( TEAM_TERRORIST );
  10994. CSGameRules()->UpdateTeamClanNames( TEAM_CT );
  10995. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  10996. if ( /*iTeamNum <= TEAM_SPECTATOR && */pResource )
  10997. {
  10998. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  10999. {
  11000. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11001. if ( pPlayer && pPlayer == this )
  11002. pResource->ResetPlayerTeammateColor( i );
  11003. }
  11004. }
  11005. }
  11006. //-----------------------------------------------------------------------------
  11007. // Purpose: Put the player in the specified team without penalty
  11008. //-----------------------------------------------------------------------------
  11009. void CCSPlayer::SwitchTeam( int iTeamNum )
  11010. {
  11011. if ( !GetGlobalTeam( iTeamNum ) || (iTeamNum != TEAM_CT && iTeamNum != TEAM_TERRORIST ) )
  11012. {
  11013. Warning( "CCSPlayer::SwitchTeam( %d ) - invalid team index.\n", iTeamNum );
  11014. return;
  11015. }
  11016. int iOldTeam = GetTeamNumber();
  11017. // if this is our current team, just abort
  11018. if ( iTeamNum == iOldTeam )
  11019. return;
  11020. // [wills] Trying to squash T-pose orphaned wearables. Note: it isn't great to remove wearables all over the place,
  11021. // since it may trigger an unnecessary texture re-composite, which is potentially costly.
  11022. // RemoveAllWearables();
  11023. // set the play time for the round since we won't be logging it after changing teams
  11024. CCS_GameStats.IncrementStat( this, CSSTAT_PLAYTIME, (int )CSGameRules()->GetRoundElapsedTime(), true );
  11025. // Always allow a change to spectator, and don't count it as one of our team changes.
  11026. // We now store the old team, so if a player changes once to one team, then to spectator,
  11027. // they won't be able to change back to their old old team, but will still be able to join
  11028. // the team they initially changed to.
  11029. m_bTeamChanged = true;
  11030. // do the team change:
  11031. BaseClass::ChangeTeam( iTeamNum );
  11032. if( HasDefuser() )
  11033. {
  11034. RemoveDefuser();
  11035. }
  11036. m_iClass = PlayerModelInfo::GetPtr()->GetNextClassForTeam( iTeamNum );
  11037. if ( IsControllingBot() )
  11038. {
  11039. // Switch team + controlling bot should reset the model class to the new one
  11040. m_PreControlData.m_iClass = m_iClass;
  11041. }
  11042. // Initialize the player counts now that a player has switched teams
  11043. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  11044. CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  11045. CSGameRules()->UpdateTeamClanNames( TEAM_TERRORIST );
  11046. CSGameRules()->UpdateTeamClanNames( TEAM_CT );
  11047. }
  11048. void CCSPlayer::ModifyOrAppendCriteria( AI_CriteriaSet& set )
  11049. {
  11050. BaseClass::ModifyOrAppendCriteria( set );
  11051. // Fix up the model name for rule matching
  11052. char modelName[MAX_PATH];
  11053. V_FileBase( STRING( GetModelName() ), modelName, sizeof( modelName ) );
  11054. char *pEnd = V_stristr( modelName, "_var" );
  11055. if ( pEnd )
  11056. *pEnd = 0;
  11057. int myteam = GetTeamNumber();
  11058. int otherTeam = ( myteam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
  11059. set.AppendCriteria( "team", myteam );
  11060. set.AppendCriteria( "model", modelName );
  11061. set.AppendCriteria( "liveallies", GetTeam()->GetAliveMembers() );
  11062. set.AppendCriteria( "liveenemies", GetGlobalTeam( otherTeam )->GetAliveMembers() );
  11063. }
  11064. void CCSPlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set )
  11065. {
  11066. // this is for giving player info to the hostage response system
  11067. // and is as yet unused.
  11068. // Eventually we could give the hostage a few tidbits about this player,
  11069. // eg their health, what weapons they have, and the hostage could
  11070. // comment accordingly.
  11071. //do not append any player data to the Criteria!
  11072. //we don't know which player we should be caring about
  11073. }
  11074. static uint32 s_BulletGroupCounter = 0;
  11075. void CCSPlayer::StartNewBulletGroup()
  11076. {
  11077. s_BulletGroupCounter++;
  11078. }
  11079. uint32 CCSPlayer::GetBulletGroup()
  11080. {
  11081. return s_BulletGroupCounter;
  11082. }
  11083. void CCSPlayer::ResetBulletGroup()
  11084. {
  11085. s_BulletGroupCounter = 0;
  11086. }
  11087. CDamageRecord::CDamageRecord( CCSPlayer * pPlayerDamager, CCSPlayer * pPlayerRecipient, int iDamage, int iCounter, int iActualHealthRemoved )
  11088. {
  11089. if ( pPlayerDamager )
  11090. {
  11091. m_PlayerDamager = pPlayerDamager;
  11092. m_PlayerDamagerControlledBot = pPlayerDamager->IsControllingBot() ? pPlayerDamager->GetControlledBot() : NULL;
  11093. Q_strncpy( m_szPlayerDamagerName, pPlayerDamager->GetPlayerName(), sizeof(m_szPlayerDamagerName) );
  11094. }
  11095. else
  11096. {
  11097. Q_strncpy( m_szPlayerDamagerName, "World", sizeof(m_szPlayerDamagerName) );
  11098. }
  11099. if ( pPlayerRecipient )
  11100. {
  11101. m_PlayerRecipient = pPlayerRecipient;
  11102. m_PlayerRecipientControlledBot = pPlayerRecipient->IsControllingBot() ? pPlayerRecipient->GetControlledBot() : NULL;
  11103. Q_strncpy( m_szPlayerRecipientName, pPlayerRecipient->GetPlayerName(), sizeof(m_szPlayerRecipientName) );
  11104. }
  11105. else
  11106. {
  11107. Q_strncpy( m_szPlayerRecipientName, "World", sizeof(m_szPlayerRecipientName) );
  11108. }
  11109. m_iDamage = iDamage;
  11110. m_iActualHealthRemoved = iActualHealthRemoved;
  11111. m_iNumHits = 1;
  11112. m_iLastBulletUpdate = iCounter;
  11113. }
  11114. bool CDamageRecord::IsDamageRecordStillValidForDamagerAndRecipient( CCSPlayer * pPlayerDamager, CCSPlayer * pPlayerRecipient )
  11115. {
  11116. if ( ( pPlayerDamager != m_PlayerDamager ) ||
  11117. ( pPlayerRecipient != m_PlayerRecipient ) ||
  11118. ( pPlayerDamager != NULL && pPlayerDamager->IsControllingBot() && m_PlayerDamagerControlledBot != pPlayerDamager->GetControlledBot() ) ||
  11119. ( pPlayerRecipient != NULL && pPlayerRecipient->IsControllingBot() && m_PlayerRecipientControlledBot != pPlayerRecipient->GetControlledBot() ) )
  11120. {
  11121. return false;
  11122. }
  11123. return true;
  11124. }
  11125. //=======================================================
  11126. // Remember this amount of damage that we dealt for stats
  11127. //=======================================================
  11128. void CCSPlayer::RecordDamage( CCSPlayer* damageDealer, CCSPlayer* damageTaker, int iDamageDealt, int iActualHealthRemoved )
  11129. {
  11130. FOR_EACH_LL( m_DamageList, i )
  11131. {
  11132. if ( m_DamageList[i]->IsDamageRecordStillValidForDamagerAndRecipient( damageDealer, damageTaker ) )
  11133. {
  11134. m_DamageList[i]->AddDamage( iDamageDealt, s_BulletGroupCounter, iActualHealthRemoved );
  11135. return;
  11136. }
  11137. }
  11138. CDamageRecord *record = new CDamageRecord( damageDealer, damageTaker, iDamageDealt, s_BulletGroupCounter, iActualHealthRemoved );
  11139. int k = m_DamageList.AddToTail();
  11140. m_DamageList[k] = record;
  11141. }
  11142. int CCSPlayer::GetNumAttackersFromDamageList( void )
  11143. {
  11144. //Doesn't distinguish friend or enemy, this will return total friendly and enemy attackers
  11145. int nTotalAttackers = 0;
  11146. FOR_EACH_LL( m_DamageList, i )
  11147. {
  11148. if ( m_DamageList[i]->GetPlayerDamagerPtr() && m_DamageList[i]->GetPlayerRecipientPtr() == this )
  11149. nTotalAttackers++;
  11150. }
  11151. return nTotalAttackers;
  11152. }
  11153. int CCSPlayer::GetMostNumHitsDamageRecordFrom( CCSPlayer *pAttacker )
  11154. {
  11155. int iNumHits = 0;
  11156. FOR_EACH_LL( m_DamageList, i )
  11157. {
  11158. if ( m_DamageList[i]->GetPlayerDamagerPtr() == pAttacker )
  11159. {
  11160. if ( m_DamageList[i]->GetNumHits() >= iNumHits )
  11161. iNumHits = m_DamageList[i]->GetNumHits();
  11162. }
  11163. }
  11164. return iNumHits;
  11165. }
  11166. //=======================================================
  11167. // Reset our damage given and taken counters
  11168. //=======================================================
  11169. void CCSPlayer::ResetDamageCounters()
  11170. {
  11171. m_DamageList.PurgeAndDeleteElements();
  11172. }
  11173. void CCSPlayer::RemoveSelfFromOthersDamageCounters()
  11174. {
  11175. // Now clear out any reference of this player in other players' damage lists.
  11176. CUtlVector< CCSPlayer * > playerVector;
  11177. CollectPlayers( &playerVector );
  11178. FOR_EACH_VEC( playerVector, i )
  11179. {
  11180. CCSPlayer *player = playerVector[ i ];
  11181. if ( playerVector[ i ] == this )
  11182. continue;
  11183. FOR_EACH_LL( player->m_DamageList, j )
  11184. {
  11185. if ( player->m_DamageList[j]->GetPlayerDamagerPtr() == this || player->m_DamageList[j]->GetPlayerRecipientPtr() == this )
  11186. {
  11187. delete player->m_DamageList[ j ];
  11188. player->m_DamageList.Remove( j );
  11189. break;
  11190. }
  11191. }
  11192. }
  11193. }
  11194. //=======================================================
  11195. // Output the damage that we dealt to other players
  11196. //=======================================================
  11197. void CCSPlayer::OutputDamageTaken( void )
  11198. {
  11199. if ( sv_damage_print_enable.GetBool() == false )
  11200. return;
  11201. bool bPrintHeader = true;
  11202. CDamageRecord *pRecord;
  11203. char buf[64];
  11204. int msg_dest = HUD_PRINTCONSOLE;
  11205. FOR_EACH_LL( m_DamageList, i )
  11206. {
  11207. if( bPrintHeader )
  11208. {
  11209. ClientPrint( this, msg_dest, "Player: %s1 - Damage Taken\n", GetPlayerName() );
  11210. ClientPrint( this, msg_dest, "-------------------------\n" );
  11211. bPrintHeader = false;
  11212. }
  11213. pRecord = m_DamageList[i];
  11214. if( pRecord && pRecord->GetPlayerRecipientPtr() == this )
  11215. {
  11216. if (pRecord->GetNumHits() == 1 )
  11217. {
  11218. Q_snprintf( buf, sizeof(buf ), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
  11219. }
  11220. else
  11221. {
  11222. Q_snprintf( buf, sizeof(buf ), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
  11223. }
  11224. ClientPrint( this, msg_dest, "Damage Taken from \"%s1\" - %s2\n", pRecord->GetPlayerDamagerName(), buf );
  11225. }
  11226. }
  11227. }
  11228. //=======================================================
  11229. // Output the damage that we took from other players
  11230. //=======================================================
  11231. void CCSPlayer::OutputDamageGiven( void )
  11232. {
  11233. int nDamageGivenThisRound = 0;
  11234. //CDamageRecord *pDamageList = pAttacker->GetDamageGivenList();
  11235. FOR_EACH_LL( m_DamageList, i )
  11236. {
  11237. if ( m_DamageList[i]->GetPlayerDamagerPtr() &&
  11238. m_DamageList[i]->GetPlayerDamagerPtr() == this &&
  11239. m_DamageList[i]->GetPlayerRecipientPtr() &&
  11240. m_DamageList[i]->GetPlayerRecipientPtr() != this &&
  11241. IsOtherSameTeam( m_DamageList[i]->GetPlayerRecipientPtr()->GetTeamNumber() ) &&
  11242. !IsOtherEnemy( m_DamageList[i]->GetPlayerRecipientPtr() ) )
  11243. {
  11244. nDamageGivenThisRound += m_DamageList[i]->GetActualHealthRemoved();
  11245. }
  11246. }
  11247. IncrementTeamDamagePoints( nDamageGivenThisRound );
  11248. m_bTDGaveProtectionWarningThisRound = false;
  11249. if ( sv_damage_print_enable.GetBool() == false )
  11250. return;
  11251. bool bPrintHeader = true;
  11252. CDamageRecord *pRecord;
  11253. char buf[64];
  11254. int msg_dest = HUD_PRINTCONSOLE;
  11255. FOR_EACH_LL( m_DamageList, i )
  11256. {
  11257. if( bPrintHeader )
  11258. {
  11259. ClientPrint( this, msg_dest, "Player: %s1 - Damage Given\n", GetPlayerName() );
  11260. ClientPrint( this, msg_dest, "-------------------------\n" );
  11261. bPrintHeader = false;
  11262. }
  11263. pRecord = m_DamageList[i];
  11264. if( pRecord && pRecord->GetPlayerDamagerPtr() == this )
  11265. {
  11266. if (pRecord->GetNumHits() == 1 )
  11267. {
  11268. Q_snprintf( buf, sizeof(buf ), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
  11269. }
  11270. else
  11271. {
  11272. Q_snprintf( buf, sizeof(buf ), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
  11273. }
  11274. ClientPrint( this, msg_dest, "Damage Given to \"%s1\" - %s2\n", pRecord->GetPlayerRecipientName(), buf );
  11275. }
  11276. }
  11277. }
  11278. void CCSPlayer::SendLastKillerDamageToClient( CCSPlayer *pLastKiller )
  11279. {
  11280. int nNumHitsGiven = 0;
  11281. int nDamageGiven = 0;
  11282. int nNumHitsTaken = 0;
  11283. int nDamageTaken = 0;
  11284. if ( sv_damage_print_enable.GetBool() )
  11285. {
  11286. FOR_EACH_LL( m_DamageList, i )
  11287. {
  11288. if( m_DamageList[i]->IsDamageRecordValidPlayerToPlayer() )
  11289. {
  11290. if ( m_DamageList[i]->IsDamageRecordStillValidForDamagerAndRecipient( this, pLastKiller ) )
  11291. {
  11292. nDamageGiven = m_DamageList[i]->GetDamage();
  11293. nNumHitsGiven = m_DamageList[i]->GetNumHits();
  11294. }
  11295. if ( m_DamageList[i]->IsDamageRecordStillValidForDamagerAndRecipient( pLastKiller, this ) )
  11296. {
  11297. nDamageTaken = m_DamageList[i]->GetDamage();
  11298. nNumHitsTaken = m_DamageList[i]->GetNumHits();
  11299. }
  11300. }
  11301. }
  11302. }
  11303. // Send a user message to the local player with the hits/damage data
  11304. //-----------------------------------------------------------------
  11305. CSingleUserRecipientFilter filter( this );
  11306. filter.MakeReliable();
  11307. CCSUsrMsg_SendLastKillerDamageToClient msg;
  11308. msg.set_num_hits_given( nNumHitsGiven );
  11309. msg.set_damage_given( nDamageGiven );
  11310. msg.set_num_hits_taken( nNumHitsTaken );
  11311. msg.set_damage_taken( nDamageTaken );
  11312. SendUserMessage( filter, CS_UM_SendLastKillerDamageToClient, msg );
  11313. //-----------------------------------------------------------------
  11314. }
  11315. void CCSPlayer::CreateViewModel( int index /*=0*/ )
  11316. {
  11317. Assert( index >= 0 && index < MAX_VIEWMODELS );
  11318. if ( GetViewModel( index ) )
  11319. return;
  11320. CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
  11321. if ( vm )
  11322. {
  11323. vm->SetAbsOrigin( GetAbsOrigin() );
  11324. vm->SetOwner( this );
  11325. vm->SetIndex( index );
  11326. DispatchSpawn( vm );
  11327. vm->FollowEntity( this, false );
  11328. m_hViewModel.Set( index, vm );
  11329. }
  11330. }
  11331. bool CCSPlayer::HasC4() const
  11332. {
  11333. return ( Weapon_OwnsThisType( "weapon_c4" ) != NULL );
  11334. }
  11335. int CCSPlayer::GetNextObserverSearchStartPoint( bool bReverse )
  11336. {
  11337. #if CS_CONTROLLABLE_BOTS_ENABLED
  11338. // Brock H. - TR - 05/05/09
  11339. // If the server is set up to allow controllable bots,
  11340. // and if we don't already have a target,
  11341. // then start with the nearest controllable bot.
  11342. if ( cv_bot_controllable.GetBool() )
  11343. {
  11344. if ( !IsValidObserverTarget( m_hObserverTarget.Get() ) )
  11345. {
  11346. if ( CCSBot *pBot = FindNearestControllableBot(true ) )
  11347. {
  11348. return pBot->entindex();
  11349. }
  11350. else if ( m_nLastKillerIndex > 0 )
  11351. {
  11352. return m_nLastKillerIndex;
  11353. }
  11354. }
  11355. }
  11356. #endif
  11357. // If we are currently watching someone who is dead, they must have died while we were watching (since
  11358. // a dead guy is not a valid pick to start watching ). He was given his killer as an observer target
  11359. // when he died, so let's start by trying to observe his killer. If we fail, we'll use the normal way.
  11360. // And this is just the start point anyway, but we want to start the search here in case it is okay.
  11361. if( m_hObserverTarget && !m_hObserverTarget->IsAlive() )
  11362. {
  11363. CCSPlayer *targetPlayer = ToCSPlayer(m_hObserverTarget );
  11364. if( targetPlayer && targetPlayer->GetObserverTarget() )
  11365. return targetPlayer->GetObserverTarget()->entindex();
  11366. }
  11367. return BaseClass::GetNextObserverSearchStartPoint( bReverse );
  11368. }
  11369. void CCSPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
  11370. {
  11371. BaseClass::PlayStepSound( vecOrigin, psurface, fvol, force );
  11372. if ( !sv_footsteps.GetFloat() )
  11373. return;
  11374. if ( !psurface )
  11375. return;
  11376. m_iFootsteps++;
  11377. IGameEvent * event = gameeventmanager->CreateEvent( "player_footstep" );
  11378. if ( event )
  11379. {
  11380. event->SetInt("userid", GetUserID() );
  11381. gameeventmanager->FireEvent( event );
  11382. }
  11383. m_bMadeFootstepNoise = true;
  11384. }
  11385. void CCSPlayer::ModifyTauntDuration( float flTimingChange )
  11386. {
  11387. m_flLookWeaponEndTime -= flTimingChange;
  11388. if ( !m_bUseNewAnimstate )
  11389. m_PlayerAnimState->ModifyTauntDuration( flTimingChange );
  11390. }
  11391. void CCSPlayer::SelectDeathPose( const CTakeDamageInfo &info )
  11392. {
  11393. MDLCACHE_CRITICAL_SECTION();
  11394. if ( !GetModelPtr() )
  11395. return;
  11396. Activity aActivity = ACT_INVALID;
  11397. int iDeathFrame = 0;
  11398. if ( m_bUseNewAnimstate && m_PlayerAnimStateCSGO )
  11399. {
  11400. float flDeathYaw = 0;
  11401. m_PlayerAnimStateCSGO->SelectDeathPose( info, m_LastHitGroup, aActivity, flDeathYaw );
  11402. SetDeathPoseYaw( flDeathYaw );
  11403. }
  11404. else
  11405. {
  11406. SelectDeathPoseActivityAndFrame( this, info, m_LastHitGroup, aActivity, iDeathFrame );
  11407. }
  11408. if ( aActivity == ACT_INVALID )
  11409. {
  11410. SetDeathPose( ACT_INVALID );
  11411. SetDeathPoseFrame( 0 );
  11412. return;
  11413. }
  11414. SetDeathPose( SelectWeightedSequence( aActivity ) );
  11415. SetDeathPoseFrame( iDeathFrame );
  11416. }
  11417. void CCSPlayer::HandleAnimEvent( animevent_t *pEvent )
  11418. {
  11419. if ( pEvent->Event() == 4001 || pEvent->Event() == 4002 )
  11420. {
  11421. // Ignore these for now - soon we will be playing footstep sounds based on these events
  11422. // that mark footfalls in the anims.
  11423. }
  11424. else if ( pEvent->Event() == AE_WPN_UNHIDE )
  11425. {
  11426. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  11427. if ( pWeapon && pWeapon->GetWeaponWorldModel() )
  11428. {
  11429. pWeapon->ShowWeaponWorldModel( true );
  11430. }
  11431. }
  11432. else if ( pEvent->Event() == AE_CL_EJECT_MAG || pEvent->Event() == AE_CL_EJECT_MAG_UNHIDE )
  11433. {
  11434. CAnimationLayer *pWeaponLayer = GetAnimOverlay( ANIMATION_LAYER_WEAPON_ACTION );
  11435. if ( pWeaponLayer && pWeaponLayer->m_nDispatchedDst != ACT_INVALID )
  11436. {
  11437. // If the weapon is running a dispatched animation, we can eat these events from the player.
  11438. // The weapon itself assumes the responsibility for these events when dispatched.
  11439. }
  11440. else
  11441. {
  11442. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  11443. if ( pWeapon && pWeapon->GetWeaponWorldModel() )
  11444. {
  11445. pWeapon->GetWeaponWorldModel()->HandleAnimEvent( pEvent );
  11446. }
  11447. }
  11448. }
  11449. else
  11450. {
  11451. BaseClass::HandleAnimEvent( pEvent );
  11452. }
  11453. }
  11454. bool CCSPlayer::CanChangeName( void )
  11455. {
  11456. if ( IsBot() )
  11457. return true;
  11458. // enforce the minimum interval
  11459. if ( (m_flNameChangeHistory[0] + MIN_NAME_CHANGE_INTERVAL ) >= gpGlobals->curtime )
  11460. {
  11461. return false;
  11462. }
  11463. // enforce that we dont do more than NAME_CHANGE_HISTORY_SIZE
  11464. // changes within NAME_CHANGE_HISTORY_INTERVAL
  11465. if ( (m_flNameChangeHistory[NAME_CHANGE_HISTORY_SIZE-1] + NAME_CHANGE_HISTORY_INTERVAL ) >= gpGlobals->curtime )
  11466. {
  11467. return false;
  11468. }
  11469. return true;
  11470. }
  11471. void CCSPlayer::ChangeName( const char *pszNewName )
  11472. {
  11473. // make sure name is not too long
  11474. char trimmedName[MAX_PLAYER_NAME_LENGTH];
  11475. Q_strncpy( trimmedName, pszNewName, sizeof( trimmedName ) );
  11476. const char *pszOldName = GetPlayerName();
  11477. if ( IsBot() && CSGameRules() && CSGameRules()->IsPlayingCooperativeGametype() )
  11478. {}
  11479. else
  11480. {
  11481. // send colored message to everyone
  11482. CReliableBroadcastRecipientFilter filter;
  11483. UTIL_SayText2Filter( filter, this, kEUtilSayTextMessageType_AllChat, "#Cstrike_Name_Change", pszOldName, trimmedName );
  11484. // broadcast event
  11485. IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
  11486. if ( event )
  11487. {
  11488. event->SetInt( "userid", GetUserID() );
  11489. event->SetString( "oldname", pszOldName );
  11490. event->SetString( "newname", trimmedName );
  11491. gameeventmanager->FireEvent( event );
  11492. }
  11493. }
  11494. // change shared player name
  11495. SetPlayerName( trimmedName );
  11496. if ( m_uiAccountId )
  11497. {
  11498. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  11499. {
  11500. Q_strncpy( pQMM->m_chPlayerName, this->GetPlayerName(), Q_ARRAYSIZE( pQMM->m_chPlayerName ) );
  11501. }
  11502. }
  11503. // tell engine to use new name
  11504. engine->ClientCommand( edict(), "name \"%s\"", GetPlayerName() );
  11505. // remember time of name change
  11506. for ( int i=NAME_CHANGE_HISTORY_SIZE-1; i>0; i-- )
  11507. {
  11508. m_flNameChangeHistory[i] = m_flNameChangeHistory[i-1];
  11509. }
  11510. m_flNameChangeHistory[0] = gpGlobals->curtime; // last change
  11511. }
  11512. bool CCSPlayer::StartReplayMode( float fDelay, float fDuration, int iEntity )
  11513. {
  11514. if ( !BaseClass::StartReplayMode( fDelay, fDuration, iEntity ) )
  11515. return false;
  11516. CSingleUserRecipientFilter filter( this );
  11517. filter.MakeReliable();
  11518. CCSUsrMsg_KillCam msg;
  11519. msg.set_obs_mode( OBS_MODE_IN_EYE );
  11520. if ( m_hObserverTarget.Get() )
  11521. {
  11522. msg.set_first_target( m_hObserverTarget.Get()->entindex() ); // first target
  11523. msg.set_second_target( entindex() ); //second target
  11524. }
  11525. else
  11526. {
  11527. msg.set_first_target( entindex() ); // first target
  11528. msg.set_second_target( 0 ); //second target
  11529. }
  11530. SendUserMessage( filter, CS_UM_KillCam, msg );
  11531. ClientPrint( this, HUD_PRINTCENTER, "Kill Cam Replay" );
  11532. return true;
  11533. }
  11534. #if HLTV_REPLAY_ENABLED
  11535. CON_COMMAND_F( replay_start, "Start GOTV replay: replay_start <delay> [<player name or index>]", FCVAR_CHEAT )
  11536. {
  11537. if ( CBasePlayer *pPlayer = UTIL_GetCommandClient() )
  11538. {
  11539. float flDelay = HLTV_MIN_DIRECTOR_DELAY + 1;
  11540. int nPlayerEntIndex = pPlayer->entindex(), nObserverEntIndex = nPlayerEntIndex;
  11541. if ( args.ArgC() > 1 )
  11542. {
  11543. flDelay = V_atof( args.Arg( 1 ) );
  11544. if ( args.ArgC() > 2 )
  11545. {
  11546. const char *pOtherPlayerName = args.Arg( 2 );
  11547. nObserverEntIndex = V_atoi( pOtherPlayerName );
  11548. if ( nObserverEntIndex )
  11549. {
  11550. if ( CBasePlayer *pObserverPlayer = UTIL_PlayerByIndex( nObserverEntIndex ) )
  11551. {
  11552. Msg( "replpaying from %s (%d) pov\n", pObserverPlayer->GetPlayerName(), nObserverEntIndex );
  11553. }
  11554. }
  11555. else
  11556. {
  11557. CBasePlayer *pObserverPlayer = UTIL_PlayerByName( pOtherPlayerName );
  11558. if ( pObserverPlayer )
  11559. {
  11560. nObserverEntIndex = pObserverPlayer->entindex();
  11561. Msg( "replpaying from %s (%d) pov\n", pObserverPlayer->GetPlayerName(), nObserverEntIndex );
  11562. }
  11563. }
  11564. }
  11565. }
  11566. HltvReplayParams_t params;
  11567. params.m_nPrimaryTargetEntIndex = nObserverEntIndex;
  11568. params.m_flDelay = flDelay;
  11569. engine->StartClientHltvReplay( nPlayerEntIndex - 1, params );
  11570. }
  11571. }
  11572. CON_COMMAND_F( replay_death, "start hltv replay of last death", FCVAR_CHEAT )
  11573. {
  11574. CBasePlayer *pBasePlayer = UTIL_GetCommandClient();
  11575. if ( !pBasePlayer )
  11576. return;
  11577. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pBasePlayer );
  11578. if ( !pPlayer )
  11579. return;
  11580. ClientReplayEventParams_t params( REPLAY_EVENT_DEATH ) ;
  11581. pPlayer->StartHltvReplayEvent( params );
  11582. }
  11583. CON_COMMAND_F( replay_stop, "stop hltv replay", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  11584. {
  11585. if ( CBasePlayer *pPlayer = UTIL_GetCommandClient() )
  11586. {
  11587. int nPlayerEntIndex = pPlayer->entindex();
  11588. engine->StopClientHltvReplay( nPlayerEntIndex - 1 );
  11589. }
  11590. }
  11591. #endif
  11592. void CCSPlayer::StopReplayMode()
  11593. {
  11594. BaseClass::StopReplayMode();
  11595. CSingleUserRecipientFilter filter( this );
  11596. filter.MakeReliable();
  11597. CCSUsrMsg_KillCam msg;
  11598. msg.set_obs_mode( OBS_MODE_NONE );
  11599. msg.set_first_target( 0 ); // first target
  11600. msg.set_second_target( 0 ); //second target
  11601. SendUserMessage( filter, CS_UM_KillCam, msg );
  11602. }
  11603. void CCSPlayer::PlayUseDenySound()
  11604. {
  11605. // Don't do a sound here because it can mute your footsteps giving you an advantage.
  11606. // The CS:S content for this sound is silent anyways.
  11607. //EmitSound( "Player.UseDeny" );
  11608. }
  11609. // [menglish, tj] This is where we reset all the per-round information for achievements for this player
  11610. void CCSPlayer::ResetRoundBasedAchievementVariables()
  11611. {
  11612. m_KillingSpreeStartTime = -1;
  11613. int numCTPlayers = 0, numTPlayers = 0;
  11614. for (int i = 0; i < g_Teams.Count(); i++ )
  11615. {
  11616. if(g_Teams[i] )
  11617. {
  11618. if ( g_Teams[i]->GetTeamNumber() == TEAM_CT )
  11619. numCTPlayers = g_Teams[i]->GetNumPlayers();
  11620. else if(g_Teams[i]->GetTeamNumber() == TEAM_TERRORIST )
  11621. numTPlayers = g_Teams[i]->GetNumPlayers();
  11622. }
  11623. }
  11624. m_NumEnemiesKilledThisRound = 0;
  11625. m_NumEnemiesKilledThisSpawn = 0;
  11626. m_maxNumEnemiesKillStreak = 0;
  11627. m_bLastKillUsedUniqueWeapon = false;
  11628. m_NumChickensKilledThisSpawn = 0;
  11629. if(GetTeamNumber() == TEAM_CT )
  11630. m_NumEnemiesAtRoundStart = numTPlayers;
  11631. else if(GetTeamNumber() == TEAM_TERRORIST )
  11632. m_NumEnemiesAtRoundStart = numCTPlayers;
  11633. //Clear the previous owner field for currently held weapons
  11634. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  11635. if ( pWeapon )
  11636. {
  11637. pWeapon->SetPreviousOwner(NULL );
  11638. }
  11639. pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  11640. if ( pWeapon )
  11641. {
  11642. pWeapon->SetPreviousOwner(NULL );
  11643. }
  11644. //Clear list of weapons used to get kills
  11645. m_killWeapons.RemoveAll();
  11646. //Clear sliding window of kill times
  11647. m_killTimes.RemoveAll();
  11648. //clear round kills
  11649. m_enemyPlayersKilledThisRound.RemoveAll();
  11650. m_killsWhileBlind = 0;
  11651. m_bombCarrierkills = 0;
  11652. m_knifeKillBombPlacer = false;
  11653. m_bSurvivedHeadshotDueToHelmet = false;
  11654. m_gooseChaseStep = GC_NONE;
  11655. m_defuseDefenseStep = DD_NONE;
  11656. m_pGooseChaseDistractingPlayer = NULL;
  11657. m_bMadeFootstepNoise = false;
  11658. m_knifeKillsWhenOutOfAmmo = 0;
  11659. m_attemptedBombPlace = false;
  11660. m_bombPickupTime = -1.0f;
  11661. m_bombPlacedTime = -1.0f;
  11662. m_bombDroppedTime = -1.0f;
  11663. m_killedTime = -1.0f;
  11664. m_spawnedTime = -1.0f;
  11665. m_longestLife = -1.0f;
  11666. m_triggerPulled = false;
  11667. m_triggerPulls = 0;
  11668. m_bMadePurchseThisRound = false;
  11669. m_bKilledDefuser = false;
  11670. m_bKilledRescuer = false;
  11671. m_maxGrenadeKills = 0;
  11672. m_grenadeDamageTakenThisRound = 0;
  11673. m_firstShotKills = 0;
  11674. m_hasReloaded = false;
  11675. WieldingKnifeAndKilledByGun(false );
  11676. SetWasKilledThisRound(false );
  11677. m_WeaponTypesUsed.RemoveAll();
  11678. m_WeaponTypesHeld.RemoveAll();
  11679. m_WeaponTypesRunningOutOfAmmo.RemoveAll();
  11680. m_BurnDamageDeltVec.RemoveAll();
  11681. m_bPickedUpDefuser = false;
  11682. m_bPickedUpWeapon = false;
  11683. m_bDefusedWithPickedUpKit = false;
  11684. m_bAttemptedDefusal = false;
  11685. m_flDefusedBombWithThisTimeRemaining = 0;
  11686. }
  11687. void CCSPlayer::HandleEndOfRound()
  11688. {
  11689. // store longest life time (for funfacts )
  11690. if( gpGlobals->curtime - m_spawnedTime > m_longestLife )
  11691. {
  11692. m_longestLife = gpGlobals->curtime - m_spawnedTime;
  11693. }
  11694. AllowImmediateDecalPainting();
  11695. RecordRebuyStructLastRound();
  11696. }
  11697. void CCSPlayer::RecordRebuyStructLastRound( void )
  11698. {
  11699. if ( !m_rebuyStruct.isEmpty() )
  11700. {
  11701. m_rebuyStructLastRound = m_rebuyStruct;
  11702. m_rebuyStruct.Clear();
  11703. }
  11704. }
  11705. void CCSPlayer::SetKilledTime( float time )
  11706. {
  11707. m_killedTime = time;
  11708. if( m_killedTime - m_spawnedTime > m_longestLife )
  11709. {
  11710. m_longestLife = m_killedTime - m_spawnedTime;
  11711. }
  11712. }
  11713. /**
  11714. * static public CCSPlayer::GetCSWeaponIDCausingDamange()
  11715. *
  11716. * Helper function to get the weapon info for the weapon causing damage.
  11717. * This is slightly non-trivial because when the damage is from a
  11718. * grenade, we don't have a pointer to the original weapon (and in any
  11719. * case, the object may no longer be around).
  11720. */
  11721. const CCSWeaponInfo* CCSPlayer::GetWeaponInfoFromDamageInfo( const CTakeDamageInfo &info )
  11722. {
  11723. CWeaponCSBase* pWeapon = dynamic_cast<CWeaponCSBase *>( info.GetWeapon() );
  11724. if ( pWeapon != NULL )
  11725. return &pWeapon->GetCSWpnData();
  11726. // if the inflictor is a grenade, we won't have a weapon in the damageinfo structure, but we can get the weaponinfo directly from the projectile
  11727. CBaseCSGrenadeProjectile* pGrenade = dynamic_cast<CBaseCSGrenadeProjectile *>( info.GetInflictor() );
  11728. if ( pGrenade )
  11729. return pGrenade->m_pWeaponInfo;
  11730. CInferno* pInferno = dynamic_cast<CInferno*>( info.GetInflictor() );
  11731. if ( pInferno )
  11732. return pInferno->GetSourceWeaponInfo();
  11733. return NULL;
  11734. }
  11735. void CCSPlayer::RestoreWeaponOnC4Abort( void )
  11736. {
  11737. if ( !m_lastWeaponBeforeC4AutoSwitch )
  11738. {
  11739. return;
  11740. }
  11741. else
  11742. {
  11743. if ( GetActiveCSWeapon() )
  11744. {
  11745. GetActiveCSWeapon()->SendWeaponAnim( ACT_VM_IDLE );
  11746. }
  11747. Weapon_Switch( m_lastWeaponBeforeC4AutoSwitch );
  11748. }
  11749. }
  11750. void CCSPlayer::PlayerUsedKnife( void )
  11751. {
  11752. // Player immunity in gun game is cleared upon weapon use
  11753. ClearGunGameImmunity();
  11754. }
  11755. void CCSPlayer::PlayerUsedGrenade( int nWeaponID )
  11756. {
  11757. // Player immunity in gun game is cleared upon weapon use
  11758. ClearGunGameImmunity();
  11759. if ( CSGameRules()->IsPlayingGunGameTRBomb() )
  11760. {
  11761. // Clear ownership flag for a grenade used in TR mode
  11762. if ( nWeaponID == WEAPON_FLASHBANG && m_bGunGameTRModeHasFlashbang )
  11763. {
  11764. m_bGunGameTRModeHasFlashbang = false;
  11765. }
  11766. else if ( nWeaponID == WEAPON_HEGRENADE && m_bGunGameTRModeHasHEGrenade )
  11767. {
  11768. m_bGunGameTRModeHasHEGrenade = false;
  11769. }
  11770. else if ( nWeaponID == WEAPON_MOLOTOV && m_bGunGameTRModeHasMolotov )
  11771. {
  11772. m_bGunGameTRModeHasMolotov = false;
  11773. }
  11774. else if ( nWeaponID == WEAPON_INCGRENADE && m_bGunGameTRModeHasIncendiary )
  11775. {
  11776. m_bGunGameTRModeHasIncendiary = false;
  11777. }
  11778. }
  11779. }
  11780. void CCSPlayer::PlayerUsedFirearm( CBaseCombatWeapon* pBaseWeapon )
  11781. {
  11782. // Player immunity in gun game is cleared upon weapon use
  11783. ClearGunGameImmunity();
  11784. if ( pBaseWeapon )
  11785. {
  11786. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  11787. if ( pWeapon )
  11788. {
  11789. CSWeaponType weaponType = pWeapon->GetWeaponType();
  11790. CSWeaponID weaponID = static_cast<CSWeaponID>( pWeapon->GetCSWeaponID() );
  11791. if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
  11792. {
  11793. if ( m_WeaponTypesUsed.Find( weaponID ) == -1 )
  11794. {
  11795. // Add this weapon to the list of weapons used by the player
  11796. m_WeaponTypesUsed.AddToTail( weaponID );
  11797. }
  11798. }
  11799. }
  11800. }
  11801. }
  11802. void CCSPlayer::AddBurnDamageDelt( int entityIndex )
  11803. {
  11804. if ( m_BurnDamageDeltVec.Find( entityIndex ) == -1 )
  11805. {
  11806. // Add this index to the list
  11807. m_BurnDamageDeltVec.AddToTail( entityIndex );
  11808. }
  11809. }
  11810. int CCSPlayer::GetNumPlayersDamagedWithFire()
  11811. {
  11812. return m_BurnDamageDeltVec.Count();
  11813. }
  11814. void CCSPlayer::PlayerEmptiedAmmoForFirearm( CBaseCombatWeapon* pBaseWeapon )
  11815. {
  11816. if ( pBaseWeapon )
  11817. {
  11818. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  11819. if ( pWeapon )
  11820. {
  11821. CSWeaponType weaponType = pWeapon->GetWeaponType();
  11822. CSWeaponID weaponID = static_cast<CSWeaponID>( pWeapon->GetCSWeaponID() );
  11823. if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
  11824. {
  11825. if ( m_WeaponTypesRunningOutOfAmmo.Find( weaponID ) == -1 )
  11826. {
  11827. // Add this weapon to the list of weapons used by the player
  11828. m_WeaponTypesRunningOutOfAmmo.AddToTail( weaponID );
  11829. }
  11830. }
  11831. }
  11832. }
  11833. }
  11834. bool CCSPlayer::DidPlayerEmptyAmmoForWeapon( CBaseCombatWeapon* pBaseWeapon )
  11835. {
  11836. if ( pBaseWeapon )
  11837. {
  11838. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
  11839. if ( pWeapon )
  11840. {
  11841. CSWeaponType weaponType = pWeapon->GetWeaponType();
  11842. CSWeaponID weaponID = static_cast<CSWeaponID>( pWeapon->GetCSWeaponID() );
  11843. if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
  11844. {
  11845. if ( m_WeaponTypesRunningOutOfAmmo.Find( weaponID ) != -1 )
  11846. {
  11847. return true;
  11848. }
  11849. }
  11850. }
  11851. }
  11852. return false;
  11853. }
  11854. void CCSPlayer::SetWasKilledThisRound(bool wasKilled )
  11855. {
  11856. m_wasKilledThisRound = wasKilled;
  11857. if( wasKilled )
  11858. {
  11859. m_numRoundsSurvived = 0;
  11860. }
  11861. }
  11862. /**
  11863. * public CCSPlayer::ProcessPlayerDeathAchievements()
  11864. *
  11865. * Do Achievement processing whenever a player is killed
  11866. *
  11867. * Parameters:
  11868. * pAttacker -
  11869. * pVictim -
  11870. * info -
  11871. */
  11872. void CCSPlayer::ProcessPlayerDeathAchievements( CCSPlayer *pAttacker, CCSPlayer *pVictim, const CTakeDamageInfo &info )
  11873. {
  11874. Assert(pVictim != NULL );
  11875. CBaseEntity *pInflictor = info.GetInflictor();
  11876. if ( pVictim )
  11877. {
  11878. pVictim->SetWasKilledThisRound(true );
  11879. pVictim->m_lowHealthGoalTime = 0.0f;
  11880. }
  11881. // all these achievements require a valid attacker on a different team
  11882. if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() )
  11883. {
  11884. // get the weapon used - some of the achievements will need this data
  11885. CWeaponCSBase* pVictimWeapon = dynamic_cast< CWeaponCSBase* >(pVictim->GetActiveWeapon() );
  11886. const CCSWeaponInfo* pAttackerWeaponInfo = GetWeaponInfoFromDamageInfo( info );
  11887. CSWeaponID attackerWeaponId = pAttackerWeaponInfo ? pAttackerWeaponInfo->m_weaponId : WEAPON_NONE;
  11888. bool bIsAttackerEligibleForAchievements = !pAttacker->HasControlledBotThisRound() &&
  11889. !pAttacker->HasBeenControlledThisRound();
  11890. if (pVictim->m_bIsDefusing )
  11891. {
  11892. pAttacker->AwardAchievement( CSKilledDefuser );
  11893. pAttacker->m_bKilledDefuser = true;
  11894. if (attackerWeaponId == WEAPON_HEGRENADE && bIsAttackerEligibleForAchievements)
  11895. {
  11896. pAttacker->AwardAchievement( CSKilledDefuserWithGrenade );
  11897. }
  11898. }
  11899. // [pfreese] Achievement check for attacker killing player while reloading
  11900. if (pVictim->IsReloading() && bIsAttackerEligibleForAchievements )
  11901. {
  11902. pAttacker->AwardAchievement( CSKillEnemyReloading );
  11903. }
  11904. // check for first shot kills
  11905. const CWeaponCSBase* pAttackerWeapon = dynamic_cast<const CWeaponCSBase*>( info.GetWeapon() );
  11906. if ( pAttackerWeapon && CSGameRules()->IsPlayingGunGameProgressive() )
  11907. {
  11908. // CSN-8954 - m_iClip1 gets decremented AFTER these achievement checks, so this was previously counting kills with the second bullet.
  11909. // also restricting to arms race to match the description.
  11910. if ( pAttackerWeapon->UsesClipsForAmmo1() && (pAttackerWeapon->Clip1() /*+ 1*/ == pAttackerWeapon->GetMaxClip1() && bIsAttackerEligibleForAchievements ) )
  11911. {
  11912. pAttacker->IncrementFirstShotKills(1 );
  11913. if ( pAttacker->GetFirstShotKills() >= 3 )
  11914. {
  11915. pAttacker->AwardAchievement( CSFirstBulletKills );
  11916. }
  11917. }
  11918. else
  11919. { // if we missed one first shot kill, start our tracking over
  11920. pAttacker->ResetFirstShotKills();
  11921. }
  11922. }
  11923. if ( pAttacker->m_bulletsFiredSinceLastSpawn == 1 && CSGameRules()->IsPlayingGunGameProgressive() && bIsAttackerEligibleForAchievements )
  11924. {
  11925. pAttacker->AwardAchievement( CSBornReady );
  11926. }
  11927. if ( pVictim->IsRescuing() )
  11928. {
  11929. // Ensure the killer did not injure any hostages
  11930. if ( !pAttacker->InjuredAHostage() && bIsAttackerEligibleForAchievements )
  11931. {
  11932. pAttacker->AwardAchievement( CSKilledRescuer );
  11933. pAttacker->m_bKilledRescuer = true;
  11934. }
  11935. }
  11936. // [menglish] Achievement check for doing 95% or more damage to a player and having another player kill them
  11937. FOR_EACH_LL( pVictim->m_DamageList, i )
  11938. {
  11939. if ( pVictim->m_DamageList[i]->IsDamageRecordValidPlayerToPlayer() &&
  11940. pVictim->m_DamageList[i]->GetPlayerRecipientPtr() == pVictim &&
  11941. pVictim->m_DamageList[i]->GetPlayerDamagerPtr() != pAttacker &&
  11942. (pVictim->m_DamageList[i]->GetDamage() >= pVictim->GetMaxHealth() - AchievementConsts::DamageNoKill_MaxHealthLeftOnKill ) &&
  11943. pVictim->IsOtherEnemy( pVictim->m_DamageList[i]->GetPlayerDamagerPtr() ) &&
  11944. !pVictim->m_DamageList[i]->GetPlayerDamagerPtr()->HasControlledBotThisRound() &&
  11945. !pVictim->m_DamageList[i]->GetPlayerDamagerPtr()->HasBeenControlledThisRound() )
  11946. {
  11947. pVictim->m_DamageList[i]->GetPlayerDamagerPtr()->AwardAchievement( CSDamageNoKill );
  11948. }
  11949. }
  11950. pAttacker->m_NumEnemiesKilledThisRound++;
  11951. pAttacker->m_NumEnemiesKilledThisSpawn++;
  11952. if ( pAttacker->m_NumEnemiesKilledThisSpawn > pAttacker->m_maxNumEnemiesKillStreak )
  11953. pAttacker->m_maxNumEnemiesKillStreak = pAttacker->m_NumEnemiesKilledThisSpawn;
  11954. //store a list of kill times for spree tracking
  11955. pAttacker->m_killTimes.AddToTail(gpGlobals->curtime );
  11956. //Add the victim to the list of players killed this round
  11957. pAttacker->m_enemyPlayersKilledThisRound.AddToTail( pVictim );
  11958. //Calculate Avenging for all players the victim has killed
  11959. for ( int avengedIndex = 0; avengedIndex < pVictim->m_enemyPlayersKilledThisRound.Count(); avengedIndex++ )
  11960. {
  11961. CCSPlayer* avengedPlayer = pVictim->m_enemyPlayersKilledThisRound[avengedIndex];
  11962. if (avengedPlayer )
  11963. {
  11964. //Make sure you are avenging someone on your own team (This is the expected flow. Just here to avoid edge cases like team-switching ).
  11965. if ( avengedPlayer->IsOtherSameTeam( pAttacker->GetTeamNumber() ) )
  11966. {
  11967. CCS_GameStats.Event_PlayerAvengedTeammate(pAttacker, pVictim->m_enemyPlayersKilledThisRound[avengedIndex] );
  11968. }
  11969. }
  11970. }
  11971. //remove elements older than a certain time
  11972. while ( pAttacker->m_killTimes.Count() > 0 && pAttacker->m_killTimes[0] + AchievementConsts::KillingSpree_WindowTime < gpGlobals->curtime )
  11973. {
  11974. pAttacker->m_killTimes.Remove(0 );
  11975. }
  11976. //If we killed enough players in the time window, award the achievement
  11977. if ( CSGameRules()->IsPlayingClassic() && pAttacker->m_killTimes.Count() >= AchievementConsts::KillingSpree_Kills && bIsAttackerEligibleForAchievements )
  11978. {
  11979. pAttacker->m_KillingSpreeStartTime = gpGlobals->curtime;
  11980. pAttacker->AwardAchievement( CSKillingSpree );
  11981. }
  11982. // Did the attacker just kill someone on a killing spree?
  11983. if ( pVictim->m_KillingSpreeStartTime >= 0 && pVictim->m_KillingSpreeStartTime - gpGlobals->curtime <= AchievementConsts::KillingSpreeEnder_TimeWindow && bIsAttackerEligibleForAchievements )
  11984. {
  11985. pAttacker->AwardAchievement( CSKillingSpreeEnder );
  11986. }
  11987. // Check the "killed someone with their own weapon" achievement
  11988. if ( pAttackerWeapon &&
  11989. bIsAttackerEligibleForAchievements &&
  11990. pAttackerWeapon->GetPreviousOwner() == pVictim )
  11991. {
  11992. pAttacker->AwardAchievement( CSKillEnemyWithFormerGun );
  11993. }
  11994. // If this player has killed the entire team award him the achievement
  11995. if ( pAttacker->m_NumEnemiesKilledThisRound == pAttacker->m_NumEnemiesAtRoundStart &&
  11996. pAttacker->m_NumEnemiesKilledThisRound >= AchievementConsts::KillEnemyTeam_MinKills &&
  11997. bIsAttackerEligibleForAchievements )
  11998. {
  11999. if ( CSGameRules()->IsPlayingClassic() )
  12000. {
  12001. pAttacker->AwardAchievement( CSKillEnemyTeam );
  12002. }
  12003. if ( CSGameRules()->IsPlayingGunGameTRBomb() && CSGameRules()->m_bBombPlanted == false )
  12004. {
  12005. if ( pAttacker->GetTeamNumber() == TEAM_CT )
  12006. {
  12007. pAttacker->AwardAchievement( CSKillEnemyTerrTeamBeforeBombPlant );
  12008. }
  12009. else
  12010. {
  12011. pAttacker->AwardAchievement( CSKillEnemyCTTeamBeforeBombPlant );
  12012. }
  12013. }
  12014. }
  12015. if ( pVictim->JustLeftSpawnImmunity() && bIsAttackerEligibleForAchievements && CSGameRules()->IsPlayingGunGameProgressive() )
  12016. {
  12017. pAttacker->AwardAchievement( CSSpawnCamper );
  12018. }
  12019. //If this is a posthumous kill award the achievement
  12020. if ( !pAttacker->IsAlive() && attackerWeaponId == WEAPON_HEGRENADE && bIsAttackerEligibleForAchievements )
  12021. {
  12022. CCS_GameStats.IncrementStat(pAttacker, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1 );
  12023. ToCSPlayer(pAttacker )->AwardAchievement( CSPosthumousGrenadeKill );
  12024. }
  12025. if ( pAttacker->GetActiveWeapon() &&
  12026. pAttacker->GetActiveWeapon()->Clip1() == 1 &&
  12027. pAttackerWeapon &&
  12028. pAttackerWeapon->GetWeaponType() != WEAPONTYPE_SNIPER_RIFLE &&
  12029. pAttackerWeapon->GetWeaponType() != WEAPONTYPE_KNIFE &&
  12030. attackerWeaponId != WEAPON_TASER )
  12031. {
  12032. if (pInflictor == pAttacker && bIsAttackerEligibleForAchievements )
  12033. {
  12034. pAttacker->AwardAchievement( CSKillEnemyLastBullet );
  12035. CCS_GameStats.IncrementStat( pAttacker, CSSTAT_KILLS_WITH_LAST_ROUND, 1 );
  12036. }
  12037. }
  12038. // [dwenger] Fun-fact processing
  12039. if ( pVictimWeapon && pVictimWeapon->GetWeaponType() == WEAPONTYPE_KNIFE && !pVictimWeapon->IsA( WEAPON_TASER ) &&
  12040. pInflictor == pAttacker &&
  12041. pAttackerWeapon &&
  12042. !pAttackerWeapon->IsA( WEAPON_KNIFE ) &&
  12043. pAttackerWeapon->GetWeaponType() != WEAPONTYPE_C4 &&
  12044. pAttackerWeapon->GetWeaponType() != WEAPONTYPE_GRENADE &&
  12045. !pVictim->HasControlledBotThisRound() &&
  12046. !pVictim->HasBeenControlledThisRound() )
  12047. {
  12048. // Victim was wielding knife when killed by a gun
  12049. pVictim->WieldingKnifeAndKilledByGun( true );
  12050. }
  12051. //see if this is a unique weapon
  12052. if ( attackerWeaponId != WEAPON_NONE )
  12053. {
  12054. if ( pAttacker->m_killWeapons.Find(attackerWeaponId ) == -1 )
  12055. {
  12056. pAttacker->m_bLastKillUsedUniqueWeapon = true;
  12057. pAttacker->m_killWeapons.AddToTail(attackerWeaponId );
  12058. if (pAttacker->m_killWeapons.Count() >= AchievementConsts::KillsWithMultipleGuns_MinWeapons &&
  12059. bIsAttackerEligibleForAchievements && !CSGameRules()->IsPlayingGunGameProgressive() )
  12060. {
  12061. pAttacker->AwardAchievement( CSKillsWithMultipleGuns );
  12062. }
  12063. }
  12064. else
  12065. {
  12066. pAttacker->m_bLastKillUsedUniqueWeapon = false;
  12067. }
  12068. }
  12069. //Check for kills while blind
  12070. if ( pAttacker->IsBlindForAchievement() )
  12071. {
  12072. //if this is from a different blinding, restart the kill counter and set the time
  12073. if ( pAttacker->m_blindStartTime != pAttacker->m_firstKillBlindStartTime )
  12074. {
  12075. pAttacker->m_killsWhileBlind = 0;
  12076. pAttacker->m_firstKillBlindStartTime = pAttacker->m_blindStartTime;
  12077. }
  12078. ++pAttacker->m_killsWhileBlind;
  12079. if ( pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlind_Kills && bIsAttackerEligibleForAchievements )
  12080. {
  12081. pAttacker->AwardAchievement( CSKillEnemiesWhileBlind );
  12082. }
  12083. if ( pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlindHard_Kills && bIsAttackerEligibleForAchievements )
  12084. {
  12085. pAttacker->AwardAchievement( CSKillEnemiesWhileBlindHard );
  12086. }
  12087. }
  12088. //Check sniper killing achievements
  12089. bool victimZoomed = ( pVictim->GetFOV() != pVictim->GetDefaultFOV() );
  12090. bool attackerZoomed = ( pAttacker->GetFOV() != pAttacker->GetDefaultFOV() );
  12091. bool attackerUsedSniperRifle = pAttackerWeapon && pAttackerWeapon->GetWeaponType() == WEAPONTYPE_SNIPER_RIFLE && pInflictor == pAttacker;
  12092. if ( victimZoomed && attackerUsedSniperRifle && bIsAttackerEligibleForAchievements )
  12093. {
  12094. pAttacker->AwardAchievement( CSKillSniperWithSniper );
  12095. }
  12096. if ( attackerWeaponId == WEAPON_KNIFE && victimZoomed && bIsAttackerEligibleForAchievements )
  12097. {
  12098. pAttacker->AwardAchievement( CSKillSniperWithKnife );
  12099. }
  12100. if ( attackerUsedSniperRifle && !attackerZoomed && bIsAttackerEligibleForAchievements )
  12101. {
  12102. pAttacker->AwardAchievement( CSHipShot );
  12103. }
  12104. //Kill a player at low health
  12105. if ( pAttacker->IsAlive() && pAttacker->GetHealth() <= AchievementConsts::KillWhenAtLowHealth_MaxHealth && bIsAttackerEligibleForAchievements )
  12106. {
  12107. pAttacker->AwardAchievement( CSKillWhenAtLowHealth );
  12108. }
  12109. //Kill a player at medium health
  12110. if ( pAttacker->IsAlive() && pAttacker->GetHealth() <= AchievementConsts::KillWhenAtMediumHealth_MaxHealth && bIsAttackerEligibleForAchievements )
  12111. {
  12112. pAttacker->m_iMediumHealthKills++;
  12113. }
  12114. //Kill a player with a knife during the pistol round
  12115. if ( CSGameRules()->IsPistolRound() && CSGameRules()->IsPlayingClassic() )
  12116. {
  12117. if ( attackerWeaponId == WEAPON_KNIFE && bIsAttackerEligibleForAchievements )
  12118. {
  12119. pAttacker->AwardAchievement( CSPistolRoundKnifeKill );
  12120. }
  12121. }
  12122. // [tj] Check for dual elites fight
  12123. CWeaponCSBase* victimWeapon = pVictim->GetActiveCSWeapon();
  12124. if ( victimWeapon )
  12125. {
  12126. CSWeaponID victimWeaponID = static_cast<CSWeaponID>( victimWeapon->GetCSWeaponID() );
  12127. if (attackerWeaponId == WEAPON_ELITE && victimWeaponID == WEAPON_ELITE && bIsAttackerEligibleForAchievements )
  12128. {
  12129. pAttacker->AwardAchievement( CSWinDualDuel );
  12130. }
  12131. }
  12132. // [tj] See if the attacker or defender are in the air [sbodenbender] dont include ladders
  12133. bool attackerInAir = pAttacker->GetMoveType() != MOVETYPE_LADDER && pAttacker->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight ) == NULL;
  12134. bool victimInAir = pVictim->GetMoveType() != MOVETYPE_LADDER && pVictim->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight ) == NULL;
  12135. // [dkorus] in air achievements are only allowable when using a gun weapon (including taser)
  12136. if ( pAttackerWeapon && IsGunWeapon(pAttackerWeapon->GetWeaponType()) && bIsAttackerEligibleForAchievements )
  12137. {
  12138. if ( attackerInAir )
  12139. {
  12140. pAttacker->AwardAchievement( CSKillWhileInAir );
  12141. }
  12142. if ( victimInAir )
  12143. {
  12144. pAttacker->AwardAchievement( CSKillEnemyInAir );
  12145. }
  12146. if ( attackerInAir && victimInAir )
  12147. {
  12148. pAttacker->AwardAchievement( CSKillerAndEnemyInAir );
  12149. }
  12150. }
  12151. // [tj] advance to the next stage of the defuse defense achievement
  12152. if (pAttacker->m_defuseDefenseStep == DD_STARTED_DEFUSE )
  12153. {
  12154. pAttacker->m_defuseDefenseStep = DD_KILLED_TERRORIST;
  12155. }
  12156. if (pVictim->HasC4() && pVictim->GetBombPickuptime() + AchievementConsts::KillBombPickup_MaxTime > gpGlobals->curtime && bIsAttackerEligibleForAchievements )
  12157. {
  12158. pAttacker->AwardAchievement( CSKillBombPickup );
  12159. }
  12160. // for progressive it's always the first round... for TR/Demolition mode, we need to check it's the first round
  12161. bool isFirstRoundOfArsenalMode = CSGameRules()->IsPlayingGunGameProgressive() || ( CSGameRules()->IsPlayingGunGameTRBomb() && CSGameRules()->m_iTotalRoundsPlayed == 0 );
  12162. if ( isFirstRoundOfArsenalMode && CSGameRules()->m_bNoTerroristsKilled && CSGameRules()->m_bNoCTsKilled && bIsAttackerEligibleForAchievements )
  12163. {
  12164. // first kill of the match! Check for achievements
  12165. pAttacker->AwardAchievement( CSGunGameFirstKill );
  12166. }
  12167. // victim may have just dropped C4 or still have it... increment kills either way
  12168. if ( pVictim->HasC4() || pVictim->GetBombDroppedTime() > 0.0f )
  12169. {
  12170. pAttacker->m_bombCarrierkills++;
  12171. }
  12172. }
  12173. //If you kill a friendly player while blind (from an enemy player ), give the guy that blinded you an achievement
  12174. if ( pAttacker != NULL && pVictim != NULL && pVictim->IsOtherSameTeam( pAttacker->GetTeamNumber() ) && pAttacker->IsBlind() )
  12175. {
  12176. CCSPlayer* flashbangAttacker = pAttacker->GetLastFlashbangAttacker();
  12177. if (flashbangAttacker &&
  12178. pAttacker->IsOtherEnemy( flashbangAttacker->entindex() ) &&
  12179. !flashbangAttacker->HasControlledBotThisRound() &&
  12180. !flashbangAttacker->HasBeenControlledThisRound() )
  12181. {
  12182. flashbangAttacker->AwardAchievement( CSCauseFriendlyFireWithFlashbang );
  12183. }
  12184. }
  12185. // do a scan to determine count of players still alive
  12186. int livePlayerCount = 0;
  12187. // Zero initialize the arrays.
  12188. int teamCount[TEAM_MAXCOUNT] = {};
  12189. int teamIgnoreCount[TEAM_MAXCOUNT] = {};
  12190. int teamAliveCount[TEAM_MAXCOUNT] = {};
  12191. CCSPlayer *pAlivePlayer = NULL;
  12192. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12193. {
  12194. CCSPlayer* pPlayer = (CCSPlayer* )UTIL_PlayerByIndex( i );
  12195. if (pPlayer )
  12196. {
  12197. int teamNum = pPlayer->GetTeamNumber();
  12198. if ( teamNum >= 0 )
  12199. {
  12200. ++teamCount[teamNum];
  12201. if (pPlayer->WasNotKilledNaturally() )
  12202. {
  12203. teamIgnoreCount[teamNum]++;
  12204. }
  12205. }
  12206. if (pPlayer->IsAlive() && pPlayer != pVictim )
  12207. {
  12208. if ( teamNum >= 0 )
  12209. {
  12210. ++teamAliveCount[teamNum];
  12211. }
  12212. ++livePlayerCount;
  12213. pAlivePlayer = pPlayer;
  12214. }
  12215. }
  12216. }
  12217. // Achievement check for being the last player alive in a match
  12218. if (pAlivePlayer )
  12219. {
  12220. int alivePlayerTeam = pAlivePlayer->GetTeamNumber();
  12221. int alivePlayerOpposingTeam = alivePlayerTeam == TEAM_CT ? TEAM_TERRORIST : TEAM_CT;
  12222. if (livePlayerCount == 1
  12223. && CSGameRules()->m_iRoundWinStatus == WINNER_NONE
  12224. && teamCount[alivePlayerTeam] - teamIgnoreCount[alivePlayerTeam] >= AchievementConsts::LastPlayerAlive_MinPlayersOnTeam
  12225. && teamCount[alivePlayerOpposingTeam] - teamIgnoreCount[alivePlayerOpposingTeam] >= AchievementConsts::DefaultMinOpponentsForAchievement
  12226. && ( !(pAlivePlayer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED ) )
  12227. && !pAlivePlayer->HasControlledBotThisRound()
  12228. && !pAlivePlayer->HasBeenControlledThisRound() )
  12229. {
  12230. pAlivePlayer->AwardAchievement( CSLastPlayerAlive);
  12231. }
  12232. }
  12233. // [tj] Added hook into player killed stat that happens before weapon drop
  12234. CCS_GameStats.Event_PlayerKilled_PreWeaponDrop(pVictim, info );
  12235. }
  12236. //[tj] traces up to maxTrace units down and returns any standable object it hits
  12237. // (doesn't check slope for standability )
  12238. CBaseEntity* CCSPlayer::GetNearestSurfaceBelow(float maxTrace )
  12239. {
  12240. trace_t trace;
  12241. Ray_t ray;
  12242. Vector traceStart = this->GetAbsOrigin();
  12243. Vector traceEnd = traceStart;
  12244. traceEnd.z -= maxTrace;
  12245. Vector minExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN : VEC_HULL_MIN;
  12246. Vector maxExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX : VEC_HULL_MAX;
  12247. ray.Init( traceStart, traceEnd, minExtent, maxExtent );
  12248. UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  12249. return trace.m_pEnt;
  12250. }
  12251. // [tj] Added a way to react to the round ending before we reset.
  12252. // It is important to note that this happens before the bomb explodes, so a player may die
  12253. // after this from a bomb explosion or a late kill after a defuse/detonation/rescue.
  12254. void CCSPlayer::OnRoundEnd(int winningTeam, int reason )
  12255. {
  12256. if ( IsAlive() && !m_bIsControllingBot )
  12257. {
  12258. m_numRoundsSurvived++;
  12259. if ( m_numRoundsSurvived > m_maxNumRoundsSurvived )
  12260. {
  12261. m_maxNumRoundsSurvived = m_numRoundsSurvived;
  12262. }
  12263. }
  12264. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  12265. {
  12266. // Send a game event to clear out the round-start sound events
  12267. IGameEvent *event = gameeventmanager->CreateEvent( "gg_reset_round_start_sounds" );
  12268. if ( event )
  12269. {
  12270. event->SetInt( "userid", GetUserID() );
  12271. gameeventmanager->FireEvent( event );
  12272. }
  12273. }
  12274. if (winningTeam == WINNER_CT || winningTeam == WINNER_TER )
  12275. {
  12276. int losingTeamId = (winningTeam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
  12277. if ( CSGameRules()->IsPlayingGunGameTRBomb() && !CSGameRules()->IsWarmupPeriod() )
  12278. {
  12279. ConVarRef mp_ggtr_bomb_pts_for_upgrade( "mp_ggtr_bomb_pts_for_upgrade" );
  12280. if ( m_iNumGunGameTRKillPoints >= mp_ggtr_bomb_pts_for_upgrade.GetInt() || CSGameRules()->GetGunGameTRBonusGrenade( this ) > 0 )
  12281. {
  12282. // Play client sound for impending weapon upgrade...
  12283. SendGunGameWeaponUpgradeAlert();
  12284. }
  12285. if ( m_iNumGunGameTRKillPoints > mp_ggtr_bomb_pts_for_upgrade.GetInt() - 1 )
  12286. {
  12287. // Need to bump the player's TR Bomb Mode weapon to the next level
  12288. m_bShouldProgressGunGameTRBombModeWeapon = true;
  12289. }
  12290. }
  12291. CTeam* losingTeam = GetGlobalTeam(losingTeamId );
  12292. int losingTeamPlayers = 0;
  12293. if (losingTeam )
  12294. {
  12295. losingTeamPlayers = losingTeam->GetNumPlayers();
  12296. int ignoreCount = 0;
  12297. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12298. {
  12299. CCSPlayer* pPlayer = (CCSPlayer* )UTIL_PlayerByIndex( i );
  12300. if (pPlayer )
  12301. {
  12302. int teamNum = pPlayer->GetTeamNumber();
  12303. if ( teamNum == losingTeamId )
  12304. {
  12305. if (pPlayer->WasNotKilledNaturally() )
  12306. {
  12307. ignoreCount++;
  12308. }
  12309. }
  12310. }
  12311. }
  12312. losingTeamPlayers -= ignoreCount;
  12313. }
  12314. //Check fast round win achievement
  12315. if ( IsAlive() &&
  12316. gpGlobals->curtime - CSGameRules()->GetRoundStartTime() < AchievementConsts::FastRoundWin_Time &&
  12317. GetTeamNumber() == winningTeam &&
  12318. losingTeamPlayers >= AchievementConsts::DefaultMinOpponentsForAchievement )
  12319. {
  12320. AwardAchievement(CSFastRoundWin );
  12321. }
  12322. //Check goosechase achievement
  12323. if (IsAlive() && reason == Target_Bombed && m_gooseChaseStep == GC_STOPPED_AFTER_GETTING_SHOT && m_pGooseChaseDistractingPlayer )
  12324. {
  12325. m_pGooseChaseDistractingPlayer->AwardAchievement(CSGooseChase );
  12326. }
  12327. //Check Defuse Defense achievement
  12328. if (IsAlive() && reason == Bomb_Defused && m_defuseDefenseStep == DD_KILLED_TERRORIST )
  12329. {
  12330. AwardAchievement(CSDefuseDefense );
  12331. }
  12332. //Check silent win
  12333. if (m_NumEnemiesKilledThisRound > 0 && GetTeamNumber() == winningTeam && !m_bMadeFootstepNoise )
  12334. {
  12335. AwardAchievement(CSSilentWin );
  12336. }
  12337. //Process && Check "win rounds without buying" achievement
  12338. if ( ( GetTeamNumber() == winningTeam ) && !m_bMadePurchseThisRound )
  12339. {
  12340. m_roundsWonWithoutPurchase++;
  12341. if ( ( m_roundsWonWithoutPurchase > AchievementConsts::WinRoundsWithoutBuying_Rounds ) &&
  12342. CSGameRules() && CSGameRules()->IsPlayingClassic() )
  12343. {
  12344. AwardAchievement(CSWinRoundsWithoutBuying );
  12345. }
  12346. }
  12347. else
  12348. {
  12349. m_roundsWonWithoutPurchase = 0;
  12350. }
  12351. }
  12352. // reset cash spent this round
  12353. m_iCashSpentThisRound = 0;
  12354. m_lastRoundResult = reason;
  12355. }
  12356. void CCSPlayer::SendGunGameWeaponUpgradeAlert( void )
  12357. {
  12358. // Send a game event for leveling up
  12359. IGameEvent *event = gameeventmanager->CreateEvent( "gg_player_impending_upgrade" );
  12360. if ( event )
  12361. {
  12362. event->SetInt( "userid", GetUserID() );
  12363. gameeventmanager->FireEvent( event );
  12364. }
  12365. }
  12366. void CCSPlayer::OnPreResetRound()
  12367. {
  12368. #if 0 // removed this achievement
  12369. //Check headshot survival achievement
  12370. if (IsAlive() && m_bSurvivedHeadshotDueToHelmet && CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() )
  12371. {
  12372. AwardAchievement(CSSurvivedHeadshotDueToHelmet );
  12373. }
  12374. #endif
  12375. if (IsAlive() && m_grenadeDamageTakenThisRound > AchievementConsts::SurviveGrenade_MinDamage )
  12376. {
  12377. AwardAchievement(CSSurviveGrenade );
  12378. }
  12379. //Check achievement for surviving attacks from multiple players.
  12380. if (IsAlive() )
  12381. {
  12382. int numberOfEnemyDamagers = GetNumEnemyDamagers();
  12383. if (numberOfEnemyDamagers >= AchievementConsts::SurviveManyAttacks_NumberDamagingPlayers )
  12384. {
  12385. AwardAchievement(CSSurviveManyAttacks );
  12386. }
  12387. }
  12388. if ( IsAlive() && CSGameRules() && CSGameRules()->ShouldRecordMatchStats() )
  12389. {
  12390. int round = CSGameRules()->GetRoundsPlayed() - 1; // last round because roundsplayed has already incremented.
  12391. if ( round >= 0 )
  12392. {
  12393. m_iMatchStats_LiveTime.GetForModify( round ) += CSGameRules()->GetRoundElapsedTime();
  12394. // Keep track in QMM data
  12395. if ( m_uiAccountId && CSGameRules() )
  12396. {
  12397. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  12398. {
  12399. pQMM->m_iMatchStats_LiveTime[ round ] = m_iMatchStats_LiveTime.Get( round );
  12400. }
  12401. }
  12402. }
  12403. }
  12404. m_bJustBecameSpectator = false;
  12405. m_isCurrentGunGameLeader = false;
  12406. m_isCurrentGunGameTeamLeader = false;
  12407. if ( m_switchTeamsOnNextRoundReset )
  12408. {
  12409. m_switchTeamsOnNextRoundReset = false;
  12410. bool bSwitched = false;
  12411. int teamNum = GetTeamNumber();
  12412. if ( teamNum == TEAM_TERRORIST )
  12413. {
  12414. SwitchTeam( TEAM_CT );
  12415. bSwitched = true;
  12416. }
  12417. else if ( teamNum == TEAM_CT )
  12418. {
  12419. SwitchTeam( TEAM_TERRORIST );
  12420. bSwitched = true;
  12421. }
  12422. else if ( teamNum == TEAM_SPECTATOR )
  12423. {
  12424. if ( GetCoachingTeam() == TEAM_CT )
  12425. {
  12426. m_iCoachingTeam = TEAM_TERRORIST;
  12427. }
  12428. else if ( GetCoachingTeam() == TEAM_TERRORIST )
  12429. {
  12430. m_iCoachingTeam = TEAM_CT;
  12431. }
  12432. return; // We're done with spectators.
  12433. }
  12434. // do this for all modes, when you switch sides, you want the second half to start for the opposite team as if it were the first half
  12435. // if ( CSGameRules()->IsPlayingClassic() )
  12436. // {
  12437. // Remove all weapons
  12438. RemoveAllItems( true );
  12439. // Reset money
  12440. ResetAccount();
  12441. InitializeAccount( CSGameRules()->GetStartMoney() );
  12442. // For queued matchmaking mode reset money for start
  12443. FOR_EACH_MAP( CSGameRules()->m_mapQueuedMatchmakingPlayersData, idxQueuedPlayer )
  12444. {
  12445. CSGameRules()->m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer )->m_cash = CSGameRules()->GetStartMoney();
  12446. }
  12447. // Make sure player doesn't receive any winnings from the prior round
  12448. MarkAsNotReceivingMoneyNextRound();
  12449. /* }*/
  12450. if ( bSwitched )
  12451. {
  12452. // Send a game event for halftime team swap
  12453. IGameEvent *event = gameeventmanager->CreateEvent( "gg_halftime" );
  12454. if ( event )
  12455. {
  12456. event->SetInt( "userid", GetUserID() );
  12457. gameeventmanager->FireEvent( event );
  12458. }
  12459. }
  12460. }
  12461. }
  12462. void CCSPlayer::OnCanceledDefuse()
  12463. {
  12464. if (m_gooseChaseStep == GC_SHOT_DURING_DEFUSE )
  12465. {
  12466. m_gooseChaseStep = GC_STOPPED_AFTER_GETTING_SHOT;
  12467. }
  12468. }
  12469. void CCSPlayer::OnStartedDefuse()
  12470. {
  12471. m_bAttemptedDefusal = true;
  12472. if (m_defuseDefenseStep == DD_NONE )
  12473. {
  12474. m_defuseDefenseStep = DD_STARTED_DEFUSE;
  12475. }
  12476. if ( !IsBot() && m_flDefusingTalkTimer < gpGlobals->curtime )
  12477. {
  12478. Radio( "DefusingBomb", "#Cstrike_TitlesTXT_Defusing_Bomb" );
  12479. m_flDefusingTalkTimer = gpGlobals->curtime + 6.0f;
  12480. }
  12481. }
  12482. //-----------------------------------------------------------------------------
  12483. // Purpose:
  12484. //-----------------------------------------------------------------------------
  12485. void CCSPlayer::AttemptToExitFreezeCam( void )
  12486. {
  12487. float fEndFreezeTravel = m_flDeathTime + spec_freeze_deathanim_time.GetFloat() + spec_freeze_traveltime.GetFloat();
  12488. if ( gpGlobals->curtime < fEndFreezeTravel )
  12489. return;
  12490. m_bAbortFreezeCam = true;
  12491. }
  12492. //-----------------------------------------------------------------------------
  12493. // Purpose: Sets whether this player is dominating the specified other player
  12494. //-----------------------------------------------------------------------------
  12495. void CCSPlayer::SetPlayerDominated( CCSPlayer *pPlayer, bool bDominated )
  12496. {
  12497. int iPlayerIndex = pPlayer->entindex();
  12498. m_bPlayerDominated.Set( iPlayerIndex, bDominated );
  12499. pPlayer->SetPlayerDominatingMe( this, bDominated );
  12500. }
  12501. //-----------------------------------------------------------------------------
  12502. // Purpose: Sets whether this player is being dominated by the other player
  12503. //-----------------------------------------------------------------------------
  12504. void CCSPlayer::SetPlayerDominatingMe( CCSPlayer *pPlayer, bool bDominated )
  12505. {
  12506. int iPlayerIndex = pPlayer->entindex();
  12507. m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
  12508. }
  12509. //-----------------------------------------------------------------------------
  12510. // Purpose: Returns whether this player is dominating the specified other player
  12511. //-----------------------------------------------------------------------------
  12512. bool CCSPlayer::IsPlayerDominated( int iPlayerIndex )
  12513. {
  12514. if ( CSGameRules()->IsPlayingGunGame() )
  12515. return m_bPlayerDominated.Get( iPlayerIndex );
  12516. return false;
  12517. }
  12518. bool CCSPlayer::IsPlayerDominatingMe( int iPlayerIndex )
  12519. {
  12520. if ( CSGameRules()->IsPlayingGunGame() )
  12521. return m_bPlayerDominatingMe.Get( iPlayerIndex );
  12522. return false;
  12523. }
  12524. uint32 Helper_AddMusicKitMVPsPendingSend( CEconItemView *pEconItemView, CCSGameRules::CQMMPlayerData_t *pQMM, int nIncBy )
  12525. {
  12526. if ( !pEconItemView || !pQMM )
  12527. return 0;
  12528. static CSchemaAttributeDefHandle pAttr_KillEater( "kill eater" );
  12529. uint32 unCurrent = 0;
  12530. if ( !pEconItemView->FindAttribute( pAttr_KillEater, &unCurrent ) )
  12531. return 0;
  12532. CCSGameRules::CQMMPlayerData_t::StattrakMusicKitValues_t::IndexType_t idx = pQMM->m_mapMusicKitUpdates.Find( pEconItemView->GetItemID() );
  12533. if ( idx != CCSGameRules::CQMMPlayerData_t::StattrakMusicKitValues_t::InvalidIndex() )
  12534. {
  12535. pQMM->m_mapMusicKitUpdates[ idx ] += nIncBy;
  12536. }
  12537. else
  12538. {
  12539. idx = pQMM->m_mapMusicKitUpdates.Insert( pEconItemView->GetItemID() );
  12540. pQMM->m_mapMusicKitUpdates[idx] = nIncBy;
  12541. }
  12542. return unCurrent + pQMM->m_mapMusicKitUpdates[idx];
  12543. }
  12544. extern ConVar sv_matchend_drops_enabled;
  12545. void CCSPlayer::IncrementNumMVPs( CSMvpReason_t mvpReason )
  12546. {
  12547. // [Forrest] Allow MVP to be turned off for a server
  12548. if ( sv_nomvp.GetBool() )
  12549. {
  12550. Msg( "Round MVP disabled: sv_nomvp is set.\n" );
  12551. return;
  12552. }
  12553. m_iMVPs++;
  12554. // Keep track in QMM data
  12555. CCSGameRules::CQMMPlayerData_t *pQMM = NULL;
  12556. if ( m_uiAccountId && CSGameRules() )
  12557. {
  12558. pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId );
  12559. if ( pQMM )
  12560. pQMM->m_numMVPs = m_iMVPs;
  12561. }
  12562. // Set the global MVP in CSGameRules
  12563. if ( CSGameRules() )
  12564. {
  12565. CCSPlayer *pOtherMvpCheck = CSGameRules()->m_pMVP.Get();
  12566. if ( pOtherMvpCheck && ( pOtherMvpCheck != this ) )
  12567. Warning( "CCSPlayer: MVP override ( %s -> %s )\n", pOtherMvpCheck->GetPlayerName(), this->GetPlayerName() );
  12568. CSGameRules()->m_pMVP = this;
  12569. }
  12570. CCS_GameStats.Event_MVPEarned( this );
  12571. IGameEvent *mvpEvent = gameeventmanager->CreateEvent( "round_mvp" );
  12572. CSteamID OwnerSteamID;
  12573. uint32 unMusicKitMVPs = 0; // this will remain zero if we fail to send the stat increment to GC
  12574. if ( mvpEvent )
  12575. {
  12576. mvpEvent->SetInt( "userid", GetUserID() );
  12577. mvpEvent->SetInt( "reason", mvpReason );
  12578. if ( unMusicKitMVPs > 0 )
  12579. mvpEvent->SetInt( "musickitmvps", (int)unMusicKitMVPs );
  12580. gameeventmanager->FireEvent( mvpEvent );
  12581. }
  12582. }
  12583. //-----------------------------------------------------------------------------
  12584. // Purpose: Sets the number of rounds this player has caused to be won for their team
  12585. //-----------------------------------------------------------------------------
  12586. void CCSPlayer::SetNumMVPs( int iNumMVP )
  12587. {
  12588. m_iMVPs = iNumMVP;
  12589. // Keep track in QMM data
  12590. if ( m_uiAccountId && CSGameRules() )
  12591. {
  12592. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  12593. {
  12594. pQMM->m_numMVPs = m_iMVPs;
  12595. }
  12596. }
  12597. }
  12598. //-----------------------------------------------------------------------------
  12599. // Purpose: Returns the number of rounds this player has caused to be won for their team
  12600. //-----------------------------------------------------------------------------
  12601. int CCSPlayer::GetNumMVPs()
  12602. {
  12603. return m_iMVPs;
  12604. }
  12605. //-----------------------------------------------------------------------------
  12606. // Purpose: Removes all nemesis relationships between this player and others
  12607. //-----------------------------------------------------------------------------
  12608. void CCSPlayer::RemoveNemesisRelationships()
  12609. {
  12610. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  12611. {
  12612. CCSPlayer *pTemp = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  12613. if ( pTemp && pTemp != this )
  12614. {
  12615. // set this player to be not dominating anyone else
  12616. SetPlayerDominated( pTemp, false );
  12617. // set no one else to be dominating this player
  12618. pTemp->SetPlayerDominated( this, false );
  12619. }
  12620. }
  12621. }
  12622. void CCSPlayer::CheckMaxGrenadeKills(int grenadeKills )
  12623. {
  12624. if (grenadeKills > m_maxGrenadeKills )
  12625. {
  12626. m_maxGrenadeKills = grenadeKills;
  12627. }
  12628. }
  12629. void CCSPlayer::CommitSuicide( bool bExplode /*= false*/, bool bForce /*= false*/ )
  12630. {
  12631. m_wasNotKilledNaturally = true;
  12632. BaseClass::CommitSuicide(bExplode, bForce );
  12633. }
  12634. void CCSPlayer::CommitSuicide( const Vector &vecForce, bool bExplode /*= false*/, bool bForce /*= false*/ )
  12635. {
  12636. m_wasNotKilledNaturally = true;
  12637. BaseClass::CommitSuicide(vecForce, bExplode, bForce );
  12638. }
  12639. void CCSPlayer::DecrementProgressiveWeaponFromSuicide( void )
  12640. {
  12641. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  12642. {
  12643. if ( ( m_LastDamageType & DMG_FALL ) || m_wasNotKilledNaturally ) // Did we die from falling or changing teams?
  12644. {
  12645. // Reset kill count with respect to new weapon
  12646. m_iNumGunGameKillsWithCurrentWeapon = 0;
  12647. // Suicided, so drop one weapon class
  12648. SubtractProgressiveWeaponIndex();
  12649. CRecipientFilter filter;
  12650. filter.AddRecipient( this );
  12651. filter.MakeReliable();
  12652. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#Cstrike_TitlesTXT_Hint_lost_a_level_generic" );
  12653. }
  12654. }
  12655. }
  12656. int CCSPlayer::GetNumEnemyDamagers()
  12657. {
  12658. int numberOfEnemyDamagers = 0;
  12659. FOR_EACH_LL( m_DamageList, i )
  12660. {
  12661. if ( m_DamageList[i]->IsDamageRecordValidPlayerToPlayer() &&
  12662. m_DamageList[i]->GetPlayerRecipientPtr() == this &&
  12663. IsOtherEnemy( m_DamageList[i]->GetPlayerDamagerPtr() ) )
  12664. {
  12665. numberOfEnemyDamagers++;
  12666. }
  12667. }
  12668. return numberOfEnemyDamagers;
  12669. }
  12670. int CCSPlayer::GetNumEnemiesDamaged()
  12671. {
  12672. int numberOfEnemiesDamaged = 0;
  12673. FOR_EACH_LL( m_DamageList, i )
  12674. {
  12675. if ( m_DamageList[i]->IsDamageRecordValidPlayerToPlayer() &&
  12676. m_DamageList[i]->GetPlayerDamagerPtr() == this &&
  12677. IsOtherEnemy( m_DamageList[i]->GetPlayerRecipientPtr() ) )
  12678. {
  12679. numberOfEnemiesDamaged++;
  12680. }
  12681. }
  12682. return numberOfEnemiesDamaged;
  12683. }
  12684. int CCSPlayer::GetTotalActualHealthRemovedFromEnemies()
  12685. {
  12686. int totalDamage = 0;
  12687. FOR_EACH_LL( m_DamageList, i )
  12688. {
  12689. if ( m_DamageList[i]->IsDamageRecordValidPlayerToPlayer() &&
  12690. m_DamageList[i]->GetPlayerDamagerPtr() == this &&
  12691. IsOtherEnemy( m_DamageList[i]->GetPlayerRecipientPtr() ) )
  12692. {
  12693. totalDamage += m_DamageList[i]->GetActualHealthRemoved();
  12694. }
  12695. }
  12696. return totalDamage;
  12697. }
  12698. bool CCSPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const
  12699. {
  12700. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
  12701. {
  12702. unsigned int myTeamMask = ( PhysicsSolidMaskForEntity() & ( CONTENTS_TEAM1 | CONTENTS_TEAM2 ) );
  12703. unsigned int otherTeamMask = ( contentsMask & ( CONTENTS_TEAM1 | CONTENTS_TEAM2 ) );
  12704. // See if we have a team and we're on the same team.
  12705. // If we are on the same team, then don't collide.
  12706. if ( myTeamMask != 0x0 && myTeamMask == otherTeamMask )
  12707. {
  12708. return false;
  12709. }
  12710. }
  12711. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  12712. }
  12713. void CCSPlayer::GiveHealthAndArmorForGuardianMode( bool bAdditive )
  12714. {
  12715. if ( !CSGameRules() )
  12716. return;
  12717. // we only do it every other, starting round 4
  12718. int nRoundsPlayed = CSGameRules()->GetTotalRoundsPlayed();
  12719. nRoundsPlayed = nRoundsPlayed - ( nRoundsPlayed % 2 );
  12720. if ( CSGameRules()->IsPlayingCoopMission() )
  12721. {
  12722. m_iHealth = 100;
  12723. m_bHasHelmet = true;
  12724. SetArmorValue( 100 );
  12725. m_bHasHeavyArmor = true;
  12726. return;
  12727. }
  12728. else
  12729. {
  12730. int nStartHealth = bAdditive ? m_iHealth : 0;
  12731. int nHealthToGive = 50;
  12732. if ( nRoundsPlayed > 3 )
  12733. {
  12734. nHealthToGive = MIN( GetMaxHealth(), 50 + ( ( nRoundsPlayed - 3 ) * 15 ) );
  12735. }
  12736. m_iHealth = MIN( nStartHealth + nHealthToGive, GetMaxHealth() );
  12737. }
  12738. // armor?
  12739. int nStartArmor = bAdditive ? ArmorValue() : 0;
  12740. int nArmorToGive = 50;
  12741. if ( CSGameRules()->IsPlayingCoopGuardian() )
  12742. {
  12743. if ( nRoundsPlayed > 3 )
  12744. {
  12745. nArmorToGive = MIN( 100, 20 + ( ( nRoundsPlayed - 3 ) * 20 ) );
  12746. }
  12747. }
  12748. int nArmor = MIN( nStartArmor + nArmorToGive, 100 );
  12749. SetArmorValue( nArmor );
  12750. }
  12751. void CCSPlayer::SetHealth( int amt )
  12752. {
  12753. m_iHealth = amt;
  12754. }
  12755. //--------------------------------------------------------------------------------------------------------
  12756. /**
  12757. * player used healthshot, make them heal
  12758. */
  12759. void CCSPlayer::OnHealthshotUsed( void )
  12760. {
  12761. EmitSound( "Healthshot.Success" );
  12762. }
  12763. //--------------------------------------------------------------------------------------------------------
  12764. bool CCSPlayer::UpdateTeamLeaderPlaySound( int nTeam )
  12765. {
  12766. if ( CSGameRules()->IsPlayingGunGameProgressive() == false )
  12767. return false;
  12768. bool bPlayedSound = false;
  12769. //GetGGLeader( int nTeam )
  12770. int nOtherTeam = (nTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
  12771. int nLeaderUserID = -1;
  12772. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  12773. {
  12774. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  12775. if ( !pPlayer || pPlayer->GetTeamNumber() != nTeam )
  12776. continue;
  12777. if ( pPlayer->entindex() == GetGlobalTeam( pPlayer->GetTeamNumber() )->GetGGLeader( pPlayer->GetTeamNumber() ) )
  12778. {
  12779. //if we made it this far, we are the current leader
  12780. IGameEvent *event = gameeventmanager->CreateEvent( "gg_team_leader" );
  12781. if ( event )
  12782. {
  12783. event->SetInt( "playerid", pPlayer->GetUserID() );
  12784. gameeventmanager->FireEvent( event );
  12785. }
  12786. nLeaderUserID = pPlayer->GetUserID();
  12787. break;
  12788. }
  12789. }
  12790. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  12791. {
  12792. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  12793. if ( !pPlayer || pPlayer->GetTeamNumber() != nTeam )
  12794. continue;
  12795. if ( nLeaderUserID == pPlayer->GetUserID() )
  12796. {
  12797. bool bIsMatchLeader = false;
  12798. CCSPlayer *pOtherLeader = ToCSPlayer( UTIL_PlayerByIndex( GetGlobalTeam( nOtherTeam )->GetGGLeader( nOtherTeam ) ) );
  12799. int nOtherIndex = pOtherLeader ? pOtherLeader->m_iGunGameProgressiveWeaponIndex : -1;
  12800. // if our GG index is higher than the other team's leader, we are the match leader
  12801. if ( pPlayer->m_iGunGameProgressiveWeaponIndex > nOtherIndex )
  12802. {
  12803. // check if we aren't already the match leader and if this is ane
  12804. if ( pPlayer->m_isCurrentGunGameLeader == false )
  12805. {
  12806. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Gun_Game_Leader" );
  12807. CSingleUserRecipientFilter filter( pPlayer );
  12808. EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.BecomeMatchLeader" );
  12809. bPlayedSound = true;
  12810. }
  12811. // they are the leader
  12812. pPlayer->m_isCurrentGunGameLeader = true;
  12813. // now kick everyone else off
  12814. int nLeaderIndex = pPlayer->entindex();
  12815. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  12816. {
  12817. CCSPlayer *pNonLeader = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  12818. if ( !pNonLeader )
  12819. continue;
  12820. if ( pNonLeader->entindex() != nLeaderIndex )
  12821. pNonLeader->m_isCurrentGunGameLeader = false;
  12822. }
  12823. }
  12824. // if we aren't the match leader and we just became the team lader, send them a message, hurrah!
  12825. if ( !bIsMatchLeader && pPlayer->m_isCurrentGunGameTeamLeader == false )
  12826. {
  12827. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Gun_Game_Team_Leader" );
  12828. CSingleUserRecipientFilter filter( pPlayer );
  12829. EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.BecomeTeamLeader" );
  12830. bPlayedSound = true;
  12831. }
  12832. pPlayer->m_isCurrentGunGameTeamLeader = true;
  12833. }
  12834. else
  12835. {
  12836. if ( pPlayer->m_isCurrentGunGameTeamLeader == true && nLeaderUserID != pPlayer->GetUserID() )
  12837. {
  12838. if ( CCSPlayer *pNewLeader = ( ( nLeaderUserID != -1 )
  12839. ? ToCSPlayer( UTIL_PlayerByUserId( nLeaderUserID ) )
  12840. : NULL ) )
  12841. { // If we cannot resolve the name of the new leader then print no message, but still play the sound
  12842. ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Stolen_Leader", CFmtStr( "#ENTNAME[%d]%s", pNewLeader->entindex(), pNewLeader->GetPlayerName() ) );
  12843. }
  12844. CSingleUserRecipientFilter filter( pPlayer );
  12845. EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.Demoted" );
  12846. bPlayedSound = true;
  12847. }
  12848. pPlayer->m_isCurrentGunGameTeamLeader = false;
  12849. }
  12850. }
  12851. return bPlayedSound;
  12852. }
  12853. void CCSPlayer::UpdateLeader( void )
  12854. {
  12855. // this code isn't used anymore
  12856. /*
  12857. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  12858. {
  12859. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  12860. if ( pPlayer && pPlayer != this && GetPlayerGunGameWeaponIndex() <= pPlayer->GetPlayerGunGameWeaponIndex() )
  12861. {
  12862. return;
  12863. }
  12864. }
  12865. //if we made it this far, we are the current leader
  12866. IGameEvent *event = gameeventmanager->CreateEvent( "gg_leader" );
  12867. if ( event )
  12868. {
  12869. event->SetInt( "playerid", GetUserID() );
  12870. gameeventmanager->FireEvent( event );
  12871. }
  12872. */
  12873. }
  12874. void CCSPlayer::ResetTRBombModeData( void )
  12875. {
  12876. m_iGunGameProgressiveWeaponIndex = 0;
  12877. m_iNumGunGameKillsWithCurrentWeapon = 0;
  12878. m_iNumGunGameTRKillPoints = 0;
  12879. m_iNumGunGameTRBombTotalPoints = 0;
  12880. m_bShouldProgressGunGameTRBombModeWeapon = false;
  12881. m_bMadeFinalGunGameProgressiveKill = false;
  12882. }
  12883. #if CS_CONTROLLABLE_BOTS_ENABLED
  12884. void CCSPlayer::SavePreControlData()
  12885. {
  12886. m_PreControlData.m_iClass = GetClass();
  12887. m_PreControlData.m_iAccount = m_iAccount;
  12888. m_PreControlData.m_iAccountMoneyEarnedForNextRound = m_iAccountMoneyEarnedForNextRound;
  12889. m_PreControlData.m_iFrags = FragCount();
  12890. m_PreControlData.m_iAssists = AssistsCount();
  12891. m_PreControlData.m_iDeaths = DeathCount();
  12892. if ( dev_reportmoneychanges.GetBool() )
  12893. Msg( "**** %s is Caching Pre-Bot Control Data (total: %d) \n", GetPlayerName(), m_PreControlData.m_iAccount );
  12894. }
  12895. bool CCSPlayer::CanControlBot( CCSBot *pBot, bool bSkipTeamCheck )
  12896. {
  12897. if ( !cv_bot_controllable.GetBool() )
  12898. return false;
  12899. if ( !pBot )
  12900. return false;
  12901. if ( !pBot->IsAlive() )
  12902. return false;
  12903. if ( !bSkipTeamCheck && IsOtherEnemy( pBot->entindex() ) )
  12904. return false;
  12905. if ( !bSkipTeamCheck && !IsValidObserverTarget(pBot ) )
  12906. return false;
  12907. if ( pBot->HasControlledByPlayer() )
  12908. return false;
  12909. if ( pBot->IsDefusingBomb() )
  12910. return false;
  12911. // Can't control a bot that is setting a bomb
  12912. const CC4 *pC4 = dynamic_cast<CC4*>( pBot->GetActiveWeapon() );
  12913. if ( pC4 && pC4->m_bStartedArming )
  12914. return false;
  12915. if ( CSGameRules()->IsRoundOver() )
  12916. return false;
  12917. if ( CSGameRules()->IsFreezePeriod() )
  12918. return false;
  12919. if ( CSGameRules()->IsWarmupPeriod() )
  12920. return false;
  12921. if ( !bSkipTeamCheck && IsAlive() )
  12922. return false;
  12923. return true;
  12924. }
  12925. bool CCSPlayer::TakeControlOfBot( CCSBot *pBot, bool bSkipTeamCheck )
  12926. {
  12927. if ( !CanControlBot(pBot, bSkipTeamCheck ) )
  12928. return false;
  12929. // First Save off our pre-control settings
  12930. SavePreControlData();
  12931. // don't save for bot, the account of the player who is taking over will transfer back
  12932. //pBot->SavePreControlData();
  12933. // Save off stuff we want from the bot
  12934. // Position / Orientation
  12935. // Appearance
  12936. // Health, Armor, Stamina
  12937. const Vector vecBotPosition = pBot->GetAbsOrigin();
  12938. QAngle vecBotAngles = pBot->GetAbsAngles();
  12939. const int nBotClass = pBot->GetClass();
  12940. const int nBotHealth = pBot->GetHealth();
  12941. const float flBotStamina = pBot->m_flStamina;
  12942. const float flBotVelocityModifier = pBot->m_flVelocityModifier;
  12943. const bool bBotDucked = pBot->m_Local.m_bDucked;
  12944. const bool bBotDucking = pBot->m_Local.m_bDucking;
  12945. const bool bBotFL_DUCKING = (pBot->GetFlags() & FL_DUCKING ) != 0;
  12946. const bool bBotFL_ANIMDUCKING = ( pBot->GetFlags() & FL_ANIMDUCKING ) != 0;
  12947. const float flBotDuckAmount = pBot->m_flDuckAmount;
  12948. const MoveType_t eBotMoveType = pBot->GetMoveType();
  12949. CWeaponCSBase * pBotWeapon = pBot->GetActiveCSWeapon();
  12950. CBaseViewModel * pBotVM = pBot->GetViewModel();
  12951. const float flBotNextAttack = pBot->GetNextAttack();
  12952. const float flBotWeaponNextPrimaryAttack = pBotWeapon ? pBotWeapon->m_flNextPrimaryAttack : gpGlobals->curtime;
  12953. const float flBotWeaponNextSecondaryAttack = pBotWeapon ? pBotWeapon->m_flNextSecondaryAttack : gpGlobals->curtime;
  12954. const float flBotWeaponTimeWeaponIdle = pBotWeapon ? pBotWeapon->m_flTimeWeaponIdle : gpGlobals->curtime;
  12955. const bool bBotWeaponInReload = pBotWeapon ? pBotWeapon->m_bInReload : false;
  12956. //char szBotAnimExtension[32]; pBot->m_szAnimExtension;
  12957. //pBotWeapon->m_IdealActivity;
  12958. //pBotWeapon->m_nIdealSequence;
  12959. const Activity eBotWeaponActivity = pBotWeapon ? pBotWeapon->GetActivity() : ACT_IDLE;
  12960. //pBotWeapon->m_nSequence;
  12961. const float flBotWeaponCycle = pBotWeapon ? pBotWeapon->GetCycle() : 0.0f;
  12962. const float flBotVMCycle = pBotVM ? pBotVM->GetCycle() : 0.0f;
  12963. char szBotWeaponClassname[64];
  12964. szBotWeaponClassname[0] = 0;
  12965. if ( pBotWeapon )
  12966. {
  12967. V_strncpy( szBotWeaponClassname, pBotWeapon->GetClassname(), sizeof(szBotWeaponClassname ) );
  12968. }
  12969. //const Activity eBotActivity = GetActivity();
  12970. //pBotWeapon->m_flAccuracy;
  12971. //pBot->m_iShotsFired;
  12972. //pBotWeapon->m_bDelayFire;
  12973. if ( bSkipTeamCheck && pBot->GetTeamNumber() != GetTeamNumber() )
  12974. {
  12975. // player needs to switch teams before controlling this bot
  12976. SwitchTeam( pBot->GetTeamNumber() );
  12977. }
  12978. if ( bSkipTeamCheck && HasControlledBot() )
  12979. {
  12980. CCSBot *pOldBot = ToCSBot( GetControlledBot() );
  12981. pBot->SwitchTeam( GetTeamNumber() );
  12982. ReleaseControlOfBot();
  12983. pOldBot->Spawn();
  12984. pOldBot->Teleport( &GetAbsOrigin(), &GetAbsAngles(), &vec3_origin );
  12985. pOldBot->State_Transition( STATE_ACTIVE );
  12986. }
  12987. // Next set the control EHANDLEs
  12988. SetControlledBot( pBot );
  12989. pBot->SetControlledByPlayer( this );
  12990. m_bIsControllingBot = true;
  12991. m_iControlledBotEntIndex = pBot->entindex();
  12992. // [wills] Trying to squash T-pose orphaned wearables. Note: it isn't great to remove wearables all over the place,
  12993. // since it may trigger an unnecessary texture re-composite, which is potentially costly.
  12994. // RemoveAllWearables();
  12995. // If we have a ragdoll, cut it loose now
  12996. if ( CCSRagdoll *pRagdoll = dynamic_cast< CCSRagdoll* >( m_hRagdoll.Get() ) )
  12997. {
  12998. pRagdoll->m_hPlayer = NULL;
  12999. m_hRagdoll = NULL;
  13000. }
  13001. // Now copy over various things from the bot
  13002. m_iClass = nBotClass;
  13003. // Make the bot dormant, so he no longer thinks, transmits, or simulates
  13004. pBot->MakeDormant();
  13005. pBot->State_Transition( STATE_DORMANT );
  13006. pBot->m_iHealth = 0;
  13007. pBot->m_lifeState = LIFE_DEAD;
  13008. pBot->RemoveAllWearables();
  13009. pBot->m_flVelocityModifier = 0.0f;
  13010. m_flVelocityModifier = flBotVelocityModifier; // GET FROM BOT?!?!?
  13011. // Finally, run some normal spawn logic
  13012. // Here, I'm trying to copy what happens when we call CCSPlayer::Spawn, in some places
  13013. // using values from the bot rather than init values
  13014. StopObserverMode();
  13015. State_Transition( STATE_ACTIVE );
  13016. bool hasChangedTeamTemp = m_bTeamChanged;
  13017. int numBotsControlled = m_botsControlled;
  13018. // HACK: Bots sometimes have some roll applied when the player takes them over due to acceleration lean
  13019. // which gets stuck on when the player takes them over. Easiest just to clear the roll on the bot when taking over
  13020. vecBotAngles.z = 0;
  13021. SetCSSpawnLocation( vecBotPosition, vecBotAngles );
  13022. Spawn();
  13023. m_bHasControlledBotThisRound = true;
  13024. pBot->m_bHasBeenControlledByPlayerThisRound = true;
  13025. m_fImmuneToGunGameDamageTime = 0;
  13026. m_bGunGameImmunity = false;
  13027. m_bTeamChanged = hasChangedTeamTemp; // dkorus: we want m_bTeamChanged to persist past the Spawn() call. This is how we acomplish this
  13028. m_botsControlled = numBotsControlled;
  13029. m_flStamina = flBotStamina; // FROM BOT
  13030. State_Transition( m_iPlayerState );
  13031. pBot->TransferInventory( this );
  13032. m_iHealth = nBotHealth;
  13033. m_lifeState = LIFE_ALIVE;
  13034. m_nNumFastDucks = 0;
  13035. m_bDuckOverride = false;
  13036. // afk check disabled for players whose first action is taking over a bot
  13037. m_bHasMovedSinceSpawn = true;
  13038. SetMoveType( eBotMoveType );
  13039. m_Local.m_bDucked = bBotDucked;
  13040. m_Local.m_bDucking = bBotDucking;
  13041. if ( bBotFL_DUCKING )
  13042. AddFlag( FL_DUCKING );
  13043. else
  13044. RemoveFlag( FL_DUCKING );
  13045. if ( bBotFL_ANIMDUCKING )
  13046. AddFlag( FL_ANIMDUCKING );
  13047. else
  13048. RemoveFlag( FL_ANIMDUCKING );
  13049. m_flDuckAmount = flBotDuckAmount;
  13050. pBot->DispatchUpdateTransmitState();
  13051. DispatchUpdateTransmitState();
  13052. CBaseCombatWeapon* pWeapon = pBotWeapon ? CSWeapon_OwnsThisType( pBotWeapon->GetEconItemView() ) : NULL;
  13053. if ( pWeapon )
  13054. {
  13055. Weapon_Switch( pWeapon );
  13056. pWeapon->SendWeaponAnim( eBotWeaponActivity );
  13057. pWeapon->SetCycle( flBotWeaponCycle );
  13058. pWeapon->m_flTimeWeaponIdle = flBotWeaponTimeWeaponIdle;
  13059. pWeapon->m_flNextPrimaryAttack = flBotWeaponNextPrimaryAttack;
  13060. pWeapon->m_flNextSecondaryAttack = flBotWeaponNextSecondaryAttack;
  13061. pWeapon->m_bInReload = bBotWeaponInReload;
  13062. if ( CBaseViewModel * pVM = GetViewModel() )
  13063. {
  13064. pVM->SetCycle( flBotVMCycle );
  13065. }
  13066. SetNextAttack( flBotNextAttack );
  13067. }
  13068. if ( pBot->IsRescuing() )
  13069. {
  13070. // Tell the hostages controlled by the bot that they should now follow this player
  13071. for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ )
  13072. {
  13073. CHostage *pHostage = g_Hostages[iHostage];
  13074. if ( pHostage && pHostage->GetLeader() == pBot )
  13075. {
  13076. pHostage->Follow( this );
  13077. }
  13078. }
  13079. if ( HOSTAGE_RULE_CAN_PICKUP && pBot->m_hCarriedHostageProp != NULL )
  13080. {
  13081. // transfer any carried hostages and refresh the viewmodel
  13082. CHostageCarriableProp *pHostageProp = static_cast< CHostageCarriableProp* >( pBot->m_hCarriedHostageProp.Get() );
  13083. if ( pHostageProp )
  13084. {
  13085. pBot->m_hCarriedHostageProp = NULL;
  13086. pHostageProp->SetAbsOrigin( GetAbsOrigin() );
  13087. pHostageProp->SetParent( this );
  13088. pHostageProp->SetOwnerEntity( this );
  13089. pHostageProp->FollowEntity( this );
  13090. m_hCarriedHostageProp = pHostageProp;
  13091. }
  13092. CBaseViewModel *vm = pBot->GetViewModel( 1 );
  13093. UTIL_Remove( vm );
  13094. pBot->m_hViewModel.Set( 1, INVALID_EHANDLE );
  13095. }
  13096. }
  13097. RefreshCarriedHostage( true );
  13098. m_botsControlled++;
  13099. IGameEvent * event = gameeventmanager->CreateEvent( "bot_takeover" );
  13100. if ( event )
  13101. {
  13102. event->SetInt( "userid", GetUserID() );
  13103. event->SetInt( "botid", pBot->GetUserID() );
  13104. event->SetInt( "index", GetClientIndex() );
  13105. gameeventmanager->FireEvent( event );
  13106. }
  13107. return true;
  13108. }
  13109. void CCSPlayer::ReleaseControlOfBot()
  13110. {
  13111. if( m_bIsControllingBot == false )
  13112. return;
  13113. CCSBot *pBot = ToCSBot( m_hControlledBot.Get() );
  13114. if ( pBot )
  13115. {
  13116. pBot->SetControlledByPlayer( NULL );
  13117. TransferInventory( pBot );
  13118. Msg( " %s RELEASED CONTROL of %s\n", GetPlayerName(), pBot->GetPlayerName() );
  13119. pBot->RemoveEFlags( EFL_DORMANT );
  13120. }
  13121. else
  13122. {
  13123. // dkorus: make sure we clear out any items the player has and reset states. This makes sure he doesn't keep the bot's items into the next round
  13124. SetArmorValue( 0 );
  13125. m_bHasHelmet = false;
  13126. m_bHasHeavyArmor = false;
  13127. m_bHasNightVision = false;
  13128. m_bNightVisionOn = false;
  13129. RemoveAllItems( true );
  13130. }
  13131. m_iClass = m_PreControlData.m_iClass;
  13132. m_iAccount = m_PreControlData.m_iAccount;
  13133. m_iAccountMoneyEarnedForNextRound = m_PreControlData.m_iAccountMoneyEarnedForNextRound;
  13134. if ( dev_reportmoneychanges.GetBool() )
  13135. Msg( "**** %s (total: %d) Restoring m_PreControlData from %s\n", GetPlayerName(), m_PreControlData.m_iAccount, pBot->GetPlayerName() );
  13136. SetControlledBot( NULL );
  13137. //UpdateAppearanceIndex();
  13138. m_bIsControllingBot = false;
  13139. m_iControlledBotEntIndex = -1;
  13140. DispatchUpdateTransmitState();
  13141. }
  13142. /*
  13143. CBaseEntity * CCSPlayer::FindNearestThrownGrenade(bool bReverse)
  13144. {
  13145. // early out if the option is disabled by the server
  13146. if ( !cv_bot_controllable.GetBool() )
  13147. return NULL;
  13148. float32 flNearestDistSqr = 0.0f;
  13149. CCSBot *pNearestBot = NULL;
  13150. for ( int idx = 1; idx <= gpGlobals->maxClients; ++idx )
  13151. {
  13152. CCSBot *pBot = ToCSBot( UTIL_PlayerByIndex( idx ) );
  13153. if ( !pBot )
  13154. continue;
  13155. if ( !CanControlBot( pBot ) )
  13156. continue;
  13157. if ( bMustBeValidObserverTarget && !IsValidObserverTarget( pBot ) )
  13158. continue;
  13159. const float flDistSqr = GetAbsOrigin().DistToSqr( pBot->GetAbsOrigin() );
  13160. if ( pNearestBot == NULL || flDistSqr < flNearestDistSqr )
  13161. {
  13162. flNearestDistSqr = flDistSqr;
  13163. pNearestBot = pBot;
  13164. }
  13165. }
  13166. return pNearestBot;
  13167. }
  13168. */
  13169. CCSBot* CCSPlayer::FindNearestControllableBot( bool bMustBeValidObserverTarget )
  13170. {
  13171. // early out if the option is disabled by the server
  13172. if ( !cv_bot_controllable.GetBool() )
  13173. return NULL;
  13174. float32 flNearestDistSqr = 0.0f;
  13175. CCSBot *pNearestBot = NULL;
  13176. for ( int idx = 1; idx <= gpGlobals->maxClients; ++idx )
  13177. {
  13178. CCSBot *pBot = ToCSBot( UTIL_PlayerByIndex( idx ) );
  13179. if ( !pBot )
  13180. continue;
  13181. if ( !CanControlBot(pBot ) )
  13182. continue;
  13183. if ( bMustBeValidObserverTarget && !IsValidObserverTarget(pBot ) )
  13184. continue;
  13185. const float flDistSqr = GetAbsOrigin().DistToSqr( pBot->GetAbsOrigin() );
  13186. if ( pNearestBot == NULL || flDistSqr < flNearestDistSqr )
  13187. {
  13188. flNearestDistSqr = flDistSqr;
  13189. pNearestBot = pBot;
  13190. }
  13191. }
  13192. return pNearestBot;
  13193. }
  13194. #endif // CONTROLABLE BOTS ENABLED
  13195. void CCSPlayer::UpdateInventory( bool bInit )
  13196. {
  13197. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  13198. if ( IsFakeClient() )
  13199. return;
  13200. if ( bInit || !m_Inventory.GetSOC() )
  13201. {
  13202. if ( steamgameserverapicontext->SteamGameServer() )
  13203. {
  13204. CSteamID steamIDForPlayer;
  13205. if ( GetSteamID( &steamIDForPlayer ) )
  13206. {
  13207. CSInventoryManager()->SteamRequestInventory( &m_Inventory, steamIDForPlayer );
  13208. }
  13209. }
  13210. }
  13211. // If we have an SOCache, we've got a connection to the GC
  13212. bool bInvalid = true;
  13213. if ( m_Inventory.GetSOC() )
  13214. {
  13215. bInvalid = m_Inventory.GetSOC()->BIsInitialized() == false;
  13216. }
  13217. //m_Shared.SetLoadoutUnavailable( bInvalid );
  13218. #endif
  13219. }
  13220. //-----------------------------------------------------------------------------
  13221. // Purpose: Request this player's inventories from the steam backend
  13222. //-----------------------------------------------------------------------------
  13223. void CCSPlayer::UpdateOnRemove( void )
  13224. {
  13225. BaseClass::UpdateOnRemove();
  13226. #if !defined(NO_STEAM) && !defined( NO_STEAM_GAMECOORDINATOR )
  13227. m_Inventory.RemoveListener( this );
  13228. m_Inventory.Shutdown();
  13229. #endif
  13230. }
  13231. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  13232. //-----------------------------------------------------------------------------
  13233. // Purpose: Steam has just notified us that the player changed his inventory
  13234. //-----------------------------------------------------------------------------
  13235. void CCSPlayer::InventoryUpdated( CPlayerInventory *pInventory )
  13236. {
  13237. UpdateEquippedCoinFromInventory();
  13238. UpdateEquippedMusicFromInventory();
  13239. UpdateEquippedPlayerSprayFromInventory();
  13240. UpdatePersonaDataFromInventory();
  13241. //m_Shared.SetLoadoutUnavailable( false );
  13242. // Make sure we're wearing the right skin.
  13243. //SetPlayerModel();
  13244. GiveDefaultItems();
  13245. }
  13246. void OnInventoryUpdatedForSteamID( CSteamID steamID )
  13247. {
  13248. if ( !steamID.IsValid() ) return;
  13249. if ( !steamID.BIndividualAccount() ) return;
  13250. if ( !steamID.GetAccountID() ) return;
  13251. extern CCSPlayer* FindPlayerFromAccountID( uint32 account_id );
  13252. if ( CCSPlayer *pPlayer = FindPlayerFromAccountID( steamID.GetAccountID() ) )
  13253. {
  13254. pPlayer->UpdateEquippedCoinFromInventory();
  13255. pPlayer->UpdateEquippedMusicFromInventory();
  13256. pPlayer->UpdateEquippedPlayerSprayFromInventory();
  13257. pPlayer->UpdatePersonaDataFromInventory();
  13258. }
  13259. }
  13260. //-----------------------------------------------------------------------------
  13261. // Purpose: Requests that the GC confirm that this player is supposed to have
  13262. // an SO cache on this gameserver and send it again if so.
  13263. //-----------------------------------------------------------------------------
  13264. void CCSPlayer::VerifySOCache()
  13265. {
  13266. /** Removed for partner depot **/
  13267. return;
  13268. }
  13269. #endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  13270. void CCSPlayer::IncrementFragCount( int nCount, int nHeadshots )
  13271. {
  13272. // calculate frag count properly for a bot-controlled player
  13273. if( IsControllingBot() )
  13274. {
  13275. CCSPlayer* controlledPlayerScorer = GetControlledBot();
  13276. if( controlledPlayerScorer )
  13277. {
  13278. controlledPlayerScorer->IncrementFragCount( nCount, nHeadshots );
  13279. }
  13280. // Keep track in QMM data for aggregate kills even when using a bot
  13281. if ( m_uiAccountId && CSGameRules() && !CSGameRules()->IsWarmupPeriod() )
  13282. {
  13283. ++ m_iEnemyKillsAgg;
  13284. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13285. {
  13286. CCSGameRules::CQMMPlayerData_t &qmmPlayerData = *pQMM;
  13287. qmmPlayerData.m_numEnemyKillsAgg = m_iEnemyKillsAgg;
  13288. }
  13289. }
  13290. return;
  13291. }
  13292. m_iFrags += nCount;
  13293. pl.frags = m_iFrags;
  13294. if ( nCount == -1 )
  13295. {
  13296. ++m_iNumRoundTKs;
  13297. }
  13298. else if ( ( nCount == 1 ) && ( nHeadshots != -1 ) )
  13299. {
  13300. ++m_iNumRoundKills;
  13301. if ( nHeadshots == 1 )
  13302. {
  13303. ++m_iNumRoundKillsHeadshots;
  13304. }
  13305. if ( CSGameRules() && !CSGameRules()->IsWarmupPeriod() )
  13306. {
  13307. ++ m_iEnemyKills;
  13308. ++ m_iEnemyKillsAgg;
  13309. if ( nHeadshots == 1 )
  13310. {
  13311. ++ m_iEnemyKillHeadshots;
  13312. }
  13313. if ( m_iNumRoundKills == 3 ) // We are now 3K!
  13314. {
  13315. ++ m_iEnemy3Ks;
  13316. }
  13317. else if ( m_iNumRoundKills == 4 ) // We are now 4K!
  13318. {
  13319. -- m_iEnemy3Ks;
  13320. ++ m_iEnemy4Ks;
  13321. }
  13322. else if ( m_iNumRoundKills == 5 ) // We are now 5K!
  13323. {
  13324. -- m_iEnemy4Ks;
  13325. ++ m_iEnemy5Ks;
  13326. }
  13327. if ( CSGameRules()->m_bNoEnemiesKilled )
  13328. {
  13329. CSGameRules()->m_bNoEnemiesKilled = false;
  13330. ++ m_numFirstKills;
  13331. DevMsg( "Player '%s'[%08X] got first kill of the round.\n",
  13332. GetPlayerName(), GetHumanPlayerAccountID() );
  13333. }
  13334. int nMyTeamID = GetTeamNumber();
  13335. if ( ( nMyTeamID == TEAM_TERRORIST ) || ( nMyTeamID == TEAM_CT ) )
  13336. {
  13337. // Is this a clutch kill? There must be no alive teammates if so:
  13338. bool bFoundAliveTeammates = false;
  13339. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  13340. {
  13341. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  13342. if ( pPlayer && ( pPlayer != this ) && pPlayer->IsAlive()
  13343. && ( pPlayer->GetTeamNumber() == nMyTeamID ) )
  13344. {
  13345. bFoundAliveTeammates = true;
  13346. break;
  13347. }
  13348. }
  13349. if ( !bFoundAliveTeammates )
  13350. {
  13351. ++ m_numClutchKills;
  13352. }
  13353. }
  13354. }
  13355. if ( CSGameRules()->ShouldRecordMatchStats() )
  13356. {
  13357. ++ m_iMatchStats_Kills.GetForModify( CSGameRules()->GetRoundsPlayed() );
  13358. ( nHeadshots == 1 ) ? ++ m_iMatchStats_HeadShotKills.GetForModify( CSGameRules()->GetRoundsPlayed() ) : 0;
  13359. }
  13360. }
  13361. // calculate killstreak.
  13362. //if ( gpGlobals->curtime < GetLastKillTime() + 8.0 )
  13363. {
  13364. IncrementKillStreak( nCount );
  13365. }
  13366. //else
  13367. //{
  13368. // ResetKillStreak();
  13369. //}
  13370. SetLastKillTime( gpGlobals->curtime );
  13371. // Keep track in QMM data
  13372. if ( m_uiAccountId && CSGameRules() )
  13373. {
  13374. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13375. {
  13376. CCSGameRules::CQMMPlayerData_t &qmmPlayerData = *pQMM;
  13377. qmmPlayerData.m_numKills = m_iFrags;
  13378. qmmPlayerData.m_numEnemyKills = m_iEnemyKills;
  13379. qmmPlayerData.m_numEnemyKillHeadshots = m_iEnemyKillHeadshots;
  13380. qmmPlayerData.m_numEnemy3Ks = m_iEnemy3Ks;
  13381. qmmPlayerData.m_numEnemy4Ks = m_iEnemy4Ks;
  13382. qmmPlayerData.m_numEnemy5Ks = m_iEnemy5Ks;
  13383. qmmPlayerData.m_numEnemyKillsAgg = m_iEnemyKillsAgg;
  13384. qmmPlayerData.m_numFirstKills = m_numFirstKills;
  13385. qmmPlayerData.m_numClutchKills = m_numClutchKills;
  13386. qmmPlayerData.m_numPistolKills = m_numPistolKills;
  13387. qmmPlayerData.m_numSniperKills = m_numSniperKills;
  13388. if ( CSGameRules()->ShouldRecordMatchStats() )
  13389. {
  13390. qmmPlayerData.m_iMatchStats_Kills[ CSGameRules()->GetRoundsPlayed() ] = m_iMatchStats_Kills.Get( CSGameRules()->GetRoundsPlayed() );
  13391. qmmPlayerData.m_iMatchStats_HeadShotKills[ CSGameRules()->GetRoundsPlayed() ] = m_iMatchStats_HeadShotKills.Get( CSGameRules()->GetRoundsPlayed() );
  13392. }
  13393. }
  13394. }
  13395. }
  13396. void CCSPlayer::IncrementTeamKillsCount( int nCount )
  13397. {
  13398. m_iTeamKills += nCount;
  13399. // Keep track in QMM data
  13400. if ( m_uiAccountId && CSGameRules() )
  13401. {
  13402. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13403. {
  13404. pQMM->m_numTeamKills = m_iTeamKills;
  13405. }
  13406. }
  13407. }
  13408. void CCSPlayer::IncrementHostageKillsCount( int nCount )
  13409. {
  13410. m_iHostagesKilled += nCount;
  13411. // Keep track in QMM data
  13412. if ( m_uiAccountId && CSGameRules() )
  13413. {
  13414. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13415. {
  13416. pQMM->m_numHostageKills = m_iHostagesKilled;
  13417. }
  13418. }
  13419. }
  13420. void CCSPlayer::IncrementTeamDamagePoints( int numDamagePoints )
  13421. {
  13422. m_nTeamDamageGivenForMatch += numDamagePoints;
  13423. // Keep track in QMM data
  13424. if ( m_uiAccountId && CSGameRules() )
  13425. {
  13426. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13427. {
  13428. pQMM->m_numTeamDamagePoints = m_nTeamDamageGivenForMatch;
  13429. }
  13430. }
  13431. }
  13432. void CCSPlayer::IncrementAssistsCount( int nCount )
  13433. {
  13434. // calculate assists properly for a bot-controlled player
  13435. if( IsControllingBot() )
  13436. {
  13437. CCSPlayer* controlledPlayerScorer = GetControlledBot();
  13438. if( controlledPlayerScorer )
  13439. {
  13440. controlledPlayerScorer->IncrementAssistsCount( nCount );
  13441. }
  13442. return;
  13443. }
  13444. m_iAssists += nCount;
  13445. pl.assists = m_iAssists;
  13446. if ( CSGameRules()->ShouldRecordMatchStats() )
  13447. {
  13448. ++ m_iMatchStats_Assists.GetForModify( CSGameRules()->GetTotalRoundsPlayed() );
  13449. }
  13450. // Keep track in QMM data
  13451. if ( m_uiAccountId && CSGameRules() )
  13452. {
  13453. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13454. {
  13455. pQMM->m_numAssists = m_iAssists;
  13456. if ( CSGameRules()->ShouldRecordMatchStats() )
  13457. {
  13458. pQMM->m_iMatchStats_Assists[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_Assists.GetForModify( CSGameRules()->GetTotalRoundsPlayed() );
  13459. }
  13460. }
  13461. }
  13462. }
  13463. void CCSPlayer::IncrementDeathCount( int nCount )
  13464. {
  13465. // calculate death count properly for a bot-controlled player
  13466. if( IsControllingBot() )
  13467. {
  13468. CCSPlayer* controlledPlayerScorer = GetControlledBot();
  13469. if( controlledPlayerScorer )
  13470. {
  13471. controlledPlayerScorer->IncrementDeathCount( nCount );
  13472. }
  13473. return;
  13474. }
  13475. m_iDeaths += nCount;
  13476. pl.deaths = m_iDeaths;
  13477. if ( CSGameRules()->ShouldRecordMatchStats() )
  13478. {
  13479. ++ m_iMatchStats_Deaths.GetForModify( CSGameRules()->GetTotalRoundsPlayed() );
  13480. }
  13481. // Keep track in QMM data
  13482. if ( m_uiAccountId && CSGameRules() )
  13483. {
  13484. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13485. {
  13486. pQMM->m_numDeaths = m_iDeaths;
  13487. if ( CSGameRules()->ShouldRecordMatchStats() )
  13488. {
  13489. pQMM->m_iMatchStats_Deaths[ CSGameRules()->GetTotalRoundsPlayed() ] = m_iMatchStats_Deaths.Get( CSGameRules()->GetTotalRoundsPlayed() );
  13490. }
  13491. }
  13492. }
  13493. }
  13494. void CCSPlayer::SetLastKillTime( float time )
  13495. {
  13496. m_flLastKillTime = time;
  13497. }
  13498. float CCSPlayer::GetLastKillTime()
  13499. {
  13500. return m_flLastKillTime;
  13501. }
  13502. void CCSPlayer::IncrementKillStreak( int nCount )
  13503. {
  13504. m_iKillStreak += nCount;
  13505. if ( CSGameRules()->IsPlayingGunGameProgressive() && m_iKillStreak > 1 )
  13506. {
  13507. char strStreak[64];
  13508. Q_snprintf( strStreak, sizeof( strStreak ), "%d", m_iKillStreak );
  13509. if ( m_iKillStreak >= 4 )
  13510. {
  13511. Radio( "OnARollBrag" );
  13512. ClientPrint( this, HUD_PRINTCENTER, "#Player_Killing_Spree_more", strStreak );
  13513. CRecipientFilter filter;
  13514. filter.AddAllPlayers();
  13515. filter.MakeReliable();
  13516. CFmtStr fmtEntName( "#ENTNAME[%d]%s", entindex(), GetPlayerName() );
  13517. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#Player_On_Killing_Spree", fmtEntName.Access(), strStreak );
  13518. }
  13519. // else if ( m_iKillStreak >= 4 )
  13520. // {
  13521. // ClientPrint( this, HUD_PRINTCENTER, "#Player_Killing_Spree_4" );
  13522. // }
  13523. // else if ( m_iKillStreak >= 3 )
  13524. // {
  13525. // ClientPrint( this, HUD_PRINTCENTER, "#Player_Killing_Spree_3" );
  13526. // }
  13527. // else
  13528. // {
  13529. // ClientPrint( this, HUD_PRINTCENTER, "#Player_Killing_Spree_2" );
  13530. // }
  13531. }
  13532. }
  13533. void CCSPlayer::ResetKillStreak()
  13534. {
  13535. m_iKillStreak = 0;
  13536. }
  13537. int CCSPlayer::GetKillStreak()
  13538. {
  13539. return m_iKillStreak;
  13540. }
  13541. void CCSPlayer::AddContributionScore( int iPoints )
  13542. {
  13543. // calculate score count properly for a bot-controlled player
  13544. if( IsControllingBot() )
  13545. {
  13546. CCSPlayer* controlledPlayerScorer = GetControlledBot();
  13547. if( controlledPlayerScorer )
  13548. {
  13549. controlledPlayerScorer->AddContributionScore( iPoints );
  13550. }
  13551. }
  13552. else
  13553. {
  13554. m_iContributionScore += iPoints;
  13555. AddRoundContributionScore( iPoints );
  13556. // note, the round score isn't capped to be positive... on any given round we expect that it may go negative (example: for determining griefers )
  13557. }
  13558. if ( m_iContributionScore < 0 )
  13559. m_iContributionScore = 0; // cap negative points at zero
  13560. pl.score = m_iContributionScore;
  13561. // Keep track in QMM data
  13562. if ( m_uiAccountId && CSGameRules() )
  13563. {
  13564. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( m_uiAccountId ) )
  13565. {
  13566. pQMM->m_numScorePoints = m_iContributionScore;
  13567. }
  13568. }
  13569. }
  13570. void CCSPlayer::AddScore( int iPoints )
  13571. {
  13572. // calculate score count properly for a bot-controlled player
  13573. if( IsControllingBot() )
  13574. {
  13575. CCSPlayer* controlledPlayerScorer = GetControlledBot();
  13576. if( controlledPlayerScorer )
  13577. {
  13578. controlledPlayerScorer->AddScore( iPoints );
  13579. }
  13580. }
  13581. else
  13582. {
  13583. m_iScore += iPoints;
  13584. // note, the round score isn't capped to be positive... on any given round we expect that it may go negative (example: determining griefers )
  13585. }
  13586. if ( m_iScore < 0 )
  13587. m_iScore = 0; // cap negative points at zero
  13588. }
  13589. void CCSPlayer::AddRoundContributionScore( int iPoints )
  13590. {
  13591. m_iRoundContributionScore += iPoints;
  13592. }
  13593. void CCSPlayer::AddRoundProximityScore( int iPoints )
  13594. {
  13595. m_iRoundProximityScore += iPoints;
  13596. }
  13597. int CCSPlayer::GetNumConcurrentDominations( )
  13598. {
  13599. //Check concurrent dominations achievement
  13600. int numConcurrentDominations = 0;
  13601. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  13602. {
  13603. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  13604. if ( pPlayer && IsPlayerDominated( pPlayer->entindex() ) )
  13605. {
  13606. numConcurrentDominations++;
  13607. }
  13608. }
  13609. return numConcurrentDominations;
  13610. }
  13611. CON_COMMAND_F( observer_use, "", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  13612. {
  13613. CCSPlayer *pPlayer = ToCSPlayer( UTIL_GetCommandClient() );
  13614. if ( pPlayer )
  13615. pPlayer->ObserverUse( true );
  13616. }
  13617. //This effectively disables the rendering of the flashbang effect,
  13618. //but allows the server to finish and game rules processing.
  13619. //(Used to hide effect at the end of a match so that players can see the scoreboard. )
  13620. void CCSPlayer::Unblind( void )
  13621. {
  13622. m_flFlashDuration = 0.0f;
  13623. m_flFlashMaxAlpha = 0.0f;
  13624. }
  13625. // the client already has a players account, sending a team only message doesn't make it more secure and it breaks GOTV
  13626. // void CCSPlayer::UpdateTeamMoney()
  13627. // {
  13628. // if ( ( m_flLastMoneyUpdateTime + 0.1f ) > gpGlobals->curtime )
  13629. // return;
  13630. //
  13631. // m_flLastMoneyUpdateTime = gpGlobals->curtime;
  13632. //
  13633. // CSingleUserRecipientFilter user( this );
  13634. // UserMessageBegin( user, "UpdateTeamMoney" );
  13635. //
  13636. // for ( int i=0; i < MAX_PLAYERS; i++ )
  13637. // {
  13638. // CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i+1 ) );
  13639. //
  13640. // if ( !pPlayer || !pPlayer->IsConnected() )
  13641. // continue;
  13642. //
  13643. // if ( pPlayer->GetTeamNumber() != GetTeamNumber() && GetTeamNumber() != TEAM_SPECTATOR )
  13644. // continue;
  13645. //
  13646. // WRITE_BYTE( i+1 ); // player index as entity
  13647. // WRITE_SHORT( pPlayer->GetAccountForScoreboard() );
  13648. //
  13649. // }
  13650. //
  13651. // WRITE_BYTE( 0 ); // end marker
  13652. //
  13653. // MessageEnd();
  13654. // }
  13655. int CCSPlayer::GetAccountForScoreboard()
  13656. {
  13657. if ( IsControllingBot() )
  13658. return m_PreControlData.m_iAccount;
  13659. CCSPlayer* pControllingPlayer = GetControlledByPlayer();
  13660. if ( pControllingPlayer != NULL )
  13661. return pControllingPlayer->m_iAccount;
  13662. return m_iAccount;
  13663. }
  13664. void CCSPlayer::UpdateRankFromKV( KeyValues *pKV )
  13665. {
  13666. if ( !pKV )
  13667. return;
  13668. for ( int i = 0; i < MEDAL_CATEGORY_ACHIEVEMENTS_END; ++i )
  13669. {
  13670. int rank = pKV->GetInt( CFmtStr( "rank%d", i), -1 );
  13671. if ( rank >= 0 && rank < MEDAL_RANK_COUNT )
  13672. {
  13673. SetRank( (MedalCategory_t)i, (MedalRank_t)rank );
  13674. }
  13675. }
  13676. }
  13677. void CCSPlayer::SetRank( MedalCategory_t category, MedalRank_t rank )
  13678. {
  13679. m_rank.Set( category, rank );
  13680. }
  13681. void CCSPlayer::UpdateEquippedCoinFromInventory()
  13682. {
  13683. m_bNeedToUpdateCoinFromInventory = true;
  13684. }
  13685. void CCSPlayer::SetMusicID( uint16 unMusicID )
  13686. {
  13687. m_unMusicID.Set( unMusicID );
  13688. }
  13689. void CCSPlayer::UpdateEquippedMusicFromInventory()
  13690. {
  13691. m_bNeedToUpdateMusicFromInventory = true;
  13692. }
  13693. void CCSPlayer::UpdateEquippedPlayerSprayFromInventory()
  13694. {
  13695. m_bNeedToUpdatePlayerSprayFromInventory = true;
  13696. }
  13697. void CCSPlayer::UpdatePersonaDataFromInventory()
  13698. {
  13699. m_bNeedToUpdatePersonaDataPublicFromInventory = true;
  13700. }
  13701. CEconPersonaDataPublic const * CCSPlayer::GetPersonaDataPublic() const
  13702. {
  13703. return m_pPersonaDataPublic;
  13704. }
  13705. bool CCSPlayer::CanKickFromTeam( int kickTeam )
  13706. {
  13707. int oppositeKickTeam = ( kickTeam == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST;
  13708. // Address issue with bots getting kicked during half-time (this was resulting in bots from the wrong team being kicked)
  13709. return ( ( GetTeamNumber() == kickTeam && !WillSwitchTeamsAtRoundReset() ) ||
  13710. ( GetTeamNumber() == oppositeKickTeam && WillSwitchTeamsAtRoundReset() ) );
  13711. }
  13712. bool CCSPlayer::CanHearAndReadChatFrom( CBasePlayer *pPlayer )
  13713. {
  13714. // can always hear the console unless we're ignoring all chat
  13715. if ( !pPlayer )
  13716. return m_iIgnoreGlobalChat != CHAT_IGNORE_EVERYTHING;
  13717. // check if we're ignoring all chat
  13718. if ( m_iIgnoreGlobalChat == CHAT_IGNORE_BROADCAST_AND_TEAM )
  13719. return false;
  13720. // check if we're ignoring all but teammates
  13721. if ( m_iIgnoreGlobalChat == CHAT_IGNORE_BROADCAST && IsOtherEnemy( pPlayer->entindex() ) )
  13722. return false;
  13723. return true;
  13724. }
  13725. void CCSPlayer::ObserverUse( bool bIsPressed )
  13726. {
  13727. if ( !bIsPressed )
  13728. return;
  13729. #if CS_CONTROLLABLE_BOTS_ENABLED
  13730. CBasePlayer * target = ToBasePlayer( GetObserverTarget() );
  13731. if ( target && target->IsBot() )
  13732. {
  13733. if ( m_bCanControlObservedBot )
  13734. {
  13735. CCSPlayer *pPlayer = this;
  13736. CCSBot *pBot = ToCSBot( pPlayer->GetObserverTarget() );
  13737. if ( pBot != NULL && pBot->IsBot() )
  13738. {
  13739. if ( !pPlayer->IsDead() )
  13740. {
  13741. Msg( "Player %s tried to take control of bot %s but was disallowed by the server\n", pPlayer->GetPlayerName(), pBot->GetPlayerName() );
  13742. }
  13743. else if ( !cv_bot_controllable.GetBool() )
  13744. {
  13745. Msg( "Player %s tried to take control of bot %s but was disallowed by the server\n", pPlayer->GetPlayerName(), pBot->GetPlayerName() );
  13746. }
  13747. else if ( ( pPlayer->GetPendingTeamNumber() != TEAM_UNASSIGNED ) && ( pPlayer->GetPendingTeamNumber() != TEAM_INVALID ) && ( pPlayer->GetTeamNumber() != pPlayer->GetPendingTeamNumber() ) )
  13748. {
  13749. Msg( "Player %s tried to take control of bot %s but was disallowed due to a pending team switch\n", pPlayer->GetPlayerName(), pBot->GetPlayerName() );
  13750. }
  13751. else if ( engine->GetClientHltvReplayDelay( pPlayer->entindex() - 1 ) )
  13752. {
  13753. Msg( "Player %s tried to take control of bot %s but was in replay mode\n", pPlayer->GetPlayerName(), pBot->GetPlayerName() );
  13754. }
  13755. else if ( pPlayer->TakeControlOfBot( pBot ) )
  13756. {
  13757. Msg( "Player %s took control bot %s (%d)\n", pPlayer->GetPlayerName(), pBot->GetPlayerName(), pBot->entindex() );
  13758. }
  13759. else
  13760. {
  13761. Msg( "Player %s tried to take control of bot %s but failed\n", pPlayer->GetPlayerName(), pBot->GetPlayerName() );
  13762. }
  13763. }
  13764. else
  13765. {
  13766. Msg( "Player %s tried to take control of bot but none could be found\n", pPlayer->GetPlayerName() );
  13767. }
  13768. return;
  13769. }
  13770. }
  13771. #endif
  13772. BaseClass::ObserverUse( bIsPressed );
  13773. }
  13774. bool CCSPlayer::GetBulletHitLocalBoneOffset( const trace_t &tr, int &boneIndexOut, Vector &vecPositionOut, QAngle &angAngleOut )
  13775. {
  13776. int nBoneIndex = GetHitboxBone( tr.hitbox );
  13777. if ( nBoneIndex < 0 || tr.DidHit() == false )
  13778. return false;
  13779. // build a matrix from the trace hit start and end position
  13780. matrix3x4_t matWorldSpaceBulletHit;
  13781. VectorMatrix( tr.startpos - tr.endpos, matWorldSpaceBulletHit );
  13782. PositionMatrix( tr.endpos, matWorldSpaceBulletHit );
  13783. // get the transform of the bone that owns the hitbox
  13784. matrix3x4_t matBoneToWorldTransform;
  13785. GetBoneTransform( nBoneIndex, matBoneToWorldTransform );
  13786. // get the local transform of the hit transform relative to the bone transform
  13787. matrix3x4_t matHitLocal;
  13788. MatrixInvert( matBoneToWorldTransform, matHitLocal );
  13789. MatrixMultiply( matHitLocal, matWorldSpaceBulletHit, matHitLocal );
  13790. boneIndexOut = nBoneIndex;
  13791. MatrixAngles( matHitLocal, angAngleOut, vecPositionOut );
  13792. return ( angAngleOut.IsValid() && vecPositionOut.IsValid() );
  13793. }
  13794. #if defined( DEBUG_FLASH_BANG )
  13795. void cc_CreateFlashbangAtEyes_f( const CCommand &args )
  13796. {
  13797. CCSPlayer* pPlayer = ToCSPlayer(UTIL_PlayerByIndex(1));
  13798. if ( pPlayer )
  13799. {
  13800. CBaseCombatCharacter *pBaseCombatCharacter = NULL;
  13801. if ( pPlayer->GetObserverTarget() )
  13802. {
  13803. pBaseCombatCharacter = pPlayer->GetObserverTarget()->MyCombatCharacterPointer();
  13804. }
  13805. else
  13806. {
  13807. pBaseCombatCharacter = pPlayer->MyCombatCharacterPointer();
  13808. }
  13809. if ( pBaseCombatCharacter )
  13810. {
  13811. Vector vForward;
  13812. pPlayer->EyeVectors( &vForward );
  13813. Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset() + vForward * 16;
  13814. Vector vecVel = pPlayer->GetAbsVelocity();
  13815. Vector vecThrow = vForward * 100 + (pPlayer->GetAbsVelocity() * 1.25);
  13816. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( WEAPON_FLASHBANG );
  13817. CFlashbangProjectile::Create(
  13818. vecSrc,
  13819. vec3_angle,
  13820. vecThrow,
  13821. AngularImpulse(600,random->RandomInt(-1200,1200),0),
  13822. pBaseCombatCharacter,
  13823. *pWeaponInfo );
  13824. }
  13825. }
  13826. }
  13827. ConCommand cc_CreateFlashbangAtEyes( "CreateFlashbangAtEyes", cc_CreateFlashbangAtEyes_f, "Create a prediction error", FCVAR_CHEAT );
  13828. #endif