Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

22005 lines
668 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for HL1.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "tf_player.h"
  9. #include "tf_gamerules.h"
  10. #include "tf_gamestats.h"
  11. #include "KeyValues.h"
  12. #include "viewport_panel_names.h"
  13. #include "client.h"
  14. #include "team.h"
  15. #include "tf_weaponbase.h"
  16. #include "tf_client.h"
  17. #include "tf_team.h"
  18. #include "tf_viewmodel.h"
  19. #include "tf_item.h"
  20. #include "in_buttons.h"
  21. #include "entity_capture_flag.h"
  22. #include "effect_dispatch_data.h"
  23. #include "te_effect_dispatch.h"
  24. #include "game.h"
  25. #include "tf_weapon_builder.h"
  26. #include "tf_obj.h"
  27. #include "tf_ammo_pack.h"
  28. #include "datacache/imdlcache.h"
  29. #include "particle_parse.h"
  30. #include "props_shared.h"
  31. #include "filesystem.h"
  32. #include "toolframework_server.h"
  33. #include "IEffects.h"
  34. #include "func_respawnroom.h"
  35. #include "networkstringtable_gamedll.h"
  36. #include "team_control_point_master.h"
  37. #include "tf_weapon_pda.h"
  38. #include "sceneentity.h"
  39. #include "fmtstr.h"
  40. #include "tf_weapon_sniperrifle.h"
  41. #include "tf_weapon_minigun.h"
  42. #include "tf_weapon_fists.h"
  43. #include "tf_weapon_shotgun.h"
  44. #include "tf_weapon_lunchbox.h"
  45. #include "tf_weapon_knife.h"
  46. #include "tf_weapon_bottle.h"
  47. #include "tf_weapon_sword.h"
  48. #include "tf_weapon_grenade_pipebomb.h"
  49. #include "tf_weapon_buff_item.h"
  50. #include "tf_weapon_flamethrower.h"
  51. #include "tf_projectile_flare.h"
  52. #include "trigger_area_capture.h"
  53. #include "triggers.h"
  54. #include "tf_weapon_medigun.h"
  55. #include "tf_weapon_invis.h"
  56. #include "hl2orange.spa.h"
  57. #include "te_tfblood.h"
  58. #include "activitylist.h"
  59. #include "cdll_int.h"
  60. #include "econ_entity_creation.h"
  61. #include "tf_weaponbase_gun.h"
  62. #include "team_train_watcher.h"
  63. #include "vgui/ILocalize.h"
  64. #include "tier3/tier3.h"
  65. #include "serverbenchmark_base.h"
  66. #include "trains.h"
  67. #include "tf_fx.h"
  68. #include "recipientfilter.h"
  69. #include "ilagcompensationmanager.h"
  70. #include "dt_utlvector_send.h"
  71. #include "tf_item_wearable.h"
  72. #include "tf_item_powerup_bottle.h"
  73. #include "nav_mesh/tf_nav_mesh.h"
  74. #include "tier0/vprof.h"
  75. #include "econ_gcmessages.h"
  76. #include "tf_gcmessages.h"
  77. #include "tf_obj_sentrygun.h"
  78. #include "tf_weapon_shovel.h"
  79. #include "bot/tf_bot.h"
  80. #include "bot/tf_bot_manager.h"
  81. #include "NextBotUtil.h"
  82. #include "tf_wearable_item_demoshield.h"
  83. #include "tier0/icommandline.h"
  84. #include "entity_healthkit.h"
  85. #include "choreoevent.h"
  86. #include "minigames/tf_duel.h"
  87. #include "tf_bot_temp.h"
  88. #include "tf_objective_resource.h"
  89. #include "tf_weapon_pipebomblauncher.h"
  90. #include "func_achievement.h"
  91. #include "halloween/merasmus/merasmus.h"
  92. #include "inetchannel.h"
  93. #include "tf_wearable_levelable_item.h"
  94. #include "tf_weapon_jar.h"
  95. #include "halloween/tf_weapon_spellbook.h"
  96. #include "soundenvelope.h"
  97. #include "tf_triggers.h"
  98. #include "collisionutils.h"
  99. #include "tf_taunt_prop.h"
  100. #include "eventlist.h"
  101. #include "entity_rune.h"
  102. #include "entity_halloween_pickup.h"
  103. #include "tf_gc_server.h"
  104. #include "tf_logic_halloween_2014.h"
  105. #include "tf_weapon_knife.h"
  106. #include "tf_weapon_grapplinghook.h"
  107. #include "tf_dropped_weapon.h"
  108. #include "tf_passtime_logic.h"
  109. #include "tf_weapon_passtime_gun.h"
  110. #include "player_resource.h"
  111. #include "tf_player_resource.h"
  112. #include "gcsdk/gcclient_sharedobjectcache.h"
  113. #include "tf_party.h"
  114. #ifdef STAGING_ONLY
  115. #include "tf_extra_map_entity.h"
  116. #endif
  117. #ifdef TF_RAID_MODE
  118. #include "bot_npc/bot_npc_decoy.h"
  119. #include "raid/tf_raid_logic.h"
  120. #endif
  121. #include "entity_currencypack.h"
  122. #include "tf_mann_vs_machine_stats.h"
  123. #include "player_vs_environment/tf_upgrades.h"
  124. #include "player_vs_environment/tf_population_manager.h"
  125. #include "tf_revive.h"
  126. #include "tf_logic_halloween_2014.h"
  127. #include "tf_logic_player_destruction.h"
  128. // NVNT haptic utils
  129. #include "haptics/haptic_utils.h"
  130. #include "gc_clientsystem.h"
  131. // memdbgon must be the last include file in a .cpp file!!!
  132. #include "tier0/memdbgon.h"
  133. #pragma warning( disable: 4355 ) // disables ' 'this' : used in base member initializer list'
  134. ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." );
  135. #define DAMAGE_FORCE_SCALE_SELF 9
  136. #define SCOUT_ADD_BIRD_ON_GIB_CHANCE 5
  137. #define MEDIC_RELEASE_DOVE_COUNT 10
  138. #define JUMP_MIN_SPEED 268.3281572999747f
  139. extern bool IsInCommentaryMode( void );
  140. extern void SpawnClientsideFlyingBird( Vector &vecSpawn );
  141. extern ConVar sk_player_head;
  142. extern ConVar sk_player_chest;
  143. extern ConVar sk_player_stomach;
  144. extern ConVar sk_player_arm;
  145. extern ConVar sk_player_leg;
  146. extern ConVar tf_spy_invis_time;
  147. extern ConVar tf_spy_invis_unstealth_time;
  148. extern ConVar tf_stalematechangeclasstime;
  149. extern ConVar tf_gravetalk;
  150. extern ConVar tf_bot_quota_mode;
  151. extern ConVar tf_bot_quota;
  152. extern ConVar halloween_starting_souls;
  153. float GetCurrentGravity( void );
  154. float m_flNextReflectZap = 0.f;
  155. static CTFPlayer *gs_pRecursivePlayerCheck = NULL;
  156. bool CTFPlayer::m_bTFPlayerNeedsPrecache = true;
  157. static const char g_pszIdleKickString[] = "#TF_Idle_kicked";
  158. EHANDLE g_pLastSpawnPoints[TF_TEAM_COUNT];
  159. EHANDLE g_hTestSub;
  160. ConVar tf_playerstatetransitions( "tf_playerstatetransitions", "-2", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "tf_playerstatetransitions <ent index or -1 for all>. Show player state transitions." );
  161. ConVar tf_playergib( "tf_playergib", "1", FCVAR_NOTIFY, "Allow player gibbing. 0: never, 1: normal, 2: always", true, 0, true, 2 );
  162. ConVar tf_damageforcescale_other( "tf_damageforcescale_other", "6.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  163. ConVar tf_damageforcescale_self_soldier_rj( "tf_damageforcescale_self_soldier_rj", "10.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  164. ConVar tf_damageforcescale_self_soldier_badrj( "tf_damageforcescale_self_soldier_badrj", "5.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  165. ConVar tf_damageforcescale_pyro_jump( "tf_damageforcescale_pyro_jump", "8.5", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  166. ConVar tf_damagescale_self_soldier( "tf_damagescale_self_soldier", "0.60", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  167. ConVar tf_damage_range( "tf_damage_range", "0.5", FCVAR_DEVELOPMENTONLY );
  168. ConVar tf_damage_multiplier_blue( "tf_damage_multiplier_blue", "1.0", FCVAR_CHEAT, "All incoming damage to a blue player is multiplied by this value" );
  169. ConVar tf_damage_multiplier_red( "tf_damage_multiplier_red", "1.0", FCVAR_CHEAT, "All incoming damage to a red player is multiplied by this value" );
  170. ConVar tf_max_voice_speak_delay( "tf_max_voice_speak_delay", "1.5", FCVAR_DEVELOPMENTONLY, "Max time after a voice command until player can do another one" );
  171. ConVar tf_allow_player_use( "tf_allow_player_use", "0", FCVAR_NOTIFY, "Allow players to execute +use while playing." );
  172. ConVar tf_deploying_bomb_time( "tf_deploying_bomb_time", "1.90", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Time to deploy bomb before the point of no return." );
  173. ConVar tf_deploying_bomb_delay_time( "tf_deploying_bomb_delay_time", "0.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Time to delay before deploying bomb." );
  174. #ifdef TF_RAID_MODE
  175. ConVar tf_raid_team_size( "tf_raid_team_size", "5", FCVAR_NOTIFY, "Max number of Raiders" );
  176. ConVar tf_raid_respawn_safety_time( "tf_raid_respawn_safety_time", "1.5", FCVAR_NOTIFY, "Number of seconds of invulnerability after respawning" );
  177. ConVar tf_raid_allow_class_change( "tf_raid_allow_class_change", "1", FCVAR_NOTIFY, "If nonzero, allow invaders to change their class after leaving the safe room" );
  178. ConVar tf_raid_use_rescue_closets( "tf_raid_use_rescue_closets", "1", FCVAR_NOTIFY );
  179. ConVar tf_raid_drop_healthkit_chance( "tf_raid_drop_healthkit_chance", "50" ); // , FCVAR_CHEAT );
  180. ConVar tf_boss_battle_team_size( "tf_boss_battle_team_size", "5", FCVAR_NOTIFY, "Max number of players in Boss Battle mode" );
  181. ConVar tf_boss_battle_respawn_safety_time( "tf_boss_battle_respawn_safety_time", "3", FCVAR_NOTIFY, "Number of seconds of invulnerability after respawning" );
  182. ConVar tf_boss_battle_respawn_on_friends( "tf_boss_battle_respawn_on_friends", "1", FCVAR_NOTIFY );
  183. #endif
  184. ConVar tf_mvm_death_penalty( "tf_mvm_death_penalty", "0", FCVAR_NOTIFY | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "How much currency players lose when dying" );
  185. extern ConVar tf_populator_damage_multiplier;
  186. extern ConVar tf_mvm_skill;
  187. #ifdef STAGING_ONLY
  188. ConVar tf_debug_ballistics( "tf_debug_ballistics", "0", FCVAR_CHEAT );
  189. ConVar tf_debug_ballistic_targeting( "tf_debug_ballistic_targeting", "0", FCVAR_CHEAT );
  190. ConVar tf_debug_ballistic_targeting_tolerance( "tf_debug_ballistic_targeting_tolerance", "5", FCVAR_CHEAT );
  191. static Vector tf_debug_ballistic_target( 0, 0, 0 );
  192. ConVar tf_space_thrust_scout( "tf_space_thrust_scout", "40.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  193. ConVar tf_space_thrust_sniper( "tf_space_thrust_sniper", "34.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  194. ConVar tf_space_thrust_spy( "tf_space_thrust_spy", "35.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  195. ConVar tf_space_thrust_pyro( "tf_space_thrust_pyro", "35.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  196. ConVar tf_space_thrust_soldier( "tf_space_thrust_soldier", "33.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  197. ConVar tf_space_thrust_engy( "tf_space_thrust_engy", "35.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  198. ConVar tf_space_thrust_medic( "tf_space_thrust_medic", "37.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  199. ConVar tf_space_thrust_heavy( "tf_space_thrust_heavy", "33.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  200. ConVar tf_space_thrust_demo( "tf_space_thrust_demo", "33.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much thrust is added while holding jump" );
  201. ConVar tf_space_thrust_use_rate( "tf_space_thrust_use_rate", "2.0", FCVAR_CHEAT | FCVAR_REPLICATED, "How much fuel is used per tick" );
  202. ConVar tf_space_thrust_recharge_rate( "tf_space_thrust_recharge_rate", "0.5", FCVAR_CHEAT | FCVAR_REPLICATED, "How much fuel is recharged per tick" );
  203. ConVar tf_skip_intro_and_spectate( "tf_skip_intro_and_spectate", "0", FCVAR_REPLICATED, "Skip intro panels and start spectating." );
  204. #endif
  205. #ifdef STAGING_ONLY
  206. ConVar tf_highfive_separation_forward( "tf_highfive_separation_forward", "0", FCVAR_CHEAT, "Forward distance between high five partners" );
  207. ConVar tf_highfive_separation_right( "tf_highfive_separation_right", "0", FCVAR_CHEAT, "Right distance between high five partners" );
  208. #else
  209. ConVar tf_highfive_separation_forward( "tf_highfive_separation_forward", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Forward distance between high five partners" );
  210. ConVar tf_highfive_separation_right( "tf_highfive_separation_right", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Right distance between high five partners" );
  211. #endif
  212. ConVar tf_highfive_max_range( "tf_highfive_max_range", "150", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "The farthest away a high five partner can be" );
  213. ConVar tf_highfive_height_tolerance( "tf_highfive_height_tolerance", "12", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "The maximum height difference allowed for two high-fivers." );
  214. ConVar tf_highfive_debug( "tf_highfive_debug", "0", FCVAR_NONE, "Turns on some console spew for debugging high five issues." );
  215. ConVar tf_test_teleport_home_fx( "tf_test_teleport_home_fx", "0", FCVAR_CHEAT );
  216. ConVar tf_halloween_giant_health_scale( "tf_halloween_giant_health_scale", "10", FCVAR_CHEAT );
  217. ConVar tf_grapplinghook_los_force_detach_time( "tf_grapplinghook_los_force_detach_time", "1", FCVAR_CHEAT );
  218. ConVar tf_powerup_max_charge_time( "tf_powerup_max_charge_time", "30", FCVAR_CHEAT );
  219. extern ConVar tf_powerup_mode;
  220. extern ConVar tf_mvm_buybacks_method;
  221. extern ConVar tf_mvm_buybacks_per_wave;
  222. #define TF_CANNONBALL_FORCE_SCALE 80.f
  223. #define TF_CANNONBALL_FORCE_UPWARD 300.f
  224. #ifdef STAGING_ONLY
  225. void CC_tf_debug_ballistic_targeting_mark_target( const CCommand &args )
  226. {
  227. CBasePlayer *player = UTIL_GetListenServerHost();
  228. if ( !player )
  229. {
  230. return;
  231. }
  232. Vector forward;
  233. AngleVectors( player->EyeAngles() + player->GetPunchAngle(), &forward );
  234. trace_t result;
  235. UTIL_TraceLine( player->EyePosition(), player->EyePosition() + 2000.0f * forward, MASK_SHOT, player, COLLISION_GROUP_NONE, &result );
  236. tf_debug_ballistic_target = result.endpos;
  237. }
  238. static ConCommand tf_debug_ballistic_targeting_mark_target( "tf_debug_ballistic_targeting_mark_target", CC_tf_debug_ballistic_targeting_mark_target, "Mark a spot for testing ballistic targeting.", FCVAR_CHEAT );
  239. ConVar tf_infinite_ammo( "tf_infinite_ammo", "0", FCVAR_CHEAT );
  240. extern ConVar tf_bountymode_currency_starting;
  241. extern ConVar tf_bountymode_upgrades_wipeondeath;
  242. extern ConVar tf_bountymode_currency_penalty_ondeath;
  243. #endif // STAGING_ONLY
  244. ConVar tf_halloween_unlimited_spells( "tf_halloween_unlimited_spells", "0", FCVAR_CHEAT );
  245. extern ConVar tf_halloween_kart_boost_recharge;
  246. extern ConVar tf_halloween_kart_boost_duration;
  247. ConVar tf_halloween_kart_impact_force( "tf_halloween_kart_impact_force", "0.75f", FCVAR_CHEAT, "Impact force scaler" );
  248. ConVar tf_halloween_kart_impact_damage( "tf_halloween_kart_impact_damage", "1.0f", FCVAR_CHEAT, "Impact damage scaler" );
  249. ConVar tf_halloween_kart_impact_rate( "tf_halloween_kart_impact_rate", "0.5f", FCVAR_CHEAT, "rate of allowing impact damage" );
  250. ConVar tf_halloween_kart_boost_impact_force( "tf_halloween_kart_boost_impact_force", "0.75f", FCVAR_CHEAT, "Impact force scaler on boosts" );
  251. ConVar tf_halloween_kart_impact_bounds_scale( "tf_halloween_kart_impact_bounds_scale", "1.0f", FCVAR_CHEAT );
  252. ConVar tf_halloween_kart_impact_feedback( "tf_halloween_kart_impact_feedback", "0.25f", FCVAR_CHEAT );
  253. ConVar tf_halloween_kart_impact_lookahead( "tf_halloween_kart_impact_lookahead", "12.0f", FCVAR_CHEAT );
  254. ConVar tf_halloween_kart_bomb_head_damage_scale( "tf_halloween_kart_bomb_head_damage_scale", "2", FCVAR_CHEAT );
  255. ConVar tf_halloween_kart_bomb_head_impulse_scale( "tf_halloween_kart_bomb_head_impulse_scale", "2", FCVAR_CHEAT );
  256. ConVar tf_halloween_kart_impact_air_scale( "tf_halloween_kart_impact_air_scale", "0.75f", FCVAR_CHEAT );
  257. ConVar tf_halloween_kart_damage_to_force( "tf_halloween_kart_damage_to_force", "300.0f", FCVAR_CHEAT );
  258. ConVar tf_halloween_kart_stun_duration_scale( "tf_halloween_kart_stun_duration_scale", "0.70f", FCVAR_CHEAT );
  259. ConVar tf_halloween_kart_stun_amount( "tf_halloween_kart_stun_amount", "1.0f", FCVAR_CHEAT );
  260. ConVar tf_halloween_kart_stun_enabled( "tf_halloween_kart_stun_enabled", "1", FCVAR_CHEAT );
  261. ConVar tf_tauntcam_fov_override( "tf_tauntcam_fov_override", "0", FCVAR_CHEAT );
  262. ConVar tf_nav_in_combat_range( "tf_nav_in_combat_range", "1000", FCVAR_CHEAT );
  263. ConVar tf_halloween_kart_punting_ghost_force_scale( "tf_halloween_kart_punting_ghost_force_scale", "4", FCVAR_CHEAT );
  264. ConVar tf_halloween_allow_ghost_hit_by_kart_delay( "tf_halloween_allow_ghost_hit_by_kart_delay", "0.5", FCVAR_CHEAT );
  265. extern ConVar tf_feign_death_duration;
  266. extern ConVar spec_freeze_time;
  267. extern ConVar spec_freeze_traveltime;
  268. extern ConVar sv_maxunlag;
  269. extern ConVar tf_allow_taunt_switch;
  270. extern ConVar weapon_medigun_chargerelease_rate;
  271. extern ConVar tf_scout_energydrink_consume_rate;
  272. extern ConVar tf_mm_trusted;
  273. extern ConVar mp_spectators_restricted;
  274. extern ConVar mp_teams_unbalance_limit;
  275. extern ConVar tf_tournament_classchange_allowed;
  276. extern ConVar tf_tournament_classchange_ready_allowed;
  277. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  278. extern ConVar mp_developer;
  279. #endif // _DEBUG || STAGING_ONLY
  280. #ifdef STAGING_ONLY
  281. extern ConVar tf_skillrating_debug_bots_allowed;
  282. #endif // STAGING_ONLY
  283. extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
  284. extern bool CanScatterGunKnockBack( CTFWeaponBase *pWeapon, float flDamage, float flDistanceSq );
  285. static const char *s_pszTauntRPSParticleNames[] =
  286. {
  287. "rps_rock_red",
  288. "rps_paper_red",
  289. "rps_scissors_red",
  290. "rps_rock_red_win",
  291. "rps_paper_red_win",
  292. "rps_scissors_red_win",
  293. "rps_rock_blue",
  294. "rps_paper_blue",
  295. "rps_scissors_blue",
  296. "rps_rock_blue_win",
  297. "rps_paper_blue_win",
  298. "rps_scissors_blue_win"
  299. };
  300. // -------------------------------------------------------------------------------- //
  301. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  302. // -------------------------------------------------------------------------------- //
  303. class CTEPlayerAnimEvent : public CBaseTempEntity
  304. {
  305. public:
  306. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  307. DECLARE_SERVERCLASS();
  308. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  309. {
  310. m_iPlayerIndex = TF_PLAYER_INDEX_NONE;
  311. }
  312. CNetworkVar( int, m_iPlayerIndex );
  313. CNetworkVar( int, m_iEvent );
  314. CNetworkVar( int, m_nData );
  315. };
  316. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  317. SendPropInt( SENDINFO( m_iPlayerIndex ), 7, SPROP_UNSIGNED ),
  318. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  319. // BUGBUG: ywb we assume this is either 0 or an animation sequence #, but it could also be an activity, which should fit within this limit, but we're not guaranteed.
  320. SendPropInt( SENDINFO( m_nData ), ANIMATION_SEQUENCE_BITS ),
  321. END_SEND_TABLE()
  322. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  323. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  324. {
  325. Vector vecEyePos = pPlayer->EyePosition();
  326. CPVSFilter filter( vecEyePos );
  327. if ( !IsCustomPlayerAnimEvent( event ) && ( event != PLAYERANIMEVENT_SNAP_YAW ) && ( event != PLAYERANIMEVENT_VOICE_COMMAND_GESTURE ) )
  328. {
  329. // if prediction is off, alway send jump
  330. if ( !( ( event == PLAYERANIMEVENT_JUMP ) && ( FStrEq(engine->GetClientConVarValue( pPlayer->entindex(), "cl_predict" ), "0" ) ) ) )
  331. {
  332. filter.RemoveRecipient( pPlayer );
  333. }
  334. }
  335. Assert( pPlayer->entindex() >= 1 && pPlayer->entindex() <= MAX_PLAYERS );
  336. g_TEPlayerAnimEvent.m_iPlayerIndex = pPlayer->entindex();
  337. g_TEPlayerAnimEvent.m_iEvent = event;
  338. Assert( nData < (1<<ANIMATION_SEQUENCE_BITS) );
  339. Assert( (1<<ANIMATION_SEQUENCE_BITS) >= ActivityList_HighestIndex() );
  340. g_TEPlayerAnimEvent.m_nData = nData;
  341. g_TEPlayerAnimEvent.Create( filter, 0 );
  342. }
  343. //=================================================================================
  344. //
  345. // Ragdoll Entity
  346. //
  347. class CTFRagdoll : public CBaseAnimatingOverlay
  348. {
  349. public:
  350. DECLARE_CLASS( CTFRagdoll, CBaseAnimatingOverlay );
  351. DECLARE_SERVERCLASS();
  352. CTFRagdoll()
  353. {
  354. m_iPlayerIndex.Set( TF_PLAYER_INDEX_NONE );
  355. m_bGib = false;
  356. m_bBurning = false;
  357. m_bElectrocuted = false;
  358. m_bFeignDeath = false;
  359. m_bWasDisguised = false;
  360. m_bBecomeAsh = false;
  361. m_bOnGround = false;
  362. m_bCloaked = false;
  363. m_iDamageCustom = 0;
  364. m_bCritOnHardHit = false;
  365. m_vecRagdollOrigin.Init();
  366. m_vecRagdollVelocity.Init();
  367. }
  368. ~CTFRagdoll()
  369. {
  370. // Destroy all of our attached wearables.
  371. for ( int i=0; i<m_hRagWearables.Count(); ++i )
  372. {
  373. if ( m_hRagWearables[i] )
  374. {
  375. m_hRagWearables[i]->Remove();
  376. }
  377. }
  378. m_hRagWearables.Purge();
  379. }
  380. // Transmit ragdolls to everyone.
  381. virtual int UpdateTransmitState()
  382. {
  383. UseClientSideAnimation();
  384. return SetTransmitState( FL_EDICT_ALWAYS );
  385. }
  386. CNetworkVar( int, m_iPlayerIndex );
  387. CNetworkVector( m_vecRagdollVelocity );
  388. CNetworkVector( m_vecRagdollOrigin );
  389. CNetworkVar( bool, m_bGib );
  390. CNetworkVar( bool, m_bBurning );
  391. CNetworkVar( bool, m_bElectrocuted );
  392. CNetworkVar( bool, m_bFeignDeath );
  393. CNetworkVar( bool, m_bWasDisguised );
  394. CNetworkVar( bool, m_bBecomeAsh );
  395. CNetworkVar( bool, m_bOnGround );
  396. CNetworkVar( bool, m_bCloaked );
  397. CNetworkVar( int, m_iDamageCustom );
  398. CNetworkVar( int, m_iTeam );
  399. CNetworkVar( int, m_iClass );
  400. CNetworkVar( bool, m_bGoldRagdoll );
  401. CNetworkVar( bool, m_bIceRagdoll );
  402. CNetworkVar( bool, m_bCritOnHardHit );
  403. CNetworkVar( float, m_flHeadScale );
  404. CNetworkVar( float, m_flTorsoScale );
  405. CNetworkVar( float, m_flHandScale );
  406. CUtlVector<CHandle<CEconWearable > > m_hRagWearables;
  407. };
  408. LINK_ENTITY_TO_CLASS( tf_ragdoll, CTFRagdoll );
  409. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTFRagdoll, DT_TFRagdoll )
  410. SendPropVector( SENDINFO( m_vecRagdollOrigin ), -1, SPROP_COORD ),
  411. SendPropInt( SENDINFO( m_iPlayerIndex ), 7, SPROP_UNSIGNED ),
  412. SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
  413. SendPropVector( SENDINFO( m_vecRagdollVelocity ), 13, SPROP_ROUNDDOWN, -2048.0f, 2048.0f ),
  414. SendPropInt( SENDINFO( m_nForceBone ) ),
  415. SendPropBool( SENDINFO( m_bGib ) ),
  416. SendPropBool( SENDINFO( m_bBurning ) ),
  417. SendPropBool( SENDINFO( m_bElectrocuted ) ),
  418. SendPropBool( SENDINFO( m_bFeignDeath ) ),
  419. SendPropBool( SENDINFO( m_bWasDisguised ) ),
  420. SendPropBool( SENDINFO( m_bBecomeAsh ) ),
  421. SendPropBool( SENDINFO( m_bOnGround ) ),
  422. SendPropBool( SENDINFO( m_bCloaked ) ),
  423. SendPropInt( SENDINFO( m_iDamageCustom ) ),
  424. SendPropInt( SENDINFO( m_iTeam ), 3, SPROP_UNSIGNED ),
  425. SendPropInt( SENDINFO( m_iClass ), 4, SPROP_UNSIGNED ),
  426. SendPropUtlVector( SENDINFO_UTLVECTOR( m_hRagWearables ), 8, SendPropEHandle( NULL, 0 ) ),
  427. SendPropBool( SENDINFO( m_bGoldRagdoll ) ),
  428. SendPropBool( SENDINFO( m_bIceRagdoll ) ),
  429. SendPropBool( SENDINFO( m_bCritOnHardHit ) ),
  430. SendPropFloat( SENDINFO( m_flHeadScale ) ),
  431. SendPropFloat( SENDINFO( m_flTorsoScale ) ),
  432. SendPropFloat( SENDINFO( m_flHandScale ) ),
  433. END_SEND_TABLE()
  434. // -------------------------------------------------------------------------------- //
  435. // Tables.
  436. // -------------------------------------------------------------------------------- //
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Filters updates to a variable so that only non-local players see
  439. // the changes. This is so we can send a low-res origin to non-local players
  440. // while sending a hi-res one to the local player.
  441. // Input : *pVarData -
  442. // *pOut -
  443. // objectID -
  444. //-----------------------------------------------------------------------------
  445. void* SendProxy_SendNonLocalDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  446. {
  447. pRecipients->SetAllRecipients();
  448. pRecipients->ClearRecipient( objectID - 1 );
  449. return ( void * )pVarData;
  450. }
  451. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalDataTable );
  452. //-----------------------------------------------------------------------------
  453. // Purpose: SendProxy that converts the UtlVector list of objects to entindexes, where it's reassembled on the client
  454. //-----------------------------------------------------------------------------
  455. void SendProxy_PlayerObjectList( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  456. {
  457. CTFPlayer *pPlayer = (CTFPlayer*)pStruct;
  458. // If this fails, then SendProxyArrayLength_PlayerObjects didn't work.
  459. Assert( iElement < pPlayer->GetObjectCount() );
  460. CBaseObject *pObject = pPlayer->GetObject(iElement);
  461. EHANDLE hObject;
  462. hObject = pObject;
  463. SendProxy_EHandleToInt( pProp, pStruct, &hObject, pOut, iElement, objectID );
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. //-----------------------------------------------------------------------------
  468. int SendProxyArrayLength_PlayerObjects( const void *pStruct, int objectID )
  469. {
  470. CTFPlayer *pPlayer = (CTFPlayer*)pStruct;
  471. int iObjects = pPlayer->GetObjectCount();
  472. Assert( iObjects <= MAX_OBJECTS_PER_PLAYER );
  473. return iObjects;
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Purpose: Send to attached medics
  477. //-----------------------------------------------------------------------------
  478. void* SendProxy_SendHealersDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  479. {
  480. CTFPlayer *pPlayer = (CTFPlayer*)pStruct;
  481. if ( pPlayer )
  482. {
  483. // Add attached medics
  484. for ( int i = 0; i < pPlayer->m_Shared.GetNumHealers(); i++ )
  485. {
  486. CTFPlayer *pMedic = ToTFPlayer( pPlayer->m_Shared.GetHealerByIndex( i ) );
  487. if ( !pMedic )
  488. continue;
  489. pRecipients->SetRecipient( pMedic->GetClientIndex() );
  490. return (void*)pVarData;
  491. }
  492. }
  493. return NULL;
  494. }
  495. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendHealersDataTable );
  496. BEGIN_DATADESC( CTFPlayer )
  497. DEFINE_INPUTFUNC( FIELD_VOID, "IgnitePlayer", InputIgnitePlayer ),
  498. DEFINE_INPUTFUNC( FIELD_STRING, "SetCustomModel", InputSetCustomModel ),
  499. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetCustomModelOffset", InputSetCustomModelOffset ),
  500. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetCustomModelRotation", InputSetCustomModelRotation ),
  501. DEFINE_INPUTFUNC( FIELD_VOID, "ClearCustomModelRotation", InputClearCustomModelRotation ),
  502. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetCustomModelRotates", InputSetCustomModelRotates ),
  503. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetCustomModelVisibleToSelf", InputSetCustomModelVisibleToSelf ),
  504. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetForcedTauntCam", InputSetForcedTauntCam ),
  505. DEFINE_INPUTFUNC( FIELD_VOID, "ExtinguishPlayer", InputExtinguishPlayer ),
  506. DEFINE_INPUTFUNC( FIELD_FLOAT, "BleedPlayer", InputBleedPlayer ),
  507. DEFINE_INPUTFUNC( FIELD_VOID, "TriggerLootIslandAchievement", InputTriggerLootIslandAchievement ),
  508. DEFINE_INPUTFUNC( FIELD_VOID, "TriggerLootIslandAchievement2", InputTriggerLootIslandAchievement2 ),
  509. DEFINE_INPUTFUNC( FIELD_STRING, "SpeakResponseConcept", InputSpeakResponseConcept ),
  510. DEFINE_INPUTFUNC( FIELD_VOID, "RollRareSpell", InputRollRareSpell ),
  511. DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),
  512. END_DATADESC()
  513. EXTERN_SEND_TABLE( DT_ScriptCreatedItem );
  514. // specific to the local player
  515. BEGIN_SEND_TABLE_NOBASE( CTFPlayer, DT_TFLocalPlayerExclusive )
  516. // send a hi-res origin to the local player for use in prediction
  517. SendPropVectorXY(SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginXY ),
  518. SendPropFloat (SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginZ ),
  519. SendPropArray2(
  520. SendProxyArrayLength_PlayerObjects,
  521. SendPropInt("player_object_array_element", 0, SIZEOF_IGNORE, NUM_NETWORKED_EHANDLE_BITS, SPROP_UNSIGNED, SendProxy_PlayerObjectList),
  522. MAX_OBJECTS_PER_PLAYER,
  523. 0,
  524. "player_object_array"
  525. ),
  526. SendPropFloat( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 8, SPROP_CHANGES_OFTEN, -90.0f, 90.0f ), // No longer used by the local player, could be omitted. Preserved for backwards-compat for now.
  527. // SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 10, SPROP_CHANGES_OFTEN ),
  528. SendPropBool( SENDINFO( m_bIsCoaching ) ),
  529. SendPropEHandle( SENDINFO( m_hCoach ) ),
  530. SendPropEHandle( SENDINFO( m_hStudent ) ),
  531. SendPropInt( SENDINFO( m_nCurrency ), -1, SPROP_VARINT ),
  532. SendPropInt( SENDINFO( m_nExperienceLevel ), 7, SPROP_UNSIGNED ),
  533. SendPropInt( SENDINFO( m_nExperienceLevelProgress ), 7, SPROP_UNSIGNED ),
  534. SendPropBool( SENDINFO( m_bMatchSafeToLeave ) ),
  535. END_SEND_TABLE()
  536. // all players except the local player
  537. BEGIN_SEND_TABLE_NOBASE( CTFPlayer, DT_TFNonLocalPlayerExclusive )
  538. // send a lo-res origin to other players
  539. SendPropVectorXY(SENDINFO(m_vecOrigin), -1, SPROP_COORD_MP_LOWPRECISION|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginXY ),
  540. SendPropFloat (SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_COORD_MP_LOWPRECISION|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_OriginZ ),
  541. SendPropFloat( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 8, SPROP_CHANGES_OFTEN, -90.0f, 90.0f ),
  542. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 10, SPROP_CHANGES_OFTEN ),
  543. END_SEND_TABLE()
  544. //-----------------------------------------------------------------------------
  545. // Purpose: Sent to attached medics
  546. //-----------------------------------------------------------------------------
  547. BEGIN_SEND_TABLE_NOBASE( CTFPlayer, DT_TFSendHealersDataTable )
  548. SendPropInt( SENDINFO( m_nActiveWpnClip ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  549. END_SEND_TABLE()
  550. //============
  551. LINK_ENTITY_TO_CLASS( player, CTFPlayer );
  552. PRECACHE_REGISTER(player);
  553. IMPLEMENT_SERVERCLASS_ST( CTFPlayer, DT_TFPlayer )
  554. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  555. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  556. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  557. SendPropExclude( "DT_BaseAnimating", "m_nBody" ),
  558. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  559. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  560. SendPropExclude( "DT_BaseEntity", "m_nModelIndex" ),
  561. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  562. // cs_playeranimstate and clientside animation takes care of these on the client
  563. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  564. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  565. SendPropExclude( "DT_BaseFlex", "m_flexWeight" ),
  566. SendPropExclude( "DT_BaseFlex", "m_blinktoggle" ),
  567. SendPropExclude( "DT_BaseFlex", "m_viewtarget" ),
  568. SendPropBool(SENDINFO(m_bSaveMeParity)),
  569. SendPropBool(SENDINFO(m_bIsMiniBoss)),
  570. SendPropBool(SENDINFO(m_bIsABot)),
  571. SendPropInt( SENDINFO(m_nBotSkill), 3, SPROP_UNSIGNED ),
  572. // This will create a race condition will the local player, but the data will be the same so.....
  573. SendPropInt( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ),
  574. // Ragdoll.
  575. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  576. SendPropDataTable( SENDINFO_DT( m_PlayerClass ), &REFERENCE_SEND_TABLE( DT_TFPlayerClassShared ) ),
  577. SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFPlayerShared ) ),
  578. SendPropEHandle(SENDINFO(m_hItem)),
  579. // Data that only gets sent to the local player
  580. SendPropDataTable( "tflocaldata", 0, &REFERENCE_SEND_TABLE(DT_TFLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
  581. // Data that gets sent to all other players
  582. SendPropDataTable( "tfnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_TFNonLocalPlayerExclusive), SendProxy_SendNonLocalDataTable ),
  583. SendPropBool( SENDINFO( m_bAllowMoveDuringTaunt ) ),
  584. SendPropBool( SENDINFO( m_bIsReadyToHighFive ) ),
  585. SendPropEHandle( SENDINFO( m_hHighFivePartner ) ),
  586. SendPropInt( SENDINFO( m_nForceTauntCam ), 2, SPROP_UNSIGNED ),
  587. SendPropFloat( SENDINFO( m_flTauntYaw ), 0, SPROP_NOSCALE ),
  588. SendPropInt( SENDINFO( m_nActiveTauntSlot ) ),
  589. SendPropInt( SENDINFO( m_iTauntItemDefIndex ) ),
  590. SendPropFloat( SENDINFO( m_flCurrentTauntMoveSpeed ) ),
  591. SendPropFloat( SENDINFO( m_flVehicleReverseTime ) ),
  592. SendPropFloat( SENDINFO( m_flLastDamageTime ), 16, SPROP_ROUNDUP ),
  593. SendPropBool( SENDINFO( m_bInPowerPlay ) ),
  594. SendPropInt( SENDINFO( m_iSpawnCounter ) ),
  595. SendPropBool( SENDINFO( m_bArenaSpectator ) ),
  596. SendPropFloat( SENDINFO( m_flHeadScale ) ),
  597. SendPropFloat( SENDINFO( m_flTorsoScale ) ),
  598. SendPropFloat( SENDINFO( m_flHandScale ) ),
  599. SendPropBool( SENDINFO( m_bUseBossHealthBar ) ),
  600. SendPropBool( SENDINFO( m_bUsingVRHeadset ) ),
  601. SendPropBool( SENDINFO( m_bForcedSkin ) ),
  602. SendPropInt( SENDINFO( m_nForcedSkin ), ANIMATION_SKIN_BITS ),
  603. SendPropDataTable( SENDINFO_DT( m_AttributeManager ), &REFERENCE_SEND_TABLE(DT_AttributeManager) ),
  604. SendPropDataTable( "TFSendHealersDataTable", 0, &REFERENCE_SEND_TABLE( DT_TFSendHealersDataTable ), SendProxy_SendHealersDataTable ),
  605. SendPropFloat( SENDINFO( m_flKartNextAvailableBoost ) ),
  606. SendPropInt( SENDINFO( m_iKartHealth ) ),
  607. SendPropInt( SENDINFO( m_iKartState ) ),
  608. SendPropEHandle( SENDINFO( m_hGrapplingHookTarget ) ),
  609. SendPropEHandle( SENDINFO( m_hSecondaryLastWeapon ) ),
  610. SendPropBool( SENDINFO( m_bUsingActionSlot ) ),
  611. SendPropFloat( SENDINFO( m_flInspectTime ) ),
  612. SendPropInt( SENDINFO( m_iCampaignMedals ) ),
  613. SendPropInt( SENDINFO( m_iPlayerSkinOverride ) ),
  614. END_SEND_TABLE()
  615. // -------------------------------------------------------------------------------- //
  616. void cc_CreatePredictionError_f()
  617. {
  618. CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
  619. pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
  620. }
  621. ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  622. // -------------------------------------------------------------------------------- //
  623. enum eCoachCommand
  624. {
  625. kCoachCommand_Look = 1, // slot1
  626. kCoachCommand_Go, // slot2
  627. kCoachCommand_Attack,
  628. kCoachCommand_Defend,
  629. kNumCoachCommands,
  630. };
  631. /**
  632. * Handles a command from the coach
  633. */
  634. static void HandleCoachCommand( CTFPlayer *pPlayer, eCoachCommand command )
  635. {
  636. if ( pPlayer && pPlayer->IsCoaching() && pPlayer->GetStudent() && command < kNumCoachCommands )
  637. {
  638. const float kMaxRateCoachCommands = 1.0f;
  639. float flLastCoachCommandDelta = gpGlobals->curtime - pPlayer->m_flLastCoachCommand;
  640. if ( flLastCoachCommandDelta < kMaxRateCoachCommands && flLastCoachCommandDelta > 0.0f )
  641. {
  642. return;
  643. }
  644. pPlayer->m_flLastCoachCommand = gpGlobals->curtime;
  645. IGameEvent *pEvent = gameeventmanager->CreateEvent( "show_annotation" );
  646. if ( pEvent )
  647. {
  648. Vector vForward;
  649. AngleVectors( pPlayer->EyeAngles(), &vForward );
  650. trace_t trace;
  651. CTraceFilterSimple filter( pPlayer->GetStudent(), COLLISION_GROUP_NONE );
  652. UTIL_TraceLine( pPlayer->EyePosition(), pPlayer->EyePosition() + vForward * MAX_TRACE_LENGTH, MASK_SOLID, &filter, &trace );
  653. CBaseEntity *pHitEntity = trace.m_pEnt && trace.m_pEnt->IsWorld() == false && trace.m_pEnt != pPlayer->GetStudent() ? trace.m_pEnt : NULL;
  654. pEvent->SetInt( "id", pPlayer->entindex() );
  655. pEvent->SetFloat( "worldPosX", trace.endpos.x );
  656. pEvent->SetFloat( "worldPosY", trace.endpos.y );
  657. pEvent->SetFloat( "worldPosZ", trace.endpos.z );
  658. pEvent->SetFloat( "worldNormalX", trace.plane.normal.x );
  659. pEvent->SetFloat( "worldNormalY", trace.plane.normal.y );
  660. pEvent->SetFloat( "worldNormalZ", trace.plane.normal.z );
  661. pEvent->SetFloat( "lifetime", 10.0f );
  662. if ( pHitEntity )
  663. {
  664. pEvent->SetInt( "follow_entindex", pHitEntity->entindex() );
  665. }
  666. pEvent->SetInt( "visibilityBitfield", ( 1 << pPlayer->entindex() | 1 << pPlayer->GetStudent()->entindex() ) );
  667. pEvent->SetBool( "show_distance", true );
  668. pEvent->SetBool( "show_effect", true );
  669. switch ( command )
  670. {
  671. case kCoachCommand_Attack:
  672. pEvent->SetString( "text", pHitEntity ? "#TF_Coach_AttackThis" : "#TF_Coach_AttackHere" );
  673. pEvent->SetString( "play_sound", "coach/coach_attack_here.wav" );
  674. break;
  675. case kCoachCommand_Defend:
  676. pEvent->SetString( "text", pHitEntity ? "#TF_Coach_DefendThis" : "#TF_Coach_DefendHere" );
  677. pEvent->SetString( "play_sound", "coach/coach_defend_here.wav" );
  678. break;
  679. case kCoachCommand_Look:
  680. pEvent->SetString( "text", pHitEntity ? "#TF_Coach_LookAt" : "#TF_Coach_LookHere" );
  681. pEvent->SetString( "play_sound", "coach/coach_look_here.wav" );
  682. break;
  683. case kCoachCommand_Go:
  684. pEvent->SetString( "text", pHitEntity ? "#TF_Coach_GoToThis" : "#TF_Coach_GoHere" );
  685. pEvent->SetString( "play_sound", "coach/coach_go_here.wav" );
  686. break;
  687. }
  688. gameeventmanager->FireEvent( pEvent );
  689. }
  690. }
  691. };
  692. //-----------------------------------------------------------------------------
  693. // Purpose:
  694. //-----------------------------------------------------------------------------
  695. CTFPlayer::CTFPlayer()
  696. {
  697. m_pAttributes = this;
  698. m_PlayerAnimState = CreateTFPlayerAnimState( this );
  699. SetArmorValue( 10 );
  700. m_hItem = NULL;
  701. m_hTauntScene = NULL;
  702. m_hTauntProp = NULL;
  703. UseClientSideAnimation();
  704. m_angEyeAngles.Init();
  705. m_pStateInfo = NULL;
  706. m_lifeState = LIFE_DEAD; // Start "dead".
  707. m_iMaxSentryKills = 0;
  708. m_flLastCoachCommand = 0;
  709. m_flNextTimeCheck = gpGlobals->curtime;
  710. m_flSpawnTime = 0;
  711. m_flWaterExitTime = 0;
  712. SetViewOffset( TF_PLAYER_VIEW_OFFSET );
  713. m_Shared.Init( this );
  714. m_iLastSkin = -1;
  715. m_bHudClassAutoKill = false;
  716. m_bMedigunAutoHeal = false;
  717. m_vecLastDeathPosition = Vector( FLT_MAX, FLT_MAX, FLT_MAX );
  718. SetDesiredPlayerClassIndex( TF_CLASS_UNDEFINED );
  719. SetContextThink( &CTFPlayer::TFPlayerThink, gpGlobals->curtime, "TFPlayerThink" );
  720. m_flLastAction = gpGlobals->curtime;
  721. m_flTimeInSpawn = 0;
  722. m_bInitTaunt = false;
  723. m_bSpeakingConceptAsDisguisedSpy = false;
  724. m_iPreviousteam = TEAM_UNASSIGNED;
  725. m_bArenaSpectator = false;
  726. m_bArenaIsAFK = false;
  727. m_bIsAFK = false;
  728. m_nDeployingBombState = TF_BOMB_DEPLOYING_NONE;
  729. m_flNextChangeClassTime = 0.0f;
  730. m_flNextChangeTeamTime = 0.0f;
  731. m_bScattergunJump = false;
  732. m_iOldStunFlags = 0;
  733. m_iLastWeaponSlot = 1;
  734. m_iNumberofDominations = 0;
  735. m_bFlipViewModels = false;
  736. m_iBlastJumpState = 0;
  737. m_flBlastJumpLandTime = 0;
  738. m_fMaxHealthTime = -1;
  739. m_iHealthBefore = 0;
  740. m_iTeamChanges = 0;
  741. m_iClassChanges = 0;
  742. m_hReviveMarker = NULL;
  743. // Bounty Mode
  744. m_nExperienceLevel = 1;
  745. m_nExperiencePoints = 0;
  746. m_nExperienceLevelProgress = 0;
  747. SetDefLessFunc( m_Cappers ); // Tracks victims for demo achievement
  748. //=============================================================================
  749. // HPE_BEGIN:
  750. // [msmith] Added a player type so we can distinguish between bots and humans.
  751. //=============================================================================
  752. m_playerType = HUMAN_PLAYER;
  753. //=============================================================================
  754. // HPE_END
  755. //=============================================================================
  756. m_bIsTargetDummy = false;
  757. m_bCollideWithSentry = false;
  758. m_flCommentOnCarrying = 0;
  759. m_bIsReadyToHighFive = false;
  760. m_hHighFivePartner = NULL;
  761. m_nForceTauntCam = 0;
  762. m_bAllowMoveDuringTaunt = false;
  763. m_bTauntForceMoveForward = false;
  764. m_flTauntForceMoveForwardSpeed = 0.f;
  765. m_flTauntMoveAccelerationTime = 0.f;
  766. m_flTauntTurnSpeed = 0.f;
  767. m_flTauntTurnAccelerationTime = 0.f;
  768. m_bTauntMimic = false;
  769. m_bIsTauntInitiator = false;
  770. m_TauntEconItemView.Invalidate();
  771. m_iPreTauntWeaponSlot = -1;
  772. m_bIsCalculatingMaximumSpeed = false;
  773. m_flLastThinkTime = -1.f;
  774. m_nCurrency = 0;
  775. m_pWaveSpawnPopulator = NULL;
  776. m_flLastReadySoundTime = 0.f;
  777. m_damageRateArray = new int[ DPS_Period ];
  778. ResetDamagePerSecond();
  779. m_nActiveWpnClip.Set( 0 );
  780. m_nActiveWpnClipPrev = 0;
  781. m_flNextClipSendTime = 0;
  782. m_nCanPurchaseUpgradesCount = 0;
  783. m_flHeadScale = 1.f;
  784. m_flTorsoScale = 1.f;
  785. m_flHandScale = 1.f;
  786. m_bPendingMerasmusPlayerBombExplode = false;
  787. m_fLastBombHeadTimestamp = 0.0f;
  788. m_bIsSapping = false;
  789. m_iSappingEvent = TF_SAPEVENT_NONE;
  790. m_flSapStartTime = 0.00;
  791. m_bIsMiniBoss = false;
  792. m_bUseBossHealthBar = false;
  793. m_bUsingVRHeadset = false;
  794. m_bForcedSkin = false;
  795. m_nForcedSkin = 0;
  796. SetRespawnOverride( -1.f, NULL_STRING );
  797. m_qPreviousChargeEyeAngle.Init();
  798. m_vHalloweenKartPush.Zero();
  799. m_flHalloweenKartPushEventTime = 0.f;
  800. m_bCheckKartCollision = false;
  801. m_flHHHKartAttackTime = 0.f;
  802. m_flNextBonusDucksVOAllowedTime = 0.f;
  803. m_flGhostLastHitByKartTime = 0.f;
  804. m_flVehicleReverseTime = FLT_MAX;
  805. m_iCampaignMedals = 0;
  806. m_bPasstimeBallSlippery = false;
  807. m_flNextScorePointForPD = -1;
  808. m_iPlayerSkinOverride = 0;
  809. m_nPrevRoundTeamNum = TEAM_UNASSIGNED;
  810. m_flLastDamageResistSoundTime = -1.f;
  811. m_hLastDamageDoneEntity = NULL;
  812. m_mapCustomAttributes.SetLessFunc( UtlStringCaseInsensitiveLessFunc );
  813. SetDefLessFunc( m_PlayersExtinguished );
  814. m_flLastAutobalanceTime = 0.f;
  815. }
  816. //-----------------------------------------------------------------------------
  817. // Purpose:
  818. //-----------------------------------------------------------------------------
  819. void CTFPlayer::ForcePlayerViewAngles( const QAngle& qTeleportAngles )
  820. {
  821. CSingleUserRecipientFilter filter( this );
  822. UserMessageBegin( filter, "ForcePlayerViewAngles" );
  823. WRITE_BYTE( 0x01 ); // Reserved space for flags.
  824. WRITE_BYTE( entindex() );
  825. WRITE_ANGLES( qTeleportAngles );
  826. MessageEnd();
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Purpose:
  830. //-----------------------------------------------------------------------------
  831. void CTFPlayer::SetGrapplingHookTarget( CBaseEntity *pTarget, bool bShouldBleed /*= false*/ )
  832. {
  833. if ( pTarget )
  834. {
  835. // prevent fall damage after a successful hook
  836. m_Shared.AddCond( TF_COND_GRAPPLINGHOOK_SAFEFALL );
  837. m_Shared.AddCond( TF_COND_GRAPPLINGHOOK_LATCHED );
  838. }
  839. else
  840. {
  841. m_Shared.RemoveCond( TF_COND_GRAPPLINGHOOK_LATCHED );
  842. }
  843. CBaseEntity *pPreviousTarget = m_hGrapplingHookTarget;
  844. m_hGrapplingHookTarget = pTarget;
  845. if ( pTarget )
  846. {
  847. if ( pTarget->IsPlayer() )
  848. {
  849. CTFPlayer *pTargetPlayer = ToTFPlayer( pTarget );
  850. m_Shared.AddCond( TF_COND_GRAPPLED_TO_PLAYER );
  851. // make player bleed
  852. if ( bShouldBleed )
  853. {
  854. CTFGrapplingHook *pGrapplingHook = dynamic_cast< CTFGrapplingHook* >( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  855. if ( pGrapplingHook )
  856. pTargetPlayer->m_Shared.MakeBleed( this, pGrapplingHook, 0, TF_BLEEDING_DMG, true );
  857. pTargetPlayer->m_nHookAttachedPlayers++;
  858. }
  859. if ( !pTargetPlayer->m_Shared.InCond( TF_COND_GRAPPLINGHOOK_BLEEDING ) && pTargetPlayer->m_nHookAttachedPlayers > 0 )
  860. {
  861. pTargetPlayer->m_Shared.AddCond( TF_COND_GRAPPLINGHOOK_BLEEDING );
  862. }
  863. if ( !pTargetPlayer->m_Shared.InCond( TF_COND_GRAPPLED_BY_PLAYER ) && pTargetPlayer->m_nHookAttachedPlayers > 0 )
  864. {
  865. pTargetPlayer->m_Shared.AddCond( TF_COND_GRAPPLED_BY_PLAYER );
  866. }
  867. }
  868. m_flLastSeenHookTarget = gpGlobals->curtime;
  869. }
  870. else
  871. {
  872. if ( pPreviousTarget && pPreviousTarget->IsPlayer() )
  873. {
  874. CTFPlayer *pPreviousTargetPlayer = ToTFPlayer( pPreviousTarget );
  875. m_Shared.RemoveCond( TF_COND_GRAPPLED_TO_PLAYER );
  876. // try to remove bleeding from hook if there's one
  877. if ( pPreviousTargetPlayer->m_Shared.InCond( TF_COND_BLEEDING ) )
  878. {
  879. CTFGrapplingHook *pGrapplingHook = dynamic_cast< CTFGrapplingHook* >( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  880. if ( pGrapplingHook )
  881. pPreviousTargetPlayer->m_Shared.StopBleed( this, pGrapplingHook );
  882. }
  883. pPreviousTargetPlayer->m_nHookAttachedPlayers--;
  884. Assert( pPreviousTargetPlayer->m_nHookAttachedPlayers >= 0 );
  885. if ( pPreviousTargetPlayer->m_nHookAttachedPlayers == 0 )
  886. {
  887. pPreviousTargetPlayer->m_Shared.RemoveCond( TF_COND_GRAPPLINGHOOK_BLEEDING );
  888. pPreviousTargetPlayer->m_Shared.RemoveCond( TF_COND_GRAPPLED_BY_PLAYER );
  889. }
  890. }
  891. }
  892. }
  893. //-----------------------------------------------------------------------------
  894. // Purpose:
  895. //-----------------------------------------------------------------------------
  896. bool CTFPlayer::CanBeForcedToLaugh( void )
  897. {
  898. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && IsBot() && ( GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  899. return false;
  900. return true;
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Purpose:
  904. //-----------------------------------------------------------------------------
  905. void CTFPlayer::TFPlayerThink()
  906. {
  907. if ( m_pStateInfo && m_pStateInfo->pfnThink )
  908. {
  909. (this->*m_pStateInfo->pfnThink)();
  910. }
  911. if ( m_flSendPickupWeaponMessageTime != -1.f && gpGlobals->curtime >= m_flSendPickupWeaponMessageTime )
  912. {
  913. CSingleUserRecipientFilter filter( this );
  914. filter.MakeReliable();
  915. UserMessageBegin( filter, "PlayerPickupWeapon" );
  916. MessageEnd();
  917. m_flSendPickupWeaponMessageTime = -1.f;
  918. }
  919. // In doomsday event, kart can run over ghost to do stuff
  920. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  921. {
  922. CUtlVector< CTFPlayer * > playerVector;
  923. CollectPlayers( &playerVector, TEAM_ANY, true );
  924. CUtlVector< CTFPlayer * > ghostVector;
  925. for ( int i=0; i<playerVector.Count(); ++i )
  926. {
  927. if ( playerVector[i] == this )
  928. continue;
  929. // touching ghost player?
  930. // we just check for radius of 100 and assume that we touch to avoid custom collision for ghost
  931. if ( playerVector[i]->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  932. {
  933. if ( ( playerVector[i]->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() < Square( 100 ) )
  934. {
  935. ghostVector.AddToTail( playerVector[i] );
  936. }
  937. }
  938. }
  939. for ( int i=0; i<ghostVector.Count(); ++i )
  940. {
  941. CTFPlayer *pGhost = ghostVector[i];
  942. // revive ghost on the same team
  943. if ( pGhost->GetTeamNumber() == GetTeamNumber() )
  944. {
  945. // Trace the ghosts bbox right where they are to see if they collide with enemy players
  946. trace_t trace;
  947. Ray_t ray;
  948. ray.Init( pGhost->GetAbsOrigin(), pGhost->GetAbsOrigin(), pGhost->GetPlayerMins(), pGhost->GetPlayerMaxs() );
  949. UTIL_TraceRay( ray, PlayerSolidMask(), pGhost, COLLISION_GROUP_PLAYER, &trace );
  950. // If our trace is clear, spawn that ghost
  951. if ( trace.fraction == 1.0f )
  952. {
  953. // Force the players kart angles to line up with our current ghost angles.
  954. // This should put us in the kart at the same direction we are currently looking.
  955. pGhost->ForcePlayerViewAngles( pGhost->GetAbsAngles() );
  956. pGhost->m_Shared.RemoveCond( TF_COND_HALLOWEEN_GHOST_MODE );
  957. pGhost->m_Shared.AddCond( TF_COND_HALLOWEEN_KART );
  958. pGhost->m_Shared.AddCond( TF_COND_HALLOWEEN_IN_HELL ); // keep you in hell to be able to respawn as ghost
  959. pGhost->m_Shared.AddCond( TF_COND_HALLOWEEN_QUICK_HEAL, 3, this );
  960. pGhost->EmitSound( "BumperCar.SpawnFromLava" );
  961. DispatchParticleEffect( "ghost_appearation", PATTACH_ABSORIGIN, pGhost );
  962. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  963. {
  964. // achievement for me!
  965. AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_RESPAWN_TEAMMATES );
  966. IGameEvent *pEvent = gameeventmanager->CreateEvent( "respawn_ghost" );
  967. if ( pEvent )
  968. {
  969. pEvent->SetInt( "reviver", GetUserID() );
  970. pEvent->SetInt( "ghost", pGhost->GetUserID() );
  971. gameeventmanager->FireEvent( pEvent, true );
  972. }
  973. }
  974. }
  975. }
  976. else if ( tf_halloween_allow_ghost_hit_by_kart_delay.GetFloat() > 0 && gpGlobals->curtime - pGhost->m_flGhostLastHitByKartTime > tf_halloween_allow_ghost_hit_by_kart_delay.GetFloat() )
  977. {
  978. // punt off other team ghost
  979. float flImpactForce = GetLocalVelocity().Length();
  980. flImpactForce = MAX( 100.f, flImpactForce ); // add min force
  981. Vector vOffset = pGhost->WorldSpaceCenter() - WorldSpaceCenter();
  982. vOffset.z = 0;
  983. Vector vPuntDir = ( vOffset ).Normalized();
  984. vPuntDir.z = 0.5f;
  985. pGhost->ApplyAirBlastImpulse( tf_halloween_kart_punting_ghost_force_scale.GetFloat() * flImpactForce * vPuntDir );
  986. pGhost->EmitSound( "BumperCar.HitGhost" );
  987. pGhost->m_flGhostLastHitByKartTime = gpGlobals->curtime;
  988. }
  989. }
  990. }
  991. if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() )
  992. {
  993. if ( IsUsingActionSlot() && GetActiveTFWeapon() && GetActiveTFWeapon()->GetWeaponID() != TF_WEAPON_GRAPPLINGHOOK )
  994. {
  995. CTFGrapplingHook *pGrapplingHook = dynamic_cast< CTFGrapplingHook* >( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  996. if ( pGrapplingHook )
  997. {
  998. Weapon_Switch( pGrapplingHook );
  999. }
  1000. }
  1001. CBaseEntity *pHookTarget = GetGrapplingHookTarget();
  1002. if ( pHookTarget )
  1003. {
  1004. // detatch hook if the object's picked up
  1005. if ( pHookTarget->IsBaseObject() )
  1006. {
  1007. CBaseObject *pObj = assert_cast< CBaseObject* >( pHookTarget );
  1008. if ( pObj->IsCarried() )
  1009. {
  1010. SetGrapplingHookTarget( NULL );
  1011. pHookTarget = NULL;
  1012. }
  1013. }
  1014. // check if something is blocking the player from traveling to the hook target
  1015. if ( pHookTarget )
  1016. {
  1017. trace_t tr;
  1018. CTraceFilterLOS filter( this, COLLISION_GROUP_PLAYER_MOVEMENT, pHookTarget );
  1019. UTIL_TraceLine( WorldSpaceCenter(), pHookTarget->WorldSpaceCenter(), MASK_PLAYERSOLID, &filter, &tr );
  1020. if ( !tr.DidHit() )
  1021. {
  1022. m_flLastSeenHookTarget = gpGlobals->curtime;
  1023. }
  1024. else if ( gpGlobals->curtime - m_flLastSeenHookTarget > tf_grapplinghook_los_force_detach_time.GetFloat() )
  1025. {
  1026. // force to detach if the hooker lost sight of the target for sometime
  1027. SetGrapplingHookTarget( NULL );
  1028. }
  1029. }
  1030. }
  1031. }
  1032. UpdateCustomAttributes();
  1033. // Time to finish the current random expression? Or time to pick a new one?
  1034. if ( IsAlive() && !IsReadyToTauntWithPartner() && ( m_flNextSpeakWeaponFire < gpGlobals->curtime ) && m_flNextRandomExpressionTime >= 0 && gpGlobals->curtime > m_flNextRandomExpressionTime )
  1035. {
  1036. // Random expressions need to be cleared, because they don't loop. So if we
  1037. // pick the same one again, we want to restart it.
  1038. ClearExpression();
  1039. m_iszExpressionScene = NULL_STRING;
  1040. UpdateExpression();
  1041. }
  1042. if ( IsTaunting() )
  1043. {
  1044. if ( !m_strTauntSoundName.IsEmpty() && m_flTauntSoundTime > 0 && m_flTauntSoundTime <= gpGlobals->curtime )
  1045. {
  1046. EmitSound( m_strTauntSoundName.String() );
  1047. m_flTauntSoundTime = 0.f;
  1048. }
  1049. if ( !m_strTauntSoundLoopName.IsEmpty() && m_flTauntSoundLoopTime > 0 && m_flTauntSoundLoopTime <= gpGlobals->curtime )
  1050. {
  1051. CReliableBroadcastRecipientFilter filter;
  1052. UserMessageBegin( filter, "PlayerTauntSoundLoopStart" );
  1053. WRITE_BYTE( entindex() );
  1054. WRITE_STRING( m_strTauntSoundLoopName.String() );
  1055. MessageEnd();
  1056. m_flTauntSoundLoopTime = 0.f;
  1057. }
  1058. // play taunt outro
  1059. if ( m_flTauntOutroTime > 0.f && m_flTauntOutroTime <= gpGlobals->curtime )
  1060. {
  1061. m_bAllowedToRemoveTaunt = true;
  1062. float flDuration = PlayTauntOutroScene();
  1063. m_flTauntRemoveTime = gpGlobals->curtime + flDuration;
  1064. m_flTauntOutroTime = 0.f;
  1065. }
  1066. }
  1067. // Halloween Hacks
  1068. // Spell Casting on Attack1
  1069. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  1070. {
  1071. // Check if this is the spellbook so we can save off info to preserve weapon switching
  1072. CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  1073. if ( pSpellBook )
  1074. {
  1075. // cast Spell
  1076. if ( m_nButtons & IN_ATTACK )
  1077. {
  1078. if ( pSpellBook )
  1079. {
  1080. pSpellBook->PrimaryAttack();
  1081. }
  1082. }
  1083. }
  1084. // Speed Boost
  1085. if ( m_nButtons & IN_ATTACK2 )
  1086. {
  1087. if ( GetKartSpeedBoost() >= 1.0f )
  1088. {
  1089. m_flKartNextAvailableBoost = gpGlobals->curtime + tf_halloween_kart_boost_recharge.GetFloat();
  1090. m_Shared.AddCond( TF_COND_HALLOWEEN_KART_DASH, tf_halloween_kart_boost_duration.GetFloat() );
  1091. }
  1092. }
  1093. }
  1094. CBaseEntity *pGroundEntity = GetGroundEntity();
  1095. // We consider players "in air" if they have no ground entity and they're not in water.
  1096. if ( pGroundEntity == NULL && GetWaterLevel() == WL_NotInWater )
  1097. {
  1098. if ( m_iLeftGroundHealth < 0 )
  1099. {
  1100. m_iLeftGroundHealth = GetHealth();
  1101. }
  1102. }
  1103. else
  1104. {
  1105. m_iLeftGroundHealth = -1;
  1106. if ( GetFlags() & FL_ONGROUND )
  1107. {
  1108. m_Shared.RemoveCond( TF_COND_KNOCKED_INTO_AIR );
  1109. }
  1110. if ( m_iBlastJumpState )
  1111. {
  1112. const char *pszEvent = NULL;
  1113. if ( StickyJumped() )
  1114. {
  1115. pszEvent = "sticky_jump_landed";
  1116. }
  1117. else if ( RocketJumped() )
  1118. {
  1119. pszEvent = "rocket_jump_landed";
  1120. }
  1121. ClearBlastJumpState();
  1122. if ( pszEvent )
  1123. {
  1124. IGameEvent * event = gameeventmanager->CreateEvent( pszEvent );
  1125. if ( event )
  1126. {
  1127. event->SetInt( "userid", GetUserID() );
  1128. gameeventmanager->FireEvent( event );
  1129. }
  1130. }
  1131. }
  1132. }
  1133. if( IsTaunting() )
  1134. {
  1135. bool bStopTaunt = false;
  1136. // if I'm not supposed to move during taunt
  1137. // stop taunting if I lost my ground entity or was moved at all
  1138. if ( !CanMoveDuringTaunt() )
  1139. {
  1140. bStopTaunt |= pGroundEntity == NULL;
  1141. if ( m_TauntEconItemView.IsValid() && m_TauntEconItemView.GetStaticData()->GetTauntData()->ShouldStopTauntIfMoved() )
  1142. bStopTaunt |= m_vecTauntStartPosition.DistToSqr( GetAbsOrigin() ) > 0.1f;
  1143. }
  1144. if ( !bStopTaunt )
  1145. {
  1146. bStopTaunt |= ShouldStopTaunting();
  1147. }
  1148. if ( bStopTaunt )
  1149. {
  1150. CancelTaunt();
  1151. }
  1152. }
  1153. if ( ( RocketJumped() || StickyJumped() ) && IsAlive() && m_bCreatedRocketJumpParticles == false )
  1154. {
  1155. const char *pEffectName = "rocketjump_smoke";
  1156. DispatchParticleEffect( pEffectName, PATTACH_POINT_FOLLOW, this, "foot_L" );
  1157. DispatchParticleEffect( pEffectName, PATTACH_POINT_FOLLOW, this, "foot_R" );
  1158. m_bCreatedRocketJumpParticles = true;
  1159. }
  1160. if ( !m_bCollideWithSentry )
  1161. {
  1162. if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  1163. {
  1164. CBaseObject *pSentry = GetObjectOfType( OBJ_SENTRYGUN );
  1165. if ( !pSentry )
  1166. {
  1167. m_bCollideWithSentry = true;
  1168. }
  1169. else
  1170. {
  1171. if ( ( pSentry->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() > 2500 )
  1172. {
  1173. m_bCollideWithSentry = true;
  1174. }
  1175. }
  1176. }
  1177. else
  1178. {
  1179. m_bCollideWithSentry = true;
  1180. }
  1181. }
  1182. if ( gpGlobals->curtime > m_flCommentOnCarrying && (m_flCommentOnCarrying != 0.f) )
  1183. {
  1184. m_flCommentOnCarrying = 0.f;
  1185. CBaseObject* pObj = m_Shared.GetCarriedObject();
  1186. if ( pObj )
  1187. {
  1188. SpeakConceptIfAllowed( MP_CONCEPT_CARRYING_BUILDING, pObj->GetResponseRulesModifier() );
  1189. }
  1190. }
  1191. #ifdef TF_RAID_MODE
  1192. CTFNavArea *area = (CTFNavArea *)GetLastKnownArea();
  1193. if ( area && area->HasAttributeTF( TF_NAV_RESCUE_CLOSET ) )
  1194. {
  1195. // we're standing in a rescue closet and need a friend to let us out - call for help!
  1196. SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_HELP );
  1197. }
  1198. #endif
  1199. // Wrenchmotron taunt effect
  1200. if ( m_bIsTeleportingUsingEurekaEffect )
  1201. {
  1202. if ( m_teleportHomeFlashTimer.HasStarted() && m_teleportHomeFlashTimer.IsElapsed() )
  1203. {
  1204. m_teleportHomeFlashTimer.Invalidate();
  1205. if ( !tf_test_teleport_home_fx.GetBool() )
  1206. {
  1207. // cover up the end of the taunt with a flash
  1208. color32 colorHit = { 255, 255, 255, 255 };
  1209. UTIL_ScreenFade( this, colorHit, 0.25f, 0.25f, FFADE_IN );
  1210. }
  1211. Vector origin = GetAbsOrigin();
  1212. CPVSFilter filter( origin );
  1213. UserMessageBegin( filter, "PlayerTeleportHomeEffect" );
  1214. WRITE_BYTE( entindex() );
  1215. MessageEnd();
  1216. // DispatchParticleEffect( "drg_wrenchmotron_teleport", PATTACH_ABSORIGIN );
  1217. switch( GetTeamNumber() )
  1218. {
  1219. case TF_TEAM_RED:
  1220. TE_TFParticleEffect( filter, 0.0, "teleported_red", origin, vec3_angle );
  1221. TE_TFParticleEffect( filter, 0.0, "player_sparkles_red", origin, vec3_angle, this, PATTACH_POINT );
  1222. break;
  1223. case TF_TEAM_BLUE:
  1224. TE_TFParticleEffect( filter, 0.0, "teleported_blue", origin, vec3_angle );
  1225. TE_TFParticleEffect( filter, 0.0, "player_sparkles_blue", origin, vec3_angle, this, PATTACH_POINT );
  1226. break;
  1227. default:
  1228. break;
  1229. }
  1230. }
  1231. // teleport home when taunt finishes
  1232. if ( !IsTaunting() )
  1233. {
  1234. // drop the intel and any powerup we are carrying
  1235. DropFlag();
  1236. DropRune();
  1237. EmitSound( "Building_Teleporter.Send" );
  1238. m_bIsTeleportingUsingEurekaEffect = false;
  1239. CObjectTeleporter* pTeleExit = assert_cast< CObjectTeleporter* >( GetObjectOfType( OBJ_TELEPORTER, MODE_TELEPORTER_EXIT ) );
  1240. // Check if they wanted to go to their teleporter AND their teleporter can accept them
  1241. if ( m_eEurekaTeleportTarget == EUREKA_TELEPORT_TELEPORTER_EXIT && pTeleExit && ( pTeleExit->GetState() != TELEPORTER_STATE_BUILDING ) )
  1242. {
  1243. pTeleExit->RecieveTeleportingPlayer( this );
  1244. }
  1245. else
  1246. {
  1247. // Default to the spawn
  1248. TFGameRules()->GetPlayerSpawnSpot( this );
  1249. }
  1250. }
  1251. }
  1252. // Send active weapon's clip state to attached medics
  1253. bool bSendClipInfo = gpGlobals->curtime > m_flNextClipSendTime &&
  1254. m_Shared.GetNumHealers() &&
  1255. IsAlive();
  1256. if ( bSendClipInfo )
  1257. {
  1258. CTFWeaponBase *pTFWeapon = GetActiveTFWeapon();
  1259. if ( pTFWeapon )
  1260. {
  1261. int nClip = 0;
  1262. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  1263. {
  1264. nClip = m_Shared.GetDisguiseAmmoCount();
  1265. }
  1266. else
  1267. {
  1268. nClip = pTFWeapon->UsesClipsForAmmo1() ? pTFWeapon->Clip1() : GetAmmoCount( pTFWeapon->GetPrimaryAmmoType() );
  1269. }
  1270. if ( nClip >= 0 && nClip != m_nActiveWpnClipPrev )
  1271. {
  1272. if ( nClip > 500 )
  1273. {
  1274. Warning( "Heal Target: ClipSize Data Limit Exceeded: %d (max 500)\n", nClip );
  1275. nClip = MIN( nClip, 500 );
  1276. }
  1277. m_nActiveWpnClip.Set( nClip );
  1278. m_nActiveWpnClipPrev = m_nActiveWpnClip;
  1279. m_flNextClipSendTime = gpGlobals->curtime + 0.25f;
  1280. }
  1281. }
  1282. }
  1283. if ( GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY && ( GetFlags() & FL_DUCKING ) && ( pGroundEntity != NULL ) )
  1284. {
  1285. int nDisguiseAsDispenserOnCrouch = 0;
  1286. CALL_ATTRIB_HOOK_FLOAT( nDisguiseAsDispenserOnCrouch, disguise_as_dispenser_on_crouch );
  1287. if ( nDisguiseAsDispenserOnCrouch != 0 )
  1288. {
  1289. m_Shared.AddCond( TF_COND_DISGUISED_AS_DISPENSER, 0.5f );
  1290. }
  1291. }
  1292. // rune charge over time
  1293. if ( m_Shared.CanRuneCharge() && !m_Shared.IsRuneCharged() )
  1294. {
  1295. float dt = gpGlobals->curtime - m_flLastRuneChargeUpdate;
  1296. float flAdd = dt * 100.f / tf_powerup_max_charge_time.GetFloat();
  1297. m_Shared.SetRuneCharge( m_Shared.GetRuneCharge() + flAdd );
  1298. if (m_Shared.GetCarryingRuneType() == RUNE_SUPERNOVA && m_Shared.IsRuneCharged() )
  1299. {
  1300. ClientPrint( this, HUD_PRINTCENTER, "#TF_Powerup_Supernova_Deploy" );
  1301. }
  1302. }
  1303. m_flLastRuneChargeUpdate = gpGlobals->curtime;
  1304. // You can't touch a hooked target, so transmit plague when you get as close as you can
  1305. if ( GetGrapplingHookTarget() && GetGrapplingHookTarget()->IsPlayer() && m_Shared.GetCarryingRuneType() == RUNE_PLAGUE )
  1306. {
  1307. CTFPlayer *pHookedPlayer = ToTFPlayer( GetGrapplingHookTarget() );
  1308. float flDistSqrToTarget = GetAbsOrigin().DistToSqr( pHookedPlayer->GetAbsOrigin() );
  1309. if ( flDistSqrToTarget < 8100 && !pHookedPlayer->m_Shared.InCond( TF_COND_PLAGUE ) &&
  1310. !m_Shared.IsAlly( pHookedPlayer ) &&
  1311. !pHookedPlayer->m_Shared.IsInvulnerable() &&
  1312. pHookedPlayer->m_Shared.GetCarryingRuneType() != RUNE_RESIST )
  1313. {
  1314. pHookedPlayer->m_Shared.AddCond( TF_COND_PLAGUE, PERMANENT_CONDITION, this );
  1315. }
  1316. }
  1317. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1318. {
  1319. // prevents player from standing on bot's head to block its movement.
  1320. if ( pGroundEntity && pGroundEntity->IsPlayer() )
  1321. {
  1322. Vector vPush = GetAbsOrigin() - pGroundEntity->GetAbsOrigin();
  1323. vPush.z = 0.f;
  1324. vPush.NormalizeInPlace();
  1325. vPush.z = 1.f;
  1326. vPush *= 100.f;
  1327. ApplyAbsVelocityImpulse( vPush );
  1328. }
  1329. }
  1330. // Scale our head
  1331. m_flHeadScale = Approach( GetDesiredHeadScale(), m_flHeadScale, GetHeadScaleSpeed() );
  1332. // scale our torso
  1333. m_flTorsoScale = Approach( GetDesiredTorsoScale(), m_flTorsoScale, GetTorsoScaleSpeed() );
  1334. // scale our torso
  1335. m_flHandScale = Approach( GetDesiredHandScale(), m_flHandScale, GetHandScaleSpeed() );
  1336. /*
  1337. #ifdef STAGING_ONLY
  1338. if ( m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  1339. {
  1340. // JetPack testing
  1341. if ( m_nButtons & IN_JUMP && !( GetFlags() & FL_ONGROUND ) && m_Shared.GetSpaceJumpChargeMeter() > tf_space_thrust_use_rate.GetFloat() )
  1342. {
  1343. //mv->m_vecVelocity[2] += 10.0f;
  1344. Vector vThrust = Vector(0,0,0);
  1345. switch( GetPlayerClass()->GetClassIndex() )
  1346. {
  1347. case TF_CLASS_SCOUT : vThrust.z = tf_space_thrust_scout.GetFloat(); break;
  1348. case TF_CLASS_SNIPER : vThrust.z = tf_space_thrust_sniper.GetFloat(); break;
  1349. case TF_CLASS_SOLDIER : vThrust.z = tf_space_thrust_soldier.GetFloat(); break;
  1350. case TF_CLASS_DEMOMAN : vThrust.z = tf_space_thrust_demo.GetFloat(); break;
  1351. case TF_CLASS_MEDIC : vThrust.z = tf_space_thrust_medic.GetFloat(); break;
  1352. case TF_CLASS_HEAVYWEAPONS : vThrust.z = tf_space_thrust_heavy.GetFloat(); break;
  1353. case TF_CLASS_PYRO : vThrust.z = tf_space_thrust_pyro.GetFloat(); break;
  1354. case TF_CLASS_SPY : vThrust.z = tf_space_thrust_spy.GetFloat(); break;
  1355. case TF_CLASS_ENGINEER : vThrust.z = tf_space_thrust_engy.GetFloat(); break;
  1356. }
  1357. ApplyAbsVelocityImpulse( vThrust );
  1358. m_Shared.SetSpaceJumpChargeMeter( m_Shared.GetSpaceJumpChargeMeter() - tf_space_thrust_use_rate.GetFloat() );
  1359. }
  1360. else
  1361. {
  1362. if ( GetFlags() & FL_ONGROUND )
  1363. {
  1364. m_Shared.SetSpaceJumpChargeMeter( m_Shared.GetSpaceJumpChargeMeter() + tf_space_thrust_recharge_rate.GetFloat() );
  1365. }
  1366. }
  1367. }
  1368. #endif
  1369. */
  1370. SetContextThink( &CTFPlayer::TFPlayerThink, gpGlobals->curtime, "TFPlayerThink" );
  1371. m_flLastThinkTime = gpGlobals->curtime;
  1372. }
  1373. //-----------------------------------------------------------------------------
  1374. // Purpose: Returns a portion of health every think.
  1375. //-----------------------------------------------------------------------------
  1376. void CTFPlayer::RegenThink( void )
  1377. {
  1378. if ( !IsAlive() )
  1379. return;
  1380. // Queue the next think
  1381. SetContextThink( &CTFPlayer::RegenThink, gpGlobals->curtime + TF_REGEN_TIME, "RegenThink" );
  1382. // if we're going in to this too often, quit out.
  1383. if ( m_flLastHealthRegenAt + TF_REGEN_TIME > gpGlobals->curtime )
  1384. return;
  1385. bool bShowRegen = true;
  1386. // Medic has a base regen amount
  1387. if ( GetPlayerClass()->GetClassIndex() == TF_CLASS_MEDIC )
  1388. {
  1389. // Heal faster if we haven't been in combat for a while.
  1390. float flTimeSinceDamage = gpGlobals->curtime - GetLastDamageReceivedTime();
  1391. float flScale = RemapValClamped( flTimeSinceDamage, 5.0f, 10.0f, 1.0f, 2.0f );
  1392. float flRegenAmt = TF_REGEN_AMOUNT;
  1393. // If you are healing a hurt patient, increase your base regen
  1394. CTFPlayer *pPatient = ToTFPlayer( MedicGetHealTarget() );
  1395. if ( pPatient && pPatient->GetHealth() < pPatient->GetMaxHealth() )
  1396. {
  1397. // Double regen amount
  1398. flRegenAmt += TF_REGEN_AMOUNT;
  1399. }
  1400. flRegenAmt *= flScale;
  1401. // If the medic has this attribute, increase their regen.
  1402. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  1403. {
  1404. int iHealingMastery = 0;
  1405. CALL_ATTRIB_HOOK_INT( iHealingMastery, healing_mastery );
  1406. if ( iHealingMastery )
  1407. {
  1408. float flPerc = RemapValClamped( (float)iHealingMastery, 1.f, 4.f, 1.25f, 2.f );
  1409. flRegenAmt *= flPerc;
  1410. }
  1411. }
  1412. m_flAccumulatedHealthRegen += flRegenAmt;
  1413. bShowRegen = false;
  1414. }
  1415. // Other classes can be regenerated by items
  1416. float flRegenAmount = 0;
  1417. CALL_ATTRIB_HOOK_FLOAT( flRegenAmount, add_health_regen );
  1418. if ( flRegenAmount )
  1419. {
  1420. float flTimeSinceDamage = gpGlobals->curtime - GetLastDamageReceivedTime();
  1421. float flScale = 1.0f;
  1422. // Ignore Scale for MvM, always give full regen
  1423. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1424. {
  1425. flScale = 1.0f;
  1426. }
  1427. else if ( flTimeSinceDamage < 5.0f )
  1428. {
  1429. flScale = 0.25f;
  1430. }
  1431. else
  1432. {
  1433. flScale = RemapValClamped( flTimeSinceDamage, 5.0f, 10.0f, 0.5f, 1.0f );
  1434. }
  1435. flRegenAmount *= flScale;
  1436. }
  1437. m_flAccumulatedHealthRegen += flRegenAmount;
  1438. // if ( m_Shared.InCond( TF_COND_HEALING_DEBUFF ) )
  1439. // {
  1440. // m_flAccumulatedHealthRegen *= 0.75f;
  1441. // }
  1442. int nHealAmount = 0;
  1443. if ( m_flAccumulatedHealthRegen >= 1.f )
  1444. {
  1445. nHealAmount = floor( m_flAccumulatedHealthRegen );
  1446. if ( GetHealth() < GetMaxHealth() )
  1447. {
  1448. int nHealedAmount = TakeHealth( nHealAmount, DMG_GENERIC | DMG_IGNORE_DEBUFFS );
  1449. if ( nHealedAmount > 0 )
  1450. {
  1451. IGameEvent *event = gameeventmanager->CreateEvent( "player_healed" );
  1452. if ( event )
  1453. {
  1454. event->SetInt( "priority", 1 ); // HLTV event priority
  1455. event->SetInt( "patient", GetUserID() );
  1456. event->SetInt( "healer", GetUserID() );
  1457. event->SetInt( "amount", nHealedAmount );
  1458. gameeventmanager->FireEvent( event );
  1459. }
  1460. }
  1461. }
  1462. }
  1463. else if ( m_flAccumulatedHealthRegen < -1.f )
  1464. {
  1465. nHealAmount = ceil( m_flAccumulatedHealthRegen );
  1466. TakeDamage( CTakeDamageInfo( this, this, NULL, vec3_origin, WorldSpaceCenter(), nHealAmount * -1, DMG_GENERIC ) );
  1467. }
  1468. if ( GetHealth() < GetMaxHealth() && nHealAmount != 0 && bShowRegen )
  1469. {
  1470. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  1471. if ( event )
  1472. {
  1473. event->SetInt( "amount", nHealAmount );
  1474. event->SetInt( "entindex", entindex() );
  1475. event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX );
  1476. gameeventmanager->FireEvent( event );
  1477. }
  1478. }
  1479. m_flAccumulatedHealthRegen -= nHealAmount;
  1480. m_flLastHealthRegenAt = gpGlobals->curtime;
  1481. // Regenerate ammo
  1482. if ( m_flNextAmmoRegenAt < gpGlobals->curtime )
  1483. {
  1484. // We regen ammo every 5 seconds
  1485. m_flNextAmmoRegenAt = gpGlobals->curtime + 5.0;
  1486. flRegenAmount = 0;
  1487. CALL_ATTRIB_HOOK_FLOAT( flRegenAmount, addperc_ammo_regen );
  1488. if ( flRegenAmount )
  1489. {
  1490. RegenAmmoInternal( TF_AMMO_PRIMARY, flRegenAmount );
  1491. RegenAmmoInternal( TF_AMMO_SECONDARY, flRegenAmount );
  1492. }
  1493. // Regenerate metal
  1494. int iMetal = 0;
  1495. CALL_ATTRIB_HOOK_INT( iMetal, add_metal_regen );
  1496. if ( iMetal )
  1497. {
  1498. GiveAmmo( iMetal, TF_AMMO_METAL, true );
  1499. }
  1500. }
  1501. }
  1502. //-----------------------------------------------------------------------------
  1503. // Purpose: Returns a portion of health every think.
  1504. //-----------------------------------------------------------------------------
  1505. void CTFPlayer::RuneRegenThink( void )
  1506. {
  1507. if ( !IsAlive() )
  1508. return;
  1509. // Queue the next think
  1510. SetContextThink( &CTFPlayer::RuneRegenThink, gpGlobals->curtime + TF_REGEN_TIME_RUNE, "RuneRegenThink" );
  1511. // if we're going in to this too often, quit out.
  1512. if ( m_flLastRuneHealthRegenAt + TF_REGEN_TIME_RUNE > gpGlobals->curtime )
  1513. return;
  1514. int nRuneType = m_Shared.GetCarryingRuneType();
  1515. if ( nRuneType == RUNE_NONE && !HasTheFlag() )
  1516. return;
  1517. // Regenerate health
  1518. float flAmount = 0.f;
  1519. switch ( GetPlayerClass()->GetClassIndex() )
  1520. {
  1521. case TF_CLASS_SCOUT:
  1522. case TF_CLASS_SPY:
  1523. flAmount = 16;
  1524. break;
  1525. case TF_CLASS_SNIPER:
  1526. case TF_CLASS_ENGINEER:
  1527. flAmount = 14;
  1528. break;
  1529. case TF_CLASS_MEDIC:
  1530. case TF_CLASS_DEMOMAN:
  1531. case TF_CLASS_PYRO:
  1532. flAmount = 12;
  1533. break;
  1534. case TF_CLASS_SOLDIER:
  1535. flAmount = 10;
  1536. break;
  1537. case TF_CLASS_HEAVYWEAPONS:
  1538. flAmount = 8;
  1539. break;
  1540. }
  1541. if ( nRuneType == RUNE_REGEN )
  1542. {
  1543. m_flAccumulatedRuneHealthRegen += flAmount;
  1544. }
  1545. // King and buffed team mates get some health regeneration unless they have the plague
  1546. else if ( ( nRuneType == RUNE_KING || m_Shared.InCond( TF_COND_KING_BUFFED ) ) && !m_Shared.InCond( TF_COND_PLAGUE ) )
  1547. {
  1548. flAmount *= 0.3;
  1549. m_flAccumulatedRuneHealthRegen += flAmount;
  1550. }
  1551. // non powered up flag carriers get a small health regeneration
  1552. else if ( HasTheFlag() && nRuneType == RUNE_NONE )
  1553. {
  1554. flAmount *= 0.1;
  1555. m_flAccumulatedRuneHealthRegen += flAmount;
  1556. }
  1557. int nHealAmount = 0;
  1558. if ( m_flAccumulatedRuneHealthRegen >= 1.0 )
  1559. {
  1560. nHealAmount = floor( m_flAccumulatedRuneHealthRegen );
  1561. if ( GetHealth() < GetMaxHealth() )
  1562. {
  1563. TakeHealth( nHealAmount, DMG_GENERIC );
  1564. int nHealedAmount = TakeHealth( nHealAmount, DMG_GENERIC );
  1565. if ( nHealedAmount > 0 )
  1566. {
  1567. IGameEvent *event = gameeventmanager->CreateEvent( "player_healed" );
  1568. if ( event )
  1569. {
  1570. event->SetInt("priority", 1); // HLTV event priority
  1571. event->SetInt("patient", GetUserID());
  1572. event->SetInt( "healer", GetUserID() );
  1573. event->SetInt( "amount", nHealedAmount );
  1574. gameeventmanager->FireEvent( event );
  1575. }
  1576. }
  1577. }
  1578. }
  1579. else if ( m_flAccumulatedRuneHealthRegen < -1.0 )
  1580. {
  1581. nHealAmount = ceil( m_flAccumulatedRuneHealthRegen );
  1582. TakeDamage( CTakeDamageInfo( this, this, NULL, vec3_origin, WorldSpaceCenter(), nHealAmount * -1, DMG_GENERIC ) );
  1583. }
  1584. if ( GetHealth() < GetMaxHealth() && nHealAmount != 0 )
  1585. {
  1586. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  1587. if ( event )
  1588. {
  1589. event->SetInt( "amount", nHealAmount );
  1590. event->SetInt( "entindex", entindex() );
  1591. event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX );
  1592. gameeventmanager->FireEvent( event );
  1593. }
  1594. }
  1595. m_flAccumulatedRuneHealthRegen -= nHealAmount;
  1596. m_flLastRuneHealthRegenAt = gpGlobals->curtime;
  1597. // Regenerate ammo and metal
  1598. if ( m_flNextRuneAmmoRegenAt < gpGlobals->curtime )
  1599. {
  1600. m_flNextRuneAmmoRegenAt = gpGlobals->curtime + 5;
  1601. if ( nRuneType == RUNE_REGEN )
  1602. {
  1603. RegenAmmoInternal( TF_AMMO_PRIMARY, 0.5f );
  1604. RegenAmmoInternal( TF_AMMO_SECONDARY, 0.5f );
  1605. GiveAmmo( 200, TF_AMMO_METAL, true );
  1606. }
  1607. }
  1608. }
  1609. //-----------------------------------------------------------------------------
  1610. // Purpose:
  1611. //-----------------------------------------------------------------------------
  1612. void CTFPlayer::RegenAmmoInternal( int iIndex, float flRegen )
  1613. {
  1614. m_flAccumulatedAmmoRegens[iIndex] += flRegen;
  1615. // As soon as we have enough accumulated to regen a single unit of ammo, do it.
  1616. int iMaxAmmo = GetMaxAmmo(iIndex);
  1617. int iAmmo = m_flAccumulatedAmmoRegens[iIndex] * iMaxAmmo;
  1618. if ( iAmmo >= 1 )
  1619. {
  1620. GiveAmmo( iAmmo, iIndex, true );
  1621. m_flAccumulatedAmmoRegens[iIndex] -= ((float)iAmmo / (float)iMaxAmmo);
  1622. }
  1623. }
  1624. //-----------------------------------------------------------------------------
  1625. // Purpose:
  1626. //-----------------------------------------------------------------------------
  1627. CTFPlayer::~CTFPlayer()
  1628. {
  1629. delete [] m_damageRateArray;
  1630. DestroyRagdoll();
  1631. m_PlayerAnimState->Release();
  1632. FOR_EACH_VEC( m_ItemsToTest, i )
  1633. {
  1634. int iDef = TESTITEM_DEFINITIONS_BEGIN_AT + m_ItemsToTest[i].scriptItem.GetItemDefIndex();
  1635. ItemSystem()->GetItemSchema()->ItemTesting_DiscardTestDefinition( iDef );
  1636. m_ItemsToTest[i].pKV->deleteThis();
  1637. m_ItemsToTest[i].pKV = NULL;
  1638. }
  1639. if ( m_hReviveMarker )
  1640. {
  1641. UTIL_Remove( m_hReviveMarker );
  1642. m_hReviveMarker = NULL;
  1643. }
  1644. }
  1645. //-----------------------------------------------------------------------------
  1646. // Purpose:
  1647. //-----------------------------------------------------------------------------
  1648. CTFPlayer *CTFPlayer::CreatePlayer( const char *className, edict_t *ed )
  1649. {
  1650. CTFPlayer::s_PlayerEdict = ed;
  1651. return (CTFPlayer*)CreateEntityByName( className );
  1652. }
  1653. //-----------------------------------------------------------------------------
  1654. // Purpose:
  1655. //-----------------------------------------------------------------------------
  1656. void CTFPlayer::UpdateTimers( void )
  1657. {
  1658. m_Shared.ConditionThink();
  1659. m_Shared.InvisibilityThink();
  1660. }
  1661. //-----------------------------------------------------------------------------
  1662. // Estimate where a projectile fired from the given weapon will initially hit (it may bounce on from there).
  1663. // NOTE: We should be able to directly compute this knowing initial velocity, angle, gravity, etc,
  1664. // but I have been unable to find a formula that reproduces what our physics actually
  1665. // do.
  1666. //-----------------------------------------------------------------------------
  1667. Vector CTFPlayer::EstimateProjectileImpactPosition( CTFWeaponBaseGun *weapon )
  1668. {
  1669. if ( !weapon )
  1670. {
  1671. return GetAbsOrigin();
  1672. }
  1673. const QAngle &angles = EyeAngles();
  1674. float initVel = weapon->IsWeapon( TF_WEAPON_PIPEBOMBLAUNCHER ) ? 900.0f : weapon->GetProjectileSpeed();
  1675. CALL_ATTRIB_HOOK_FLOAT( initVel, mult_projectile_range );
  1676. return EstimateProjectileImpactPosition( angles.x, angles.y, initVel );
  1677. }
  1678. //-----------------------------------------------------------------------------
  1679. // Estimate where a stickybomb projectile will hit,
  1680. // using given pitch, yaw, and weapon charge (0-1)
  1681. //-----------------------------------------------------------------------------
  1682. Vector CTFPlayer::EstimateStickybombProjectileImpactPosition( float pitch, float yaw, float charge )
  1683. {
  1684. // estimate impact spot
  1685. float initVel = charge * ( TF_PIPEBOMB_MAX_CHARGE_VEL - TF_PIPEBOMB_MIN_CHARGE_VEL ) + TF_PIPEBOMB_MIN_CHARGE_VEL;
  1686. CALL_ATTRIB_HOOK_FLOAT( initVel, mult_projectile_range );
  1687. return EstimateProjectileImpactPosition( pitch, yaw, initVel );
  1688. }
  1689. //-----------------------------------------------------------------------------
  1690. // Estimate where a projectile fired will initially hit (it may bounce on from there),
  1691. // using given pitch, yaw, and initial velocity.
  1692. //-----------------------------------------------------------------------------
  1693. Vector CTFPlayer::EstimateProjectileImpactPosition( float pitch, float yaw, float initVel )
  1694. {
  1695. // copied from CTFWeaponBaseGun::FirePipeBomb()
  1696. Vector vecForward, vecRight, vecUp;
  1697. QAngle angles( pitch, yaw, 0.0f );
  1698. AngleVectors( angles, &vecForward, &vecRight, &vecUp );
  1699. // we will assume bots never flip viewmodels
  1700. float fRight = 8.f;
  1701. Vector vecSrc = Weapon_ShootPosition();
  1702. vecSrc += vecForward * 16.0f + vecRight * fRight + vecUp * -6.0f;
  1703. const float initVelScale = 0.9f;
  1704. Vector vecVelocity = initVelScale * ( ( vecForward * initVel ) + ( vecUp * 200.0f ) );
  1705. const float timeStep = 0.01f;
  1706. const float maxTime = 5.0f;
  1707. Vector pos = vecSrc;
  1708. Vector lastPos = pos;
  1709. const float g = GetCurrentGravity();
  1710. // compute forward facing unit vector in horiz plane
  1711. Vector alongDir = vecForward;
  1712. alongDir.z = 0.0f;
  1713. alongDir.NormalizeInPlace();
  1714. float alongVel = FastSqrt( vecVelocity.x * vecVelocity.x + vecVelocity.y * vecVelocity.y );
  1715. trace_t trace;
  1716. NextBotTraceFilterIgnoreActors traceFilter( NULL, COLLISION_GROUP_NONE );
  1717. float t;
  1718. for( t = 0.0f; t < maxTime; t += timeStep )
  1719. {
  1720. float along = alongVel * t;
  1721. float height = vecVelocity.z * t - 0.5f * g * t * t;
  1722. pos.x = vecSrc.x + alongDir.x * along;
  1723. pos.y = vecSrc.y + alongDir.y * along;
  1724. pos.z = vecSrc.z + height;
  1725. UTIL_TraceHull( lastPos, pos, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  1726. if ( trace.DidHit() )
  1727. {
  1728. #ifdef STAGING_ONLY
  1729. if ( tf_debug_ballistics.GetBool() )
  1730. {
  1731. NDebugOverlay::Cross3D( trace.endpos, 10.0f, 100, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1732. }
  1733. #endif
  1734. break;
  1735. }
  1736. #ifdef STAGING_ONLY
  1737. if ( tf_debug_ballistics.GetBool() )
  1738. {
  1739. NDebugOverlay::Line( lastPos, pos, 0, 255, 0, false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1740. }
  1741. #endif
  1742. lastPos = pos;
  1743. }
  1744. return trace.endpos;
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Purpose:
  1748. //-----------------------------------------------------------------------------
  1749. bool CTFPlayer::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1750. {
  1751. // TF Players only process scene events on the server while running taunts
  1752. if ( !IsTaunting() )
  1753. return false;
  1754. // Only process sequences
  1755. if ( event->GetType() != CChoreoEvent::SEQUENCE )
  1756. return false;
  1757. return BaseClass::ProcessSceneEvent( info, scene, event );
  1758. }
  1759. //-----------------------------------------------------------------------------
  1760. // Purpose:
  1761. //-----------------------------------------------------------------------------
  1762. void CTFPlayer::PreThink()
  1763. {
  1764. // Update timers.
  1765. UpdateTimers();
  1766. // Pass through to the base class think.
  1767. BaseClass::PreThink();
  1768. // Reset bullet force accumulator, only lasts one frame, for ragdoll forces from multiple shots.
  1769. m_vecTotalBulletForce = vec3_origin;
  1770. CheckForIdle();
  1771. ProcessSceneEvents();
  1772. if ( TFGameRules()->IsInArenaMode() == true )
  1773. {
  1774. if ( TFGameRules()->State_Get() != GR_STATE_TEAM_WIN )
  1775. {
  1776. if ( GetTeamNumber() == TEAM_SPECTATOR )
  1777. {
  1778. m_Local.m_iHideHUD &= ~HIDEHUD_MISCSTATUS;
  1779. }
  1780. }
  1781. }
  1782. // Hype Decreases over time
  1783. if ( IsPlayerClass( TF_CLASS_SCOUT ) )
  1784. {
  1785. float flHypeDecays = 0;
  1786. CALL_ATTRIB_HOOK_FLOAT( flHypeDecays, hype_decays_over_time );
  1787. if ( flHypeDecays != 0 )
  1788. {
  1789. // Loose hype over time
  1790. float flHype = m_Shared.GetScoutHypeMeter();
  1791. flHype = flHype - flHypeDecays;
  1792. m_Shared.SetScoutHypeMeter( flHype );
  1793. TeamFortress_SetSpeed();
  1794. }
  1795. }
  1796. #ifdef STAGING_ONLY
  1797. // show ballistic path for currently equipped weapon (ie: grenades)
  1798. if ( tf_debug_ballistics.GetBool() )
  1799. {
  1800. CTFWeaponBaseGun *myWeapon = dynamic_cast< CTFWeaponBaseGun * >( m_Shared.GetActiveTFWeapon() );
  1801. EstimateProjectileImpactPosition( myWeapon );
  1802. }
  1803. if ( tf_debug_ballistic_targeting.GetBool() )
  1804. {
  1805. CTFWeaponBaseGun *myWeapon = dynamic_cast< CTFWeaponBaseGun * >( m_Shared.GetActiveTFWeapon() );
  1806. Vector hitPos = EstimateProjectileImpactPosition( myWeapon );
  1807. Vector toEye = EyePosition() - hitPos;
  1808. Vector toTarget = tf_debug_ballistic_target - hitPos;
  1809. float error = toTarget.NormalizeInPlace();
  1810. if ( error < 20.0f )
  1811. {
  1812. // on target
  1813. NDebugOverlay::Cross3D( tf_debug_ballistic_target, 10.0f, 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1814. }
  1815. else
  1816. {
  1817. if ( DotProduct( toEye, toTarget ) > 0.0f )
  1818. {
  1819. // too far
  1820. NDebugOverlay::Cross3D( tf_debug_ballistic_target, 10.0f, 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1821. NDebugOverlay::VertArrow( tf_debug_ballistic_target, tf_debug_ballistic_target - Vector( 0, 0, error ), 10.0f, 0, 0, 255, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1822. }
  1823. else
  1824. {
  1825. // too near
  1826. NDebugOverlay::Cross3D( tf_debug_ballistic_target, 10.0f, 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1827. NDebugOverlay::VertArrow( tf_debug_ballistic_target, tf_debug_ballistic_target + Vector( 0, 0, error ), 10.0f, 255, 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  1828. }
  1829. }
  1830. }
  1831. #endif // STAGING_ONLY
  1832. }
  1833. ConVar mp_idledealmethod( "mp_idledealmethod", "1", FCVAR_GAMEDLL, "Deals with Idle Players. 1 = Sends them into Spectator mode then kicks them if they're still idle, 2 = Kicks them out of the game;" );
  1834. ConVar mp_idlemaxtime( "mp_idlemaxtime", "3", FCVAR_GAMEDLL, "Maximum time a player is allowed to be idle (in minutes)" );
  1835. //-----------------------------------------------------------------------------
  1836. // Purpose:
  1837. //-----------------------------------------------------------------------------
  1838. void CTFPlayer::CheckForIdle( void )
  1839. {
  1840. if ( m_afButtonLast != m_nButtons )
  1841. m_flLastAction = gpGlobals->curtime;
  1842. if ( mp_idledealmethod.GetInt() )
  1843. {
  1844. if ( IsHLTV() || IsReplay() )
  1845. return;
  1846. if ( IsFakeClient() )
  1847. return;
  1848. if ( IsCoaching() && GetStudent() != NULL )
  1849. return;
  1850. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  1851. return;
  1852. if ( TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS )
  1853. return;
  1854. //Don't mess with the host on a listen server (probably one of us debugging something)
  1855. if ( engine->IsDedicatedServer() == false && entindex() == 1 )
  1856. return;
  1857. if ( IsAutoKickDisabled() )
  1858. return;
  1859. const bool cbMoving = ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) != 0;
  1860. m_bIsAFK = false;
  1861. if ( !cbMoving && PointInRespawnRoom( this, WorldSpaceCenter() ) )
  1862. {
  1863. m_flTimeInSpawn += TICK_INTERVAL;
  1864. }
  1865. else
  1866. m_flTimeInSpawn = 0;
  1867. if ( TFGameRules()->IsInArenaMode() && tf_arena_use_queue.GetBool() == true )
  1868. {
  1869. if ( GetTeamNumber() == TEAM_SPECTATOR )
  1870. return;
  1871. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN && TFGameRules()->GetWinningTeam() != GetTeamNumber() )
  1872. {
  1873. if ( m_bArenaIsAFK )
  1874. {
  1875. m_bIsAFK = true;
  1876. m_bArenaIsAFK = false;
  1877. }
  1878. }
  1879. }
  1880. else
  1881. {
  1882. // Cannot possibly get out of the spawn room in 0 seconds--so if the ConVar says 0, let's assume 30 seconds.
  1883. float flIdleTime = Max( mp_idlemaxtime.GetFloat() * 60, 30.0f );
  1884. if ( TFGameRules()->InStalemate() )
  1885. {
  1886. flIdleTime = mp_stalemate_timelimit.GetInt() * 0.5f;
  1887. }
  1888. m_bIsAFK = ( gpGlobals->curtime - m_flLastAction ) > flIdleTime
  1889. || ( m_flTimeInSpawn > flIdleTime );
  1890. }
  1891. if ( m_bIsAFK == true )
  1892. {
  1893. bool bKickPlayer = false;
  1894. ConVarRef mp_allowspectators( "mp_allowspectators" );
  1895. if ( ( mp_allowspectators.IsValid() && mp_allowspectators.GetBool() == false ) || ( TFGameRules()->IsInArenaMode() && tf_arena_use_queue.GetBool() ) )
  1896. {
  1897. // just kick the player if this server doesn't allow spectators
  1898. bKickPlayer = true;
  1899. }
  1900. else if ( mp_idledealmethod.GetInt() == 1 )
  1901. {
  1902. if ( GetTeamNumber() < FIRST_GAME_TEAM )
  1903. {
  1904. bKickPlayer = true;
  1905. }
  1906. else
  1907. {
  1908. //First send them into spectator mode then kick him.
  1909. ForceChangeTeam( TEAM_SPECTATOR );
  1910. m_flLastAction = gpGlobals->curtime;
  1911. m_flTimeInSpawn = 0;
  1912. return;
  1913. }
  1914. }
  1915. else if ( mp_idledealmethod.GetInt() == 2 )
  1916. {
  1917. bKickPlayer = true;
  1918. }
  1919. if ( bKickPlayer == true )
  1920. {
  1921. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#game_idle_kick", GetPlayerName() );
  1922. engine->ServerCommand( UTIL_VarArgs( "kickid %d %s\n", GetUserID(), g_pszIdleKickString ) );
  1923. m_flLastAction = gpGlobals->curtime;
  1924. m_flTimeInSpawn = 0;
  1925. }
  1926. }
  1927. }
  1928. }
  1929. extern ConVar flashlight;
  1930. //-----------------------------------------------------------------------------
  1931. // Purpose:
  1932. //-----------------------------------------------------------------------------
  1933. int CTFPlayer::FlashlightIsOn( void )
  1934. {
  1935. return IsEffectActive( EF_DIMLIGHT );
  1936. }
  1937. //-----------------------------------------------------------------------------
  1938. // Purpose:
  1939. //-----------------------------------------------------------------------------
  1940. void CTFPlayer::FlashlightTurnOn( void )
  1941. {
  1942. if( flashlight.GetInt() > 0 && IsAlive() )
  1943. {
  1944. AddEffects( EF_DIMLIGHT );
  1945. }
  1946. }
  1947. //-----------------------------------------------------------------------------
  1948. // Purpose:
  1949. //-----------------------------------------------------------------------------
  1950. void CTFPlayer::FlashlightTurnOff( void )
  1951. {
  1952. if( IsEffectActive(EF_DIMLIGHT) )
  1953. {
  1954. RemoveEffects( EF_DIMLIGHT );
  1955. }
  1956. }
  1957. //-----------------------------------------------------------------------------
  1958. // Purpose: Update Halloween scenario effects on players.
  1959. //-----------------------------------------------------------------------------
  1960. void CTFPlayer::UpdateHalloween( void )
  1961. {
  1962. // This is a push force
  1963. if ( !m_vHalloweenKartPush.IsZero() )
  1964. {
  1965. if ( !m_Shared.InCond( TF_COND_INVULNERABLE_USER_BUFF ) )
  1966. {
  1967. CPVSFilter filter( GetAbsOrigin() );
  1968. TE_TFParticleEffect( filter, 0.0, "kart_impact_sparks", GetAbsOrigin(), vec3_angle, this, PATTACH_ABSORIGIN );
  1969. float flStunDuration = m_vHalloweenKartPush.Length() / 1000.0f;
  1970. if ( m_vHalloweenKartPush.LengthSqr() > 1000 * 1000 )
  1971. {
  1972. TE_TFParticleEffect( filter, 0.0, "kartimpacttrail", GetAbsOrigin(), vec3_angle, this, PATTACH_ABSORIGIN_FOLLOW );
  1973. EmitSound( "BumperCar.BumpIntoAir" );
  1974. EmitSound( "BumperCar.BumpHard" );
  1975. }
  1976. else
  1977. {
  1978. EmitSound( "BumperCar.Bump" );
  1979. }
  1980. if ( tf_halloween_kart_stun_enabled.GetBool() )
  1981. {
  1982. m_Shared.StunPlayer( flStunDuration * tf_halloween_kart_stun_duration_scale.GetFloat(), tf_halloween_kart_stun_amount.GetFloat(), TF_STUN_BOTH | TF_STUN_NO_EFFECTS );
  1983. }
  1984. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH ) )
  1985. {
  1986. m_Shared.RemoveCond( TF_COND_HALLOWEEN_KART_DASH );
  1987. }
  1988. ApplyAirBlastImpulse( m_vHalloweenKartPush );
  1989. }
  1990. m_vHalloweenKartPush.Zero();
  1991. }
  1992. }
  1993. //-----------------------------------------------------------------------------
  1994. // Purpose:
  1995. //-----------------------------------------------------------------------------
  1996. void CTFPlayer::AddHalloweenKartPushEvent( CTFPlayer *pOther, CBaseEntity *pInflictor, CBaseEntity *pWeapon, Vector vForce, int iDamage, int iDamageType /* = 0 */ )
  1997. {
  1998. // Create a damage event so they can get credit for the kill
  1999. //m_vHalloweenKartPushEventTime + 0.2 > gpGlobals->curtime &&
  2000. if ( m_Shared.InCond( TF_COND_INVULNERABLE_USER_BUFF ) )
  2001. return;
  2002. // Ignore small forces
  2003. float flForce = vForce.LengthSqr();
  2004. if ( flForce < 100.0f )
  2005. return;
  2006. float flExtraMultiplier = 1.f;
  2007. if ( pOther && pOther != this && pOther->m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  2008. {
  2009. iDamage *= tf_halloween_kart_bomb_head_damage_scale.GetFloat();
  2010. flExtraMultiplier = tf_halloween_kart_bomb_head_impulse_scale.GetFloat();
  2011. pOther->SetKartBombHeadTarget( pOther );
  2012. pOther->m_Shared.RemoveCond( TF_COND_HALLOWEEN_BOMB_HEAD );
  2013. }
  2014. const float flCurrentKartKnockbackMultiplier = GetKartKnockbackMultiplier( flExtraMultiplier );
  2015. if ( pOther )
  2016. {
  2017. // Fake Damage
  2018. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  2019. if ( event )
  2020. {
  2021. int iKartDamageType = DMG_CLUB | DMG_PREVENT_PHYSICS_FORCE;
  2022. if ( pOther != this && flCurrentKartKnockbackMultiplier * vForce.LengthSqr() > 1000 * 1000 )
  2023. {
  2024. iKartDamageType |= DMG_CRITICAL;
  2025. }
  2026. event->SetInt( "userid", GetUserID() );
  2027. event->SetInt( "health", MAX( 0, m_iHealth ) );
  2028. // HLTV event priority, not transmitted
  2029. event->SetInt( "priority", 5 );
  2030. event->SetInt( "damageamount", iDamage );
  2031. // Hurt by another player.
  2032. event->SetInt( "attacker", pOther->GetUserID() );
  2033. event->SetInt( "custom", TF_DMG_CUSTOM_SUICIDE );
  2034. event->SetBool( "crit", ( iKartDamageType & DMG_CRITICAL ) != 0 );
  2035. event->SetBool( "allseecrit", ( iKartDamageType & DMG_CRITICAL ) != 0 );
  2036. event->SetInt( "bonuseffect", (int)kBonusEffect_None );
  2037. //
  2038. gameeventmanager->FireEvent( event );
  2039. }
  2040. m_AchievementData.AddDamagerToHistory( pOther );
  2041. m_AchievementData.AddPusherToHistory( pOther );
  2042. //m_Shared.SetAssist( pOther );
  2043. }
  2044. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  2045. {
  2046. m_iKartHealth += iDamage;
  2047. }
  2048. // HHH
  2049. if ( TFGameRules()->IsIT( pOther ) )
  2050. {
  2051. // Tag! You're IT!
  2052. TFGameRules()->SetIT( this );
  2053. }
  2054. if ( iDamageType == TF_DMG_CUSTOM_DECAPITATION_BOSS )
  2055. {
  2056. m_flHHHKartAttackTime = gpGlobals->curtime;
  2057. }
  2058. //m_vHalloweenKartPushEventTime = gpGlobals->curtime;
  2059. float flImpulseScale = 1.0f;
  2060. // m_flKartHealth might change by this point, calculate new knock back multiplier
  2061. const float flNewKartKnockbackMultiplier = GetKartKnockbackMultiplier( flExtraMultiplier );
  2062. // push other
  2063. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  2064. {
  2065. // If applying damage increase the force, otherwise its likely just a wall collision
  2066. if ( iDamage > 0 )
  2067. {
  2068. vForce *= flImpulseScale * flNewKartKnockbackMultiplier;
  2069. vForce.z *= ( ( flNewKartKnockbackMultiplier - 1.0f ) * 0.20f ) + 1.0f;
  2070. // Decrease all forces if in the air
  2071. if (!(GetFlags() & FL_ONGROUND) )
  2072. {
  2073. vForce *= tf_halloween_kart_impact_air_scale.GetFloat();
  2074. }
  2075. }
  2076. }
  2077. else
  2078. {
  2079. // Make non-karters take damage!
  2080. vForce *= ( flImpulseScale * 2.0f );
  2081. //ghostinfo.SetDamageCustom( TF_DMG_CUSTOM_KART );
  2082. // Create a damage event based on Speed
  2083. float flDamage = vForce.Length() / 50.0f + RandomFloat( 3.0f, 7.0f );
  2084. CTakeDamageInfo info;
  2085. info.SetAttacker( pOther );
  2086. info.SetInflictor( pOther );
  2087. info.SetDamage( flDamage );
  2088. info.SetDamageCustom( TF_DMG_CUSTOM_KART );
  2089. info.SetDamagePosition( GetAbsOrigin() );
  2090. int iKartDamageType = DMG_CLUB;
  2091. if ( flDamage > 20 )
  2092. {
  2093. iKartDamageType |= DMG_CRITICAL;
  2094. }
  2095. info.SetDamageType( iKartDamageType );
  2096. info.SetDamageForce( vForce );
  2097. TakeDamage( info );
  2098. return;
  2099. }
  2100. m_vHalloweenKartPush += vForce;
  2101. // Dropped collection game tokens if hit hard enough and we're in the collection minigame
  2102. if ( pOther && iDamage > 10 && CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() &&
  2103. CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame()->GetMinigameType() == CTFMiniGame::EMinigameType::MINIGAME_HALLOWEEN2014_COLLECTION )
  2104. {
  2105. CUtlVector< CTFPlayer* > vecEveryone;
  2106. CollectPlayers( &vecEveryone );
  2107. int nNumPlayers = vecEveryone.Count();
  2108. // Drop tokens
  2109. uint32 nNumToSpawn = ( iDamage / 5 ) + 1;
  2110. nNumToSpawn = RemapValClamped( nNumPlayers, 8, 16, nNumToSpawn, 1 );
  2111. if ( gpGlobals->curtime > m_flNextBonusDucksVOAllowedTime )
  2112. {
  2113. // Tell this user to play a "Bonus Ducks!" line.
  2114. CSingleUserRecipientFilter filter( pOther );
  2115. UserMessageBegin( filter, "BonusDucks" );
  2116. WRITE_BYTE( entindex() );
  2117. WRITE_BYTE( false );
  2118. MessageEnd();
  2119. }
  2120. while( nNumToSpawn-- )
  2121. {
  2122. CHalloweenPickup *pPickup = dynamic_cast< CHalloweenPickup * >( CreateEntityByName( "tf_halloween_pickup" ) );
  2123. if (pPickup)
  2124. {
  2125. pPickup->m_nSkin = 2; // Golden skin
  2126. pPickup->Precache();
  2127. DispatchSpawn(pPickup);
  2128. Vector vecRandom = RandomVector( -200.f, 200.f );
  2129. vecRandom.z = RandomFloat( 300.f, 400.f );
  2130. Vector vecDropVector = vecRandom + vForce * 0.2f;
  2131. pPickup->DropSingleInstance( vecDropVector, this, 1.f, 1.f );
  2132. pPickup->SetAbsOrigin( GetAbsOrigin() + Vector( 0.f, 0.f, 40.f ) );
  2133. pPickup->Activate();
  2134. }
  2135. }
  2136. }
  2137. //DevMsg( "Kart Impact %fx,%fy,%fz - %f Base. %f Multiplayer, %f TotalForce, %d Damage, %i Class \n",
  2138. // vForce.x, vForce.y, vForce.z, vForce.Length(), flNewKartKnockbackMultiplier, vForce.Length() * flNewKartKnockbackMultiplier, iDamage, GetPlayerClass()->GetClassIndex() );
  2139. vForce.z = 0;
  2140. vForce.NormalizeInPlace();
  2141. DispatchParticleEffect( "kart_impact_sparks", GetAbsOrigin() + vForce * 24, GetAbsAngles() );
  2142. }
  2143. //-----------------------------------------------------------------------------
  2144. // Purpose:
  2145. //-----------------------------------------------------------------------------
  2146. float CTFPlayer::GetKartKnockbackMultiplier( float flExtraMultiplier /*= 1.f*/ ) const
  2147. {
  2148. return flExtraMultiplier * ( 1.0f + (float)m_iKartHealth / tf_halloween_kart_damage_to_force.GetFloat() );
  2149. }
  2150. //-----------------------------------------------------------------------------
  2151. // Purpose:
  2152. //-----------------------------------------------------------------------------
  2153. void CTFPlayer::ResetKartDamage()
  2154. {
  2155. m_iKartHealth = 0;
  2156. }
  2157. //-----------------------------------------------------------------------------
  2158. // Purpose:
  2159. //-----------------------------------------------------------------------------
  2160. void CTFPlayer::CancelEurekaTeleport()
  2161. {
  2162. m_bIsTeleportingUsingEurekaEffect = false;
  2163. m_teleportHomeFlashTimer.Invalidate();
  2164. }
  2165. //-----------------------------------------------------------------------------
  2166. // Purpose:
  2167. //-----------------------------------------------------------------------------
  2168. void CTFPlayer::PostThink()
  2169. {
  2170. BaseClass::PostThink();
  2171. QAngle angles = GetLocalAngles();
  2172. angles[PITCH] = 0;
  2173. SetLocalAngles( angles );
  2174. // Store the eye angles pitch so the client can compute its animation state correctly.
  2175. m_angEyeAngles = EyeAngles();
  2176. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  2177. if ( m_flTauntAttackTime && m_flTauntAttackTime < gpGlobals->curtime )
  2178. {
  2179. m_flTauntAttackTime = 0;
  2180. DoTauntAttack();
  2181. }
  2182. // if we are coaching, then capture events for adding annotations
  2183. if ( m_bIsCoaching && m_hStudent )
  2184. {
  2185. if ( ( m_afButtonPressed & ( IN_ATTACK | IN_ATTACK2 ) ) != 0 )
  2186. {
  2187. if ( m_afButtonPressed & IN_ATTACK )
  2188. {
  2189. HandleCoachCommand( this, kCoachCommand_Attack );
  2190. }
  2191. else if ( m_afButtonPressed & IN_ATTACK2 )
  2192. {
  2193. HandleCoachCommand( this, kCoachCommand_Defend );
  2194. }
  2195. }
  2196. if ( m_hStudent->GetTeamNumber() != TEAM_SPECTATOR )
  2197. {
  2198. // tether coach to student--if the coach gets too far, move them toward the student
  2199. Vector vecTarget = m_hStudent->GetAbsOrigin();
  2200. Vector vecDelta = GetAbsOrigin() - vecTarget;
  2201. float flDistance = vecDelta.Length();
  2202. const float kInchesToMeters = 0.0254f;
  2203. const float kMetersToInches = 1.0f / kInchesToMeters;
  2204. const float kMaxDistanceToStudent = 30;
  2205. int distance = RoundFloatToInt( flDistance * kInchesToMeters );
  2206. if ( distance > kMaxDistanceToStudent )
  2207. {
  2208. VectorNormalize( vecDelta );
  2209. SetAbsOrigin( vecTarget + vecDelta * ( kMaxDistanceToStudent * kMetersToInches ) );
  2210. }
  2211. }
  2212. }
  2213. if ( TFGameRules()->IsMannVsMachineMode() )
  2214. {
  2215. // metal is free during setup time
  2216. if ( TFGameRules()->IsQuickBuildTime() )
  2217. {
  2218. GiveAmmo( 1000, TF_AMMO_METAL, true );
  2219. }
  2220. // clamp maximum velocity to avoid sending mini-bosses into the stratosphere
  2221. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  2222. {
  2223. Vector ahead = GetAbsVelocity();
  2224. float speed = ahead.NormalizeInPlace();
  2225. const float velocityLimit = 1000.0f;
  2226. if ( speed > velocityLimit )
  2227. {
  2228. speed = velocityLimit;
  2229. }
  2230. SetAbsVelocity( speed * ahead );
  2231. }
  2232. }
  2233. UpdateHalloween();
  2234. #ifdef STAGING_ONLY
  2235. m_Shared.DoRocketPack();
  2236. #endif // STAGING_ONLY
  2237. }
  2238. //-----------------------------------------------------------------------------
  2239. // Purpose:
  2240. //-----------------------------------------------------------------------------
  2241. void CTFPlayer::PrecacheMvM()
  2242. {
  2243. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  2244. {
  2245. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szBotModels ) == TF_LAST_NORMAL_CLASS );
  2246. int iModelIndex = PrecacheModel( g_szBotModels[ i ] );
  2247. PrecacheGibsForModel( iModelIndex );
  2248. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szBotBossModels ) == TF_LAST_NORMAL_CLASS );
  2249. iModelIndex = PrecacheModel( g_szBotBossModels[ i ] );
  2250. PrecacheGibsForModel( iModelIndex );
  2251. }
  2252. int iModelIndex = PrecacheModel( g_szBotBossSentryBusterModel );
  2253. PrecacheGibsForModel( iModelIndex );
  2254. PrecacheModel( "models/items/currencypack_small.mdl" );
  2255. PrecacheModel( "models/items/currencypack_medium.mdl" );
  2256. PrecacheModel( "models/items/currencypack_large.mdl" );
  2257. PrecacheModel( "models/bots/tw2/boss_bot/twcarrier_addon.mdl" );
  2258. PrecacheParticleSystem( "bot_impact_light" );
  2259. PrecacheParticleSystem( "bot_impact_heavy" );
  2260. PrecacheParticleSystem( "bot_death" );
  2261. PrecacheParticleSystem( "bot_radio_waves" );
  2262. PrecacheScriptSound( "MVM.BotStep" );
  2263. PrecacheScriptSound( "MVM.GiantHeavyStep" );
  2264. PrecacheScriptSound( "MVM.GiantSoldierStep" );
  2265. PrecacheScriptSound( "MVM.GiantDemomanStep" );
  2266. PrecacheScriptSound( "MVM.GiantScoutStep" );
  2267. PrecacheScriptSound( "MVM.GiantPyroStep" );
  2268. PrecacheScriptSound( "MVM.GiantHeavyLoop" );
  2269. PrecacheScriptSound( "MVM.GiantSoldierLoop" );
  2270. PrecacheScriptSound( "MVM.GiantDemomanLoop" );
  2271. PrecacheScriptSound( "MVM.GiantScoutLoop" );
  2272. PrecacheScriptSound( "MVM.GiantPyroLoop" );
  2273. PrecacheScriptSound( "MVM.GiantHeavyExplodes" );
  2274. PrecacheScriptSound( "MVM.GiantCommonExplodes" );
  2275. PrecacheScriptSound( "MVM.SentryBusterExplode" );
  2276. PrecacheScriptSound( "MVM.SentryBusterLoop" );
  2277. PrecacheScriptSound( "MVM.SentryBusterIntro" );
  2278. PrecacheScriptSound( "MVM.SentryBusterStep" );
  2279. PrecacheScriptSound( "MVM.SentryBusterSpin" );
  2280. PrecacheScriptSound( "MVM.DeployBombSmall" );
  2281. PrecacheScriptSound( "MVM.DeployBombGiant" );
  2282. PrecacheScriptSound( "Weapon_Upgrade.ExplosiveHeadshot" );
  2283. PrecacheScriptSound( "Spy.MVM_Chuckle" );
  2284. PrecacheScriptSound( "MVM.Robot_Engineer_Spawn" );
  2285. PrecacheScriptSound( "MVM.Robot_Teleporter_Deliver" );
  2286. PrecacheScriptSound( "MVM.MoneyPickup" );
  2287. PrecacheMaterial( "effects/circle_nocull" );
  2288. }
  2289. //-----------------------------------------------------------------------------
  2290. // Purpose:
  2291. //-----------------------------------------------------------------------------
  2292. void CTFPlayer::PrecacheKart()
  2293. {
  2294. PrecacheModel( "models/player/items/taunts/bumpercar/parts/bumpercar.mdl" );
  2295. PrecacheModel( "models/props_halloween/bumpercar_cage.mdl" );
  2296. PrecacheScriptSound( "BumperCar.Spawn" );
  2297. PrecacheScriptSound( "BumperCar.SpawnFromLava" );
  2298. PrecacheScriptSound( "BumperCar.GoLoop" );
  2299. PrecacheScriptSound( "BumperCar.Screech" );
  2300. PrecacheScriptSound( "BumperCar.HitGhost" );
  2301. PrecacheScriptSound( "BumperCar.Bump" );
  2302. PrecacheScriptSound( "BumperCar.BumpHard" );
  2303. PrecacheScriptSound( "BumperCar.BumpIntoAir" );
  2304. PrecacheScriptSound( "BumperCar.SpeedBoostStart" );
  2305. PrecacheScriptSound( "BumperCar.SpeedBoostStop" );
  2306. PrecacheScriptSound( "BumperCar.Jump" );
  2307. PrecacheScriptSound( "BumperCar.JumpLand" );
  2308. PrecacheScriptSound( "sf14.Merasmus.DuckHunt.BonusDucks" );
  2309. PrecacheParticleSystem( "kartimpacttrail" );
  2310. PrecacheParticleSystem( "kart_dust_trail_red" );
  2311. PrecacheParticleSystem( "kart_dust_trail_blue" );
  2312. PrecacheParticleSystem( "kartdamage_4");
  2313. }
  2314. //-----------------------------------------------------------------------------
  2315. // Purpose: Precache the player models and player model gibs.
  2316. //-----------------------------------------------------------------------------
  2317. void CTFPlayer::PrecachePlayerModels( void )
  2318. {
  2319. int i;
  2320. for ( i = 0; i < TF_CLASS_COUNT_ALL; i++ )
  2321. {
  2322. const char *pszModel = GetPlayerClassData( i )->m_szModelName;
  2323. if ( pszModel && pszModel[0] )
  2324. {
  2325. int iModel = PrecacheModel( pszModel );
  2326. PrecacheGibsForModel( iModel );
  2327. }
  2328. pszModel = GetPlayerClassData( i )->m_szHandModelName;
  2329. if ( pszModel && pszModel[0] )
  2330. {
  2331. PrecacheModel( pszModel );
  2332. }
  2333. /*
  2334. if ( !IsX360() )
  2335. {
  2336. // Precache the hardware facial morphed models as well.
  2337. const char *pszHWMModel = GetPlayerClassData( i )->m_szHWMModelName;
  2338. if ( pszHWMModel && pszHWMModel[0] )
  2339. {
  2340. PrecacheModel( pszHWMModel );
  2341. }
  2342. }
  2343. */
  2344. }
  2345. // Always precache the silly gibs.
  2346. for ( i = 4; i < ARRAYSIZE( g_pszBDayGibs ); ++i )
  2347. {
  2348. PrecacheModel( g_pszBDayGibs[i] );
  2349. }
  2350. if ( TFGameRules() && TFGameRules()->IsBirthday() )
  2351. {
  2352. for ( i = 0; i < 4/*ARRAYSIZE(g_pszBDayGibs)*/; i++ )
  2353. {
  2354. PrecacheModel( g_pszBDayGibs[i] );
  2355. }
  2356. PrecacheModel( "models/effects/bday_hat.mdl" );
  2357. }
  2358. if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
  2359. {
  2360. PrecacheModel( "models/props_halloween/halloween_gift.mdl" );
  2361. PrecacheModel( "models/props_halloween/ghost_no_hat.mdl" );
  2362. PrecacheModel( "models/props_halloween/ghost_no_hat_red.mdl" );
  2363. }
  2364. // Precache player class sounds
  2365. for ( i = TF_FIRST_NORMAL_CLASS; i < TF_CLASS_COUNT_ALL; ++i )
  2366. {
  2367. TFPlayerClassData_t *pData = GetPlayerClassData( i );
  2368. for ( int i = 0; i < ARRAYSIZE( pData->m_szDeathSound ); ++i )
  2369. {
  2370. PrecacheScriptSound( pData->m_szDeathSound[ i ] );
  2371. }
  2372. }
  2373. #ifdef STAGING_ONLY
  2374. PrecacheModel( "models/weapons/c_models/c_bread/c_bread_plainloaf.mdl" );
  2375. #endif
  2376. COMPILE_TIME_ASSERT( TF_CALLING_CARD_MODEL_COUNT == ARRAYSIZE( g_pszDeathCallingCardModels ) );
  2377. // Precache, Deliberatly skipping zero
  2378. for ( i = 1; i < TF_CALLING_CARD_MODEL_COUNT; i++ )
  2379. {
  2380. PrecacheModel( g_pszDeathCallingCardModels[i] );
  2381. }
  2382. #ifdef STAGING_ONLY
  2383. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szPlayerRobotModels ) == TF_LAST_NORMAL_CLASS );
  2384. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  2385. {
  2386. PrecacheModel( g_szPlayerRobotModels[i] );
  2387. //int iModelIndex = PrecacheModel( g_szPlayerRobotModels[i] );
  2388. //PrecacheGibsForModel( iModelIndex );
  2389. }
  2390. #endif // STAGING_ONLY
  2391. }
  2392. //-----------------------------------------------------------------------------
  2393. // Purpose:
  2394. //-----------------------------------------------------------------------------
  2395. void CTFPlayer::PrecacheTFPlayer()
  2396. {
  2397. VPROF_BUDGET( "CTFPlayer::PrecacheTFPlayer", VPROF_BUDGETGROUP_PLAYER );
  2398. if( !m_bTFPlayerNeedsPrecache )
  2399. return;
  2400. m_bTFPlayerNeedsPrecache = false;
  2401. // Precache the player models and gibs.
  2402. PrecachePlayerModels();
  2403. // Precache the player sounds.
  2404. PrecacheScriptSound( "Player.Spawn" );
  2405. PrecacheScriptSound( "TFPlayer.Pain" );
  2406. PrecacheScriptSound( "TFPlayer.CritHit" );
  2407. PrecacheScriptSound( "TFPlayer.CritHitMini" );
  2408. PrecacheScriptSound( "TFPlayer.DoubleDonk" );
  2409. PrecacheScriptSound( "TFPlayer.CritPain" );
  2410. PrecacheScriptSound( "TFPlayer.CritDeath" );
  2411. PrecacheScriptSound( "TFPlayer.FreezeCam" );
  2412. PrecacheScriptSound( "TFPlayer.Drown" );
  2413. PrecacheScriptSound( "TFPlayer.AttackerPain" );
  2414. PrecacheScriptSound( "TFPlayer.SaveMe" );
  2415. PrecacheScriptSound( "TFPlayer.CritBoostOn" );
  2416. PrecacheScriptSound( "TFPlayer.CritBoostOff" );
  2417. PrecacheScriptSound( "TFPlayer.Decapitated" );
  2418. PrecacheScriptSound( "TFPlayer.ReCharged" );
  2419. PrecacheScriptSound( "Camera.SnapShot" );
  2420. PrecacheScriptSound( "TFPlayer.Dissolve" );
  2421. PrecacheScriptSound( "Saxxy.TurnGold" );
  2422. PrecacheScriptSound( "Icicle.TurnToIce" );
  2423. PrecacheScriptSound( "Icicle.HitWorld" );
  2424. PrecacheScriptSound( "Icicle.Melt" );
  2425. PrecacheScriptSound( "DemoCharge.ChargeCritOn" );
  2426. PrecacheScriptSound( "DemoCharge.ChargeCritOff" );
  2427. PrecacheScriptSound( "DemoCharge.Charging" );
  2428. PrecacheScriptSound( "TFPlayer.StunImpactRange" );
  2429. PrecacheScriptSound( "TFPlayer.StunImpact" );
  2430. PrecacheScriptSound( "Halloween.PlayerScream" );
  2431. PrecacheScriptSound( "Halloween.PlayerEscapedUnderworld" );
  2432. PrecacheScriptSound( "Game.YourTeamLost" );
  2433. PrecacheScriptSound( "Game.YourTeamWon" );
  2434. PrecacheScriptSound( "Game.SuddenDeath" );
  2435. PrecacheScriptSound( "Game.Stalemate" );
  2436. PrecacheScriptSound( "TV.Tune" );
  2437. //This will be moved out once we do the announcer pass.
  2438. PrecacheScriptSound( "Announcer.AM_FirstBloodRandom" );
  2439. PrecacheScriptSound( "Announcer.AM_CapEnabledRandom" );
  2440. PrecacheScriptSound( "Announcer.AM_RoundStartRandom" );
  2441. PrecacheScriptSound( "Announcer.AM_FirstBloodFast" );
  2442. PrecacheScriptSound( "Announcer.AM_FirstBloodFinally" );
  2443. PrecacheScriptSound( "Announcer.AM_FlawlessVictoryRandom" );
  2444. PrecacheScriptSound( "Announcer.AM_FlawlessDefeatRandom" );
  2445. PrecacheScriptSound( "Announcer.AM_FlawlessVictory01" );
  2446. PrecacheScriptSound( "Announcer.AM_TeamScrambleRandom" );
  2447. PrecacheScriptSound( "Taunt.MedicHeroic" );
  2448. PrecacheScriptSound( "Taunt.GuitarRiff" );
  2449. // Dmg absorb sound
  2450. PrecacheScriptSound( "Powerup.ReducedDamage" );
  2451. // Tourney UI
  2452. PrecacheScriptSound( "Tournament.PlayerReady" );
  2453. PrecacheScriptSound( "Medic.AutoCallerAnnounce" );
  2454. #ifdef STAGING_ONLY
  2455. // Killstreak sounds
  2456. PrecacheScriptSound( "Announcer.KillStreak_Level1" );
  2457. PrecacheScriptSound( "Announcer.KillStreak_Level2" );
  2458. PrecacheScriptSound( "Announcer.KillStreak_Level3" );
  2459. PrecacheScriptSound( "Announcer.KillStreak_Level4" );
  2460. PrecacheScriptSound( "Game.KillStreak" );
  2461. #endif
  2462. // Precache particle systems
  2463. PrecacheParticleSystem( "crit_text" );
  2464. PrecacheParticleSystem( "miss_text" );
  2465. PrecacheParticleSystem( "cig_smoke" );
  2466. PrecacheParticleSystem( "speech_mediccall" );
  2467. PrecacheParticleSystem( "speech_mediccall_auto" );
  2468. PrecacheParticleSystem( "speech_taunt_all" );
  2469. PrecacheParticleSystem( "speech_taunt_red" );
  2470. PrecacheParticleSystem( "speech_taunt_blue" );
  2471. PrecacheParticleSystem( "player_recent_teleport_blue" );
  2472. PrecacheParticleSystem( "player_recent_teleport_red" );
  2473. PrecacheParticleSystem( "particle_nemesis_red" );
  2474. PrecacheParticleSystem( "particle_nemesis_blue" );
  2475. PrecacheParticleSystem( "spy_start_disguise_red" );
  2476. PrecacheParticleSystem( "spy_start_disguise_blue" );
  2477. PrecacheParticleSystem( "burningplayer_red" );
  2478. PrecacheParticleSystem( "burningplayer_blue" );
  2479. PrecacheParticleSystem( "burningplayer_rainbow" );
  2480. PrecacheParticleSystem( "blood_spray_red_01" );
  2481. PrecacheParticleSystem( "blood_spray_red_01_far" );
  2482. PrecacheParticleSystem( "pyrovision_blood" );
  2483. PrecacheParticleSystem( "water_blood_impact_red_01" );
  2484. PrecacheParticleSystem( "blood_impact_red_01" );
  2485. PrecacheParticleSystem( "water_playerdive" );
  2486. PrecacheParticleSystem( "water_playeremerge" );
  2487. PrecacheParticleSystem( "healthgained_red" );
  2488. PrecacheParticleSystem( "healthgained_blu" );
  2489. PrecacheParticleSystem( "healthgained_red_large" );
  2490. PrecacheParticleSystem( "healthgained_blu_large" );
  2491. PrecacheParticleSystem( "healthgained_red_giant" );
  2492. PrecacheParticleSystem( "healthgained_blu_giant" );
  2493. PrecacheParticleSystem( "critgun_weaponmodel_red" );
  2494. PrecacheParticleSystem( "critgun_weaponmodel_blu" );
  2495. PrecacheParticleSystem( "overhealedplayer_red_pluses" );
  2496. PrecacheParticleSystem( "overhealedplayer_blue_pluses" );
  2497. PrecacheParticleSystem( "highfive_red" );
  2498. PrecacheParticleSystem( "highfive_blue" );
  2499. PrecacheParticleSystem( "god_rays" );
  2500. PrecacheParticleSystem( "bl_killtaunt" );
  2501. PrecacheParticleSystem( "birthday_player_circling" );
  2502. PrecacheParticleSystem( "drg_fiery_death" );
  2503. PrecacheParticleSystem( "drg_wrenchmotron_teleport" );
  2504. PrecacheParticleSystem( "taunt_flip_land_red" );
  2505. PrecacheParticleSystem( "taunt_flip_land_blue" );
  2506. PrecacheParticleSystem( "tfc_sniper_mist" );
  2507. PrecacheParticleSystem( "dxhr_sniper_rail_blue" );
  2508. PrecacheParticleSystem( "dxhr_sniper_rail_red" );
  2509. PrecacheParticleSystem( "tfc_sniper_distortion_trail" );
  2510. for ( int i=0; i<ARRAYSIZE( s_pszTauntRPSParticleNames ); ++i )
  2511. {
  2512. PrecacheParticleSystem( s_pszTauntRPSParticleNames[i] );
  2513. }
  2514. PrecacheParticleSystem( "blood_decap" );
  2515. PrecacheParticleSystem( "xms_icicle_idle" );
  2516. PrecacheParticleSystem( "xms_icicle_impact" );
  2517. PrecacheParticleSystem( "xms_icicle_impact_dryice" );
  2518. PrecacheParticleSystem( "xms_icicle_melt" );
  2519. PrecacheParticleSystem( "xms_ornament_glitter" );
  2520. PrecacheParticleSystem( "xms_ornament_smash_blue" );
  2521. PrecacheParticleSystem( "xms_ornament_smash_red" );
  2522. PrecacheParticleSystem( "drg_pomson_muzzleflash" );
  2523. PrecacheParticleSystem( "drg_pomson_impact" );
  2524. PrecacheParticleSystem( "drg_pomson_impact_drain" );
  2525. PrecacheParticleSystem( "dxhr_arm_muzzleflash" );
  2526. PrecacheModel( "effects/beam001_red.vmt" );
  2527. PrecacheModel( "effects/beam001_blu.vmt" );
  2528. PrecacheModel( "effects/beam001_white.vmt" );
  2529. PrecacheScriptSound( "Weapon_Mantreads.Impact" );
  2530. // Precache footstep override sounds.
  2531. PrecacheScriptSound( "cleats_conc.StepLeft" );
  2532. PrecacheScriptSound( "cleats_conc.StepRight" );
  2533. PrecacheScriptSound( "cleats_dirt.StepLeft" );
  2534. PrecacheScriptSound( "cleats_dirt.StepRight" );
  2535. PrecacheScriptSound( "xmas.jingle" );
  2536. PrecacheScriptSound( "xmas.jingle_higher" );
  2537. PrecacheScriptSound( "PegLeg.StepRight" );
  2538. // Halloween
  2539. // Bombinomicon deaths
  2540. PrecacheParticleSystem( "bombinomicon_burningdebris" );
  2541. PrecacheParticleSystem( "bombinomicon_burningdebris_halloween" );
  2542. PrecacheParticleSystem( "halloween_player_death_blue" );
  2543. PrecacheParticleSystem( "halloween_player_death" );
  2544. PrecacheScriptSound( "Bombinomicon.Explode" );
  2545. PrecacheScriptSound( "Weapon_DRG_Wrench.Teleport" );
  2546. PrecacheScriptSound( "Weapon_Pomson.Single" );
  2547. PrecacheScriptSound( "Weapon_Pomson.SingleCrit" );
  2548. PrecacheScriptSound( "Weapon_Pomson.Reload" );
  2549. PrecacheScriptSound( "Weapon_Pomson.DrainedVictim" );
  2550. PrecacheScriptSound( "BlastJump.Whistle" );
  2551. PrecacheScriptSound( "Spy.TeaseVictim" );
  2552. PrecacheScriptSound( "Demoman.CritDeath" );
  2553. PrecacheScriptSound( "Heavy.Battlecry03" );
  2554. PrecacheModel( "models/effects/resist_shield/resist_shield.mdl" );
  2555. PrecacheModel( "models/props_mvm/mvm_revive_tombstone.mdl" );
  2556. PrecacheScriptSound( "General.banana_slip" ); // Used for SodaPopper Hype Jumps
  2557. #ifdef STAGING_ONLY
  2558. PrecacheScriptSound( "RD.SpaceGravityTransition" );
  2559. #endif // STAGING_ONLY
  2560. PrecacheScriptSound( "Parachute_open" );
  2561. PrecacheScriptSound( "Parachute_close" );
  2562. #ifdef STAGING_ONLY
  2563. PrecacheScriptSound( "WeaponDNAGun.Transform" );
  2564. PrecacheParticleSystem( "spy_stolen_smoke_blue" );
  2565. PrecacheParticleSystem( "spy_stolen_smoke_red" );
  2566. #endif // STAGING_ONLY
  2567. // precache the EOTL bomb cart replacements
  2568. PrecacheModel( "models/props_trainyard/bomb_eotl_blue.mdl" );
  2569. PrecacheModel( "models/props_trainyard/bomb_eotl_red.mdl" );
  2570. }
  2571. //-----------------------------------------------------------------------------
  2572. // Purpose:
  2573. //-----------------------------------------------------------------------------
  2574. void CTFPlayer::Precache()
  2575. {
  2576. VPROF_BUDGET( "CTFPlayer::Precache", VPROF_BUDGETGROUP_PLAYER );
  2577. /*
  2578. Note: All TFPlayer specific must go inside PrecacheTFPlayer()
  2579. This assumes that we're loading all resources for any TF player class
  2580. The reason is to safe performance because tons of string compares is very EXPENSIVE!!!
  2581. The most offending function is PrecacheGibsForModel which re-parsing through KeyValues every time it's called
  2582. If you have any question, come talk to me (Bank)
  2583. */
  2584. PrecacheTFPlayer();
  2585. BaseClass::Precache();
  2586. }
  2587. //-----------------------------------------------------------------------------
  2588. // Purpose: Allow pre-frame adjustments on the player
  2589. //-----------------------------------------------------------------------------
  2590. ConVar sv_runcmds( "sv_runcmds", "1" );
  2591. void CTFPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper )
  2592. {
  2593. static bool bSeenSyncError = false;
  2594. VPROF( "CTFPlayer::PlayerRunCommand" );
  2595. if ( !sv_runcmds.GetInt() )
  2596. return;
  2597. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  2598. {
  2599. m_Shared.CreateVehicleMove( gpGlobals->frametime, ucmd );
  2600. }
  2601. else if ( IsTaunting() || m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
  2602. {
  2603. // For some taunts, it is critical that the player not move once they start
  2604. if ( !CanMoveDuringTaunt() )
  2605. {
  2606. ucmd->forwardmove = 0;
  2607. ucmd->upmove = 0;
  2608. ucmd->sidemove = 0;
  2609. ucmd->viewangles = pl.v_angle;
  2610. }
  2611. if ( tf_allow_taunt_switch.GetInt() == 0 && ucmd->weaponselect != 0 )
  2612. {
  2613. ucmd->weaponselect = 0;
  2614. // FIXME: The client will have predicted the weapon switch and have
  2615. // called Holster/Deploy which will make the wielded weapon
  2616. // invisible on their end.
  2617. }
  2618. }
  2619. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  2620. // try to play taunt remap on input after updating user command
  2621. if ( IsTaunting() && m_flNextAllowTauntRemapInputTime >= 0.f && m_flNextAllowTauntRemapInputTime <= gpGlobals->curtime )
  2622. {
  2623. float flSceneDuration = PlayTauntRemapInputScene();
  2624. if ( flSceneDuration > 0.f )
  2625. {
  2626. m_flNextAllowTauntRemapInputTime = gpGlobals->curtime + flSceneDuration;
  2627. }
  2628. }
  2629. }
  2630. //-----------------------------------------------------------------------------
  2631. // Purpose:
  2632. //-----------------------------------------------------------------------------
  2633. bool CTFPlayer::IsReadyToPlay( void )
  2634. {
  2635. bool bRetVal = false;
  2636. if ( GetTeamNumber() == TEAM_SPECTATOR && m_bArenaSpectator == true )
  2637. return false;
  2638. //=============================================================================
  2639. // HPE_BEGIN:
  2640. // [msmith] We don't want to say that the player is ready if they're still
  2641. // a training video.
  2642. //=============================================================================
  2643. if ( TFGameRules() && TFGameRules()->IsInTraining() && tf_training_client_message.GetInt() == TRAINING_CLIENT_MESSAGE_WATCHING_INTRO_MOVIE )
  2644. return false;
  2645. //=============================================================================
  2646. // HPE_END
  2647. //=============================================================================
  2648. if ( GetTeamNumber() > LAST_SHARED_TEAM )
  2649. {
  2650. if ( GetDesiredPlayerClassIndex() > TF_CLASS_UNDEFINED )
  2651. {
  2652. bRetVal = true;
  2653. }
  2654. else
  2655. {
  2656. if ( TFGameRules() && TFGameRules()->IsInArenaMode() && tf_arena_force_class.GetBool() == true && tf_arena_use_queue.GetBool() == true )
  2657. {
  2658. bRetVal = true;
  2659. }
  2660. }
  2661. }
  2662. return bRetVal;
  2663. }
  2664. //-----------------------------------------------------------------------------
  2665. // Purpose:
  2666. //-----------------------------------------------------------------------------
  2667. bool CTFPlayer::IsReadyToSpawn( void )
  2668. {
  2669. if ( IsClassMenuOpen() )
  2670. {
  2671. return false;
  2672. }
  2673. #ifdef TF_RAID_MODE
  2674. if ( GetTeamNumber() == TF_TEAM_RED )
  2675. {
  2676. if ( TFGameRules()->IsRaidMode() || TFGameRules()->IsBossBattleMode() )
  2677. {
  2678. // enemy bots never respawn - they are spawned by the population system
  2679. return false;
  2680. }
  2681. }
  2682. #endif // TF_RAID_MODE
  2683. // Medic attached to marker - delay
  2684. if ( m_hReviveMarker && m_hReviveMarker->IsReviveInProgress() && ( StateGet() != TF_STATE_DYING ) )
  2685. {
  2686. return false;
  2687. }
  2688. // Map-makers can force players to have custom respawn times
  2689. if ( GetRespawnTimeOverride() != -1.f && gpGlobals->curtime < GetDeathTime() + GetRespawnTimeOverride() )
  2690. return false;
  2691. return ( StateGet() != TF_STATE_DYING );
  2692. }
  2693. //-----------------------------------------------------------------------------
  2694. // Purpose: Return true if this player should be allowed to instantly spawn
  2695. // when they next finish picking a class.
  2696. //-----------------------------------------------------------------------------
  2697. bool CTFPlayer::ShouldGainInstantSpawn( void )
  2698. {
  2699. return ( GetPlayerClass()->GetClassIndex() == TF_CLASS_UNDEFINED || IsClassMenuOpen() );
  2700. }
  2701. //-----------------------------------------------------------------------------
  2702. // Purpose: Resets player scores
  2703. //-----------------------------------------------------------------------------
  2704. void CTFPlayer::ResetScores( void )
  2705. {
  2706. m_Shared.ResetScores();
  2707. CTF_GameStats.ResetPlayerStats( this );
  2708. RemoveNemesisRelationships();
  2709. MannVsMachineStats_ResetPlayerEvents( this );
  2710. BaseClass::ResetScores();
  2711. }
  2712. //-----------------------------------------------------------------------------
  2713. // Purpose:
  2714. //-----------------------------------------------------------------------------
  2715. void CTFPlayer::InitialSpawn( void )
  2716. {
  2717. BaseClass::InitialSpawn();
  2718. m_AttributeManager.InitializeAttributes( this );
  2719. m_AttributeManager.SetPlayer( this );
  2720. m_AttributeList.SetManager( &m_AttributeManager );
  2721. SetWeaponBuilder( NULL );
  2722. ResetScores();
  2723. StateEnter( TF_STATE_WELCOME );
  2724. UpdateInventory( true );
  2725. ResetAccumulatedSentryGunDamageDealt();
  2726. ResetAccumulatedSentryGunKillCount();
  2727. ResetDamagePerSecond();
  2728. IGameEvent * event = gameeventmanager->CreateEvent( "player_initial_spawn" );
  2729. if ( event )
  2730. {
  2731. event->SetInt( "index", entindex() );
  2732. gameeventmanager->FireEvent( event );
  2733. }
  2734. }
  2735. //-----------------------------------------------------------------------------
  2736. // Purpose: Request this player's inventories from the steam backend
  2737. //-----------------------------------------------------------------------------
  2738. void CTFPlayer::UpdateOnRemove( void )
  2739. {
  2740. BaseClass::UpdateOnRemove();
  2741. #if !defined(NO_STEAM)
  2742. m_Inventory.RemoveListener( this );
  2743. #endif
  2744. }
  2745. //-----------------------------------------------------------------------------
  2746. // Purpose: Override Base ApplyAbsVelocityImpulse (BaseEntity) to apply potential item attributes
  2747. //-----------------------------------------------------------------------------
  2748. void CTFPlayer::ApplyAbsVelocityImpulse( const Vector &vecImpulse )
  2749. {
  2750. // Check for Attributes (mult_aiming_knockback_resistance)
  2751. Vector vecForce = vecImpulse;
  2752. float flImpulseScale = 1.0f;
  2753. if ( IsPlayerClass( TF_CLASS_SNIPER ) && m_Shared.InCond( TF_COND_AIMING ) )
  2754. {
  2755. CALL_ATTRIB_HOOK_FLOAT( flImpulseScale, mult_aiming_knockback_resistance );
  2756. }
  2757. if ( m_Shared.InCond( TF_COND_HALLOWEEN_TINY ) && !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  2758. {
  2759. flImpulseScale *= 2.f;
  2760. }
  2761. // take extra force if you have a parachute deployed in x-y directions
  2762. if ( m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
  2763. {
  2764. // don't allow parachute robot to get push in MvM
  2765. float flHorizontalScale = TFGameRules()->IsMannVsMachineMode() && IsBot() ? 0.f : 1.5f;
  2766. vecForce.x *= flHorizontalScale;
  2767. vecForce.y *= flHorizontalScale;
  2768. }
  2769. CBaseMultiplayerPlayer::ApplyAbsVelocityImpulse( vecForce * flImpulseScale );
  2770. }
  2771. //-----------------------------------------------------------------------------
  2772. // Purpose:
  2773. //-----------------------------------------------------------------------------
  2774. void CTFPlayer::ApplyAirBlastImpulse( const Vector &vecImpulse )
  2775. {
  2776. // Knockout powerup carriers are immune to airblast
  2777. if ( m_Shared.GetCarryingRuneType() == RUNE_KNOCKOUT || m_Shared.InCond( TF_COND_MEGAHEAL ) )
  2778. return;
  2779. Vector vForce = vecImpulse;
  2780. float flScale = 1.0f;
  2781. CALL_ATTRIB_HOOK_FLOAT( flScale, airblast_vulnerability_multiplier );
  2782. vForce *= flScale;
  2783. // if on the ground, require min force to boost you off it
  2784. if ( ( GetFlags() & FL_ONGROUND ) && ( vForce.z < JUMP_MIN_SPEED ) )
  2785. {
  2786. // Minimum value of vecForce.z
  2787. vForce.z = JUMP_MIN_SPEED;
  2788. }
  2789. CALL_ATTRIB_HOOK_FLOAT( vForce.z, airblast_vertical_vulnerability_multiplier );
  2790. RemoveFlag( FL_ONGROUND );
  2791. m_Shared.AddCond( TF_COND_KNOCKED_INTO_AIR );
  2792. ApplyAbsVelocityImpulse( vForce );
  2793. }
  2794. //-----------------------------------------------------------------------------
  2795. // Purpose: Go between for Setting Local Punch Impulses. Checks item attributes
  2796. // Use this instead of directly calling m_Local.m_vecPunchAngle.SetX( value );
  2797. //-----------------------------------------------------------------------------
  2798. bool CTFPlayer::ApplyPunchImpulseX ( float flImpulse )
  2799. {
  2800. // Check for No Aim Flinch
  2801. bool bFlinch = true;
  2802. if ( IsPlayerClass( TF_CLASS_SNIPER ) && m_Shared.InCond( TF_COND_AIMING ) )
  2803. {
  2804. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  2805. if ( pWeapon && WeaponID_IsSniperRifle( pWeapon->GetWeaponID() ) )
  2806. {
  2807. CTFSniperRifle *pRifle = static_cast< CTFSniperRifle* >( pWeapon );
  2808. if ( pRifle->IsFullyCharged() )
  2809. {
  2810. int iAimingNoFlinch = 0;
  2811. CALL_ATTRIB_HOOK_INT( iAimingNoFlinch, aiming_no_flinch );
  2812. if ( iAimingNoFlinch > 0 )
  2813. {
  2814. bFlinch = false;
  2815. }
  2816. }
  2817. }
  2818. }
  2819. if ( bFlinch )
  2820. {
  2821. m_Local.m_vecPunchAngle.SetX( flImpulse );
  2822. }
  2823. return bFlinch;
  2824. }
  2825. //-----------------------------------------------------------------------------
  2826. // Purpose: Request this player's inventories from the steam backend
  2827. //-----------------------------------------------------------------------------
  2828. void CTFPlayer::UpdateInventory( bool bInit )
  2829. {
  2830. #if !defined(NO_STEAM)
  2831. if ( IsFakeClient() )
  2832. return;
  2833. if ( bInit || !m_Inventory.GetSOC() )
  2834. {
  2835. if ( steamgameserverapicontext->SteamGameServer() )
  2836. {
  2837. CSteamID steamIDForPlayer;
  2838. if ( GetSteamID( &steamIDForPlayer ) )
  2839. {
  2840. TFInventoryManager()->SteamRequestInventory( &m_Inventory, steamIDForPlayer, this );
  2841. }
  2842. }
  2843. }
  2844. // If we have an SOCache, we've got a connection to the GC
  2845. bool bInvalid = true;
  2846. if ( m_Inventory.GetSOC() )
  2847. {
  2848. bInvalid = m_Inventory.GetSOC()->BIsInitialized() == false;
  2849. }
  2850. m_Shared.SetLoadoutUnavailable( bInvalid );
  2851. #endif
  2852. }
  2853. //-----------------------------------------------------------------------------
  2854. // Purpose: Requests that the GC confirm that this player is supposed to have
  2855. // an SO cache on this gameserver and send it again if so.
  2856. //-----------------------------------------------------------------------------
  2857. void CTFPlayer::VerifySOCache()
  2858. {
  2859. #if !defined(NO_STEAM)
  2860. if ( IsFakeClient() || IsHLTV() || IsReplay() )
  2861. return;
  2862. CSteamID steamIDForPlayer;
  2863. GetSteamID( &steamIDForPlayer );
  2864. if( steamIDForPlayer.BIndividualAccount() )
  2865. {
  2866. // if we didn't find an inventory ask the GC to refresh us
  2867. GCSDK::CGCMsg<MsgGCVerifyCacheSubscription_t> msgVerifyCache( k_EMsgGCVerifyCacheSubscription );
  2868. msgVerifyCache.Body().m_ulSteamID = steamIDForPlayer.ConvertToUint64();
  2869. GCClientSystem()->BSendMessage( msgVerifyCache );
  2870. }
  2871. else
  2872. {
  2873. Msg( "Cannot verify load for invalid steam ID %s\n", steamIDForPlayer.Render() );
  2874. }
  2875. #endif
  2876. }
  2877. #ifdef DEBUG
  2878. CON_COMMAND_F( verifyloadout, "Cause the server to verify the player's items on the server.", FCVAR_NONE )
  2879. {
  2880. CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
  2881. if ( !pPlayer )
  2882. return;
  2883. pPlayer->VerifySOCache();
  2884. }
  2885. #endif // DEBUG
  2886. //-----------------------------------------------------------------------------
  2887. // Purpose:
  2888. //-----------------------------------------------------------------------------
  2889. int CTFPlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  2890. {
  2891. // always send information to student or client
  2892. if ( pInfo->m_pClientEnt )
  2893. {
  2894. if ( m_hStudent && m_hStudent == CBaseEntity::Instance( pInfo->m_pClientEnt ) )
  2895. {
  2896. return FL_EDICT_ALWAYS;
  2897. }
  2898. else if ( m_hCoach && m_hCoach == CBaseEntity::Instance( pInfo->m_pClientEnt ) )
  2899. {
  2900. return FL_EDICT_ALWAYS;
  2901. }
  2902. else if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  2903. {
  2904. // TODO it should be possible to restrict this further based on
  2905. // the values of tf_passtime_player_reticles_friends/enemies
  2906. return FL_EDICT_ALWAYS;
  2907. }
  2908. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  2909. if ( pRecipientEntity && pRecipientEntity->ShouldForceTransmitsForTeam( GetTeamNumber() ) )
  2910. return FL_EDICT_ALWAYS;
  2911. }
  2912. return BaseClass::ShouldTransmit( pInfo );
  2913. }
  2914. //-----------------------------------------------------------------------------
  2915. // Purpose:
  2916. //-----------------------------------------------------------------------------
  2917. void CTFPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  2918. {
  2919. // coach can only "see" what the student "sees"
  2920. if ( m_bIsCoaching && m_hStudent )
  2921. {
  2922. Vector org;
  2923. org = m_hStudent->EyePosition();
  2924. engine->AddOriginToPVS( org );
  2925. }
  2926. else
  2927. {
  2928. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  2929. }
  2930. }
  2931. //-----------------------------------------------------------------------------
  2932. // Purpose:
  2933. //-----------------------------------------------------------------------------
  2934. void CTFPlayer::Spawn()
  2935. {
  2936. VPROF_BUDGET( "CTFPlayer::Spawn", VPROF_BUDGETGROUP_PLAYER );
  2937. MDLCACHE_CRITICAL_SECTION();
  2938. m_bIsABot = IsBot();
  2939. if ( m_bIsABot && IsBotOfType( TF_BOT_TYPE ) )
  2940. {
  2941. m_nBotSkill = ToTFBot( this )->GetDifficulty();
  2942. }
  2943. else
  2944. {
  2945. m_nBotSkill = 0;
  2946. }
  2947. m_flSpawnTime = gpGlobals->curtime;
  2948. SetModelScale( 1.0f );
  2949. UpdateModel();
  2950. SetMoveType( MOVETYPE_WALK );
  2951. BaseClass::Spawn();
  2952. // We have to clear this early, so that the sword knows its max health in ManageRegularWeapons below
  2953. m_Shared.SetDecapitations( 0 );
  2954. // Check the make sure we have our inventory each time we spawn
  2955. UpdateInventory( false );
  2956. #ifndef NO_STEAM
  2957. if( m_Shared.IsLoadoutUnavailable() )
  2958. {
  2959. VerifySOCache();
  2960. }
  2961. #endif
  2962. // Create our off hand viewmodel if necessary
  2963. CreateViewModel( 1 );
  2964. // Make sure it has no model set, in case it had one before
  2965. GetViewModel(1)->SetModel( "" );
  2966. // Kind of lame, but CBasePlayer::Spawn resets a lot of the state that we initially want on.
  2967. // So if we're in the welcome state, call its enter function to reset
  2968. if ( m_Shared.InState( TF_STATE_WELCOME ) )
  2969. {
  2970. StateEnterWELCOME();
  2971. }
  2972. // If they were dead, then they're respawning. Put them in the active state.
  2973. if ( m_Shared.InState( TF_STATE_DYING ) )
  2974. {
  2975. StateTransition( TF_STATE_ACTIVE );
  2976. }
  2977. // If they're spawning into the world as fresh meat, give them items and stuff.
  2978. bool bMatchSummary = TFGameRules() && TFGameRules()->ShowMatchSummary();
  2979. if ( m_Shared.InState( TF_STATE_ACTIVE ) || bMatchSummary )
  2980. {
  2981. // remove our disguise each time we spawn
  2982. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  2983. {
  2984. m_Shared.RemoveDisguise();
  2985. }
  2986. if ( !bMatchSummary )
  2987. {
  2988. EmitSound( "Player.Spawn" );
  2989. }
  2990. InitClass();
  2991. m_Shared.RemoveAllCond(); // Remove conc'd, burning, rotting, hallucinating, etc.
  2992. // add team glows for a period of time after we respawn
  2993. m_Shared.AddCond( TF_COND_TEAM_GLOWS, tf_spawn_glows_duration.GetInt() );
  2994. UpdateSkin( GetTeamNumber() );
  2995. // Prevent firing for a second so players don't blow their faces off
  2996. SetNextAttack( gpGlobals->curtime + 1.0 );
  2997. DoAnimationEvent( PLAYERANIMEVENT_SPAWN );
  2998. // Force a taunt off, if we are still taunting, the condition should have been cleared above.
  2999. StopTaunt();
  3000. // turn on separation so players don't get stuck in each other when spawned
  3001. m_Shared.SetSeparation( true );
  3002. m_Shared.SetSeparationVelocity( vec3_origin );
  3003. RemoveTeleportEffect();
  3004. //If this is true it means I respawned without dying (changing class inside the spawn room) but doesn't necessarily mean that my healers have stopped healing me
  3005. //This means that medics can still be linked to me but my health would not be affected since this condition is not set.
  3006. //So instead of going and forcing every healer on me to stop healing we just set this condition back on.
  3007. //If the game decides I shouldn't be healed by someone (LOS, Distance, etc) they will break the link themselves like usual.
  3008. if ( m_Shared.GetNumHealers() > 0 )
  3009. {
  3010. m_Shared.AddCond( TF_COND_HEALTH_BUFF );
  3011. }
  3012. if ( !m_bSeenRoundInfo )
  3013. {
  3014. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  3015. {
  3016. CSingleUserRecipientFilter filter( this );
  3017. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_PASSTIME_HOWTO );
  3018. }
  3019. TFGameRules()->ShowRoundInfoPanel( this );
  3020. m_bSeenRoundInfo = true;
  3021. }
  3022. if ( IsInCommentaryMode() && !IsFakeClient() )
  3023. {
  3024. // Player is spawning in commentary mode. Tell the commentary system.
  3025. CBaseEntity *pEnt = NULL;
  3026. variant_t emptyVariant;
  3027. while ( (pEnt = gEntList.FindEntityByClassname( pEnt, "commentary_auto" )) != NULL )
  3028. {
  3029. pEnt->AcceptInput( "MultiplayerSpawned", this, this, emptyVariant, 0 );
  3030. }
  3031. }
  3032. }
  3033. CTF_GameStats.Event_PlayerSpawned( this );
  3034. m_iSpawnCounter = !m_iSpawnCounter;
  3035. m_bAllowInstantSpawn = false;
  3036. m_Shared.SetSpyCloakMeter( 100.0f );
  3037. m_Shared.SetScoutEnergyDrinkMeter( 100.0f );
  3038. m_Shared.SetScoutHypeMeter( 0.0f );
  3039. m_Shared.StopScoutHypeDrain();
  3040. m_Shared.SetRageMeter( 0.0f );
  3041. m_Shared.SetDemomanChargeMeter( 100.0f );
  3042. m_Shared.ClearDamageEvents();
  3043. m_AchievementData.ClearHistories();
  3044. m_flLastDamageTime = 0.f;
  3045. m_flLastDamageDoneTime = 0.f;
  3046. m_iMaxSentryKills = 0;
  3047. m_flNextVoiceCommandTime = gpGlobals->curtime;
  3048. m_iVoiceSpamCounter = 0;
  3049. ClearZoomOwner();
  3050. SetFOV( this , 0 );
  3051. SetViewOffset( GetClassEyeHeight() * GetModelScale() );
  3052. RemoveAllScenesInvolvingActor( this );
  3053. ClearExpression();
  3054. m_flNextSpeakWeaponFire = gpGlobals->curtime;
  3055. m_bInPowerPlay = false;
  3056. // This makes the surrounding box always the same size as the standing collision box
  3057. // helps with parts of the hitboxes that extend out of the crouching hitbox, eg with the
  3058. // heavyweapons guy
  3059. Vector mins = VEC_HULL_MIN;
  3060. Vector maxs = VEC_HULL_MAX;
  3061. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &mins, &maxs );
  3062. m_iLeftGroundHealth = -1;
  3063. m_iBlastJumpState = 0;
  3064. m_bGoingFeignDeath = false;
  3065. m_bTakenBlastDamageSinceLastMovement = false;
  3066. ClearTauntAttack();
  3067. m_hTauntItem = NULL;
  3068. m_bArenaIsAFK = false;
  3069. m_Shared.SetFeignDeathReady( false );
  3070. m_bScattergunJump = false;
  3071. m_iOldStunFlags = 0;
  3072. m_flAccumulatedHealthRegen = 0;
  3073. memset( m_flAccumulatedAmmoRegens, 0, sizeof(m_flAccumulatedAmmoRegens) );
  3074. IGameEvent * event = gameeventmanager->CreateEvent( "player_spawn" );
  3075. if ( event )
  3076. {
  3077. event->SetInt( "userid", GetUserID() );
  3078. event->SetInt( "team", GetTeamNumber() );
  3079. event->SetInt( "class", GetPlayerClass()->GetClassIndex() );
  3080. gameeventmanager->FireEvent( event );
  3081. }
  3082. #ifdef TF_RAID_MODE
  3083. if ( TFGameRules()->IsRaidMode() && GetTeamNumber() == TF_TEAM_BLUE )
  3084. {
  3085. // raiders respawn invulnerable for a short time
  3086. m_Shared.AddCond( TF_COND_INVULNERABLE, tf_raid_respawn_safety_time.GetFloat() );
  3087. // friends glow
  3088. AddGlowEffect();
  3089. }
  3090. if ( TFGameRules()->IsBossBattleMode() && GetTeamNumber() == TF_TEAM_BLUE )
  3091. {
  3092. // respawn invulnerable for a short time
  3093. m_Shared.AddCond( TF_COND_INVULNERABLE, tf_boss_battle_respawn_safety_time.GetFloat() );
  3094. }
  3095. #endif // TF_RAID_MODE
  3096. m_bIsMissionEnemy = false;
  3097. m_bIsSupportEnemy = false;
  3098. m_bIsLimitedSupportEnemy = false;
  3099. m_Shared.Spawn();
  3100. m_bCollideWithSentry = false;
  3101. m_calledForMedicTimer.Invalidate();
  3102. m_placedSapperTimer.Invalidate();
  3103. m_bIsReadyToHighFive = false;
  3104. m_nForceTauntCam = 0;
  3105. m_bAllowedToRemoveTaunt = true;
  3106. m_purgatoryPainMultiplier = 1;
  3107. m_purgatoryPainMultiplierTimer.Invalidate();
  3108. m_bIsTeleportingUsingEurekaEffect = false;
  3109. m_playerMovementStuckTimer.Invalidate();
  3110. m_bIsMiniBoss = false;
  3111. m_bUseBossHealthBar = false;
  3112. m_hGrapplingHookTarget = NULL;
  3113. m_nHookAttachedPlayers = 0;
  3114. m_bUsingActionSlot = false;
  3115. m_flInspectTime = 0.f;
  3116. m_flSendPickupWeaponMessageTime = -1.f;
  3117. SetRespawnOverride( -1.f, NULL_STRING );
  3118. // Remove all powerups and add temporary invuln on spawn
  3119. if ( TFGameRules()->IsPowerupMode() )
  3120. {
  3121. m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, 8.f );
  3122. }
  3123. if ( TFGameRules() )
  3124. {
  3125. // It's halloween, and it's hell time. Force this player to spawn in hell.
  3126. if ( TFGameRules()->ArePlayersInHell() )
  3127. {
  3128. const char *pSpawnEntName = NULL;
  3129. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  3130. {
  3131. pSpawnEntName = "hell_ghost_spawn";
  3132. }
  3133. else if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) && CTFMinigameLogic::GetMinigameLogic() )
  3134. {
  3135. CTFMiniGame *pActiveMinigame = CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame();
  3136. if ( pActiveMinigame )
  3137. {
  3138. pSpawnEntName = pActiveMinigame->GetTeamSpawnPointName( GetTeamNumber() );
  3139. }
  3140. }
  3141. if ( pSpawnEntName )
  3142. {
  3143. TFGameRules()->SpawnPlayerInHell( this, pSpawnEntName );
  3144. }
  3145. }
  3146. TFGameRules()->OnPlayerSpawned( this );
  3147. }
  3148. if ( m_hReviveMarker )
  3149. {
  3150. UTIL_Remove( m_hReviveMarker );
  3151. m_hReviveMarker = NULL;
  3152. }
  3153. // make sure we clear custom attributes that we added
  3154. RemoveAllCustomAttributes();
  3155. #ifdef STAGING_ONLY
  3156. // in MVM, scout can see glowing cash by default
  3157. if ( TFGameRules()->IsMannVsMachineMode() )
  3158. {
  3159. int iClass = GetPlayerClass()->GetClassIndex();
  3160. if ( iClass == TF_CLASS_SCOUT || iClass == TF_CLASS_SPY )
  3161. {
  3162. AddCustomAttribute( "mvm see cash through wall", 1.f );
  3163. }
  3164. }
  3165. #endif
  3166. CTFPlayerResource *pResource = dynamic_cast<CTFPlayerResource *>( g_pPlayerResource );
  3167. if ( pResource )
  3168. {
  3169. pResource->SetPlayerClassWhenKilled( entindex(), TF_CLASS_UNDEFINED );
  3170. }
  3171. if ( TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS )
  3172. {
  3173. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  3174. if ( pMatchDesc && pMatchDesc->m_params.m_bAutoReady )
  3175. {
  3176. TFGameRules()->PlayerReadyStatus_UpdatePlayerState( this, true );
  3177. }
  3178. }
  3179. CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
  3180. if ( pMatch )
  3181. {
  3182. CSteamID steamID;
  3183. GetSteamID( &steamID );
  3184. // This client entered a running match
  3185. CMatchInfo::PlayerMatchData_t *pMatchPlayer = pMatch->GetMatchDataForPlayer( steamID );
  3186. if ( pMatchPlayer && TFGameRules() && TFGameRules()->State_Get() == GR_STATE_RND_RUNNING )
  3187. {
  3188. pMatchPlayer->bPlayed = true;
  3189. }
  3190. }
  3191. }
  3192. //-----------------------------------------------------------------------------
  3193. // Purpose: Removes all nemesis relationships between this player and others
  3194. //-----------------------------------------------------------------------------
  3195. void CTFPlayer::RemoveNemesisRelationships()
  3196. {
  3197. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  3198. {
  3199. CTFPlayer *pTemp = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  3200. if ( pTemp && pTemp != this )
  3201. {
  3202. bool bRemove = false;
  3203. if ( TFGameRules()->IsInArenaMode() == true )
  3204. {
  3205. if ( GetTeamNumber() != TEAM_SPECTATOR )
  3206. {
  3207. if ( InSameTeam( pTemp ) == true )
  3208. {
  3209. bRemove = true;
  3210. }
  3211. }
  3212. if ( IsDisconnecting() == true )
  3213. {
  3214. bRemove = true;
  3215. }
  3216. }
  3217. else
  3218. {
  3219. bRemove = true;
  3220. }
  3221. if ( bRemove == true )
  3222. {
  3223. // set this player to be not dominating anyone else
  3224. m_Shared.SetPlayerDominated( pTemp, false );
  3225. m_iNumberofDominations = 0;
  3226. // set no one else to be dominating this player
  3227. bool bThisPlayerIsDominatingMe = m_Shared.IsPlayerDominatingMe( i );
  3228. pTemp->m_Shared.SetPlayerDominated( this, false );
  3229. if ( bThisPlayerIsDominatingMe )
  3230. {
  3231. int iDoms = pTemp->GetNumberofDominations();
  3232. pTemp->SetNumberofDominations( iDoms - 1);
  3233. }
  3234. }
  3235. }
  3236. }
  3237. if ( TFGameRules()->IsInArenaMode() == false || IsDisconnecting() == true )
  3238. {
  3239. // reset the matrix of who has killed whom with respect to this player
  3240. CTF_GameStats.ResetKillHistory( this );
  3241. }
  3242. IGameEvent *event = gameeventmanager->CreateEvent( "remove_nemesis_relationships" );
  3243. if ( event )
  3244. {
  3245. event->SetInt( "player", entindex() );
  3246. gameeventmanager->FireEvent( event );
  3247. }
  3248. }
  3249. //-----------------------------------------------------------------------------
  3250. // Purpose:
  3251. //-----------------------------------------------------------------------------
  3252. void CTFPlayer::Regenerate( bool bRefillHealthAndAmmo /*= true*/ )
  3253. {
  3254. // We may have been boosted over our max health. If we have,
  3255. // restore it after we reset out class values.
  3256. int nOldMaxHealth = GetMaxHealth();
  3257. int nOldHealth = GetHealth();
  3258. bool bBoosted = ( nOldHealth > nOldMaxHealth || !bRefillHealthAndAmmo ) && ( nOldMaxHealth > 0 );
  3259. int nAmmo[ TF_AMMO_COUNT ];
  3260. if ( !bRefillHealthAndAmmo )
  3261. {
  3262. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3263. {
  3264. nAmmo[ iAmmo ] = GetAmmoCount( iAmmo );
  3265. }
  3266. }
  3267. m_bRegenerating = true;
  3268. // This recomputes MaxHealth
  3269. InitClass();
  3270. m_bRegenerating = false;
  3271. if ( bBoosted )
  3272. {
  3273. SetHealth( MAX( nOldHealth, GetMaxHealth() ) );
  3274. }
  3275. if ( bRefillHealthAndAmmo )
  3276. {
  3277. if ( m_Shared.InCond( TF_COND_BURNING ) )
  3278. {
  3279. m_Shared.RemoveCond( TF_COND_BURNING );
  3280. }
  3281. if ( m_Shared.InCond( TF_COND_URINE ) )
  3282. {
  3283. m_Shared.RemoveCond( TF_COND_URINE );
  3284. }
  3285. if ( m_Shared.InCond( TF_COND_MAD_MILK ) )
  3286. {
  3287. m_Shared.RemoveCond( TF_COND_MAD_MILK );
  3288. }
  3289. if ( m_Shared.InCond( TF_COND_BLEEDING ) )
  3290. {
  3291. m_Shared.RemoveCond( TF_COND_BLEEDING );
  3292. }
  3293. if ( m_Shared.InCond( TF_COND_ENERGY_BUFF ) )
  3294. {
  3295. m_Shared.RemoveCond( TF_COND_ENERGY_BUFF );
  3296. if ( m_Shared.InCond( TF_COND_CANNOT_SWITCH_FROM_MELEE ) )
  3297. {
  3298. m_Shared.RemoveCond( TF_COND_CANNOT_SWITCH_FROM_MELEE );
  3299. }
  3300. }
  3301. if ( m_Shared.InCond( TF_COND_PHASE ) )
  3302. {
  3303. m_Shared.RemoveCond( TF_COND_PHASE );
  3304. }
  3305. if ( m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
  3306. {
  3307. m_Shared.RemoveCond( TF_COND_PARACHUTE_DEPLOYED );
  3308. }
  3309. if ( m_Shared.InCond( TF_COND_PLAGUE ) )
  3310. {
  3311. m_Shared.RemoveCond( TF_COND_PLAGUE );
  3312. }
  3313. #ifdef STAGING_ONLY
  3314. if ( m_Shared.InCond( TF_COND_TRANQ_MARKED ) )
  3315. {
  3316. m_Shared.RemoveCond( TF_COND_TRANQ_MARKED );
  3317. }
  3318. #endif // STAGING_ONLY
  3319. m_Shared.SetSpyCloakMeter( 100.0f );
  3320. m_Shared.SetScoutEnergyDrinkMeter( 100.0f );
  3321. m_Shared.SetDemomanChargeMeter( 100.0f );
  3322. }
  3323. // Reset our first allowed fire time. This allows honorbound weapons to be switched away
  3324. // from for a bit.
  3325. m_Shared.m_flFirstPrimaryAttack = MAX( m_Shared.m_flFirstPrimaryAttack, gpGlobals->curtime + 1.0f );
  3326. if ( bRefillHealthAndAmmo )
  3327. {
  3328. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3329. {
  3330. if ( GetAmmoCount( iAmmo ) > GetMaxAmmo( iAmmo ) )
  3331. {
  3332. SetAmmoCount( GetMaxAmmo( iAmmo ), iAmmo );
  3333. }
  3334. }
  3335. }
  3336. else
  3337. {
  3338. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3339. {
  3340. SetAmmoCount( nAmmo[ iAmmo ], iAmmo );
  3341. }
  3342. }
  3343. IGameEvent *event = gameeventmanager->CreateEvent( "player_regenerate" );
  3344. if ( event )
  3345. {
  3346. gameeventmanager->FireEvent( event );
  3347. }
  3348. }
  3349. //-----------------------------------------------------------------------------
  3350. // Purpose:
  3351. //-----------------------------------------------------------------------------
  3352. void CTFPlayer::InitClass( void )
  3353. {
  3354. SetArmorValue( GetPlayerClass()->GetMaxArmor() );
  3355. // Init the anim movement vars
  3356. m_PlayerAnimState->SetRunSpeed( GetPlayerClass()->GetMaxSpeed() );
  3357. m_PlayerAnimState->SetWalkSpeed( GetPlayerClass()->GetMaxSpeed() * 0.5 );
  3358. // Give default items for class.
  3359. GiveDefaultItems();
  3360. // Set initial health and armor based on class.
  3361. // Do it after items have been delivered, so items can modify it
  3362. SetMaxHealth( GetMaxHealth() );
  3363. SetHealth( GetMaxHealth() );
  3364. TeamFortress_SetSpeed();
  3365. #ifdef STAGING_ONLY
  3366. int nForceRobotModel = 0;
  3367. CALL_ATTRIB_HOOK_INT( nForceRobotModel, appear_as_mvm_robot );
  3368. if ( nForceRobotModel != 0 )
  3369. {
  3370. int nClassIndex = ( GetPlayerClass() ? GetPlayerClass()->GetClassIndex() : TF_CLASS_UNDEFINED );
  3371. GetPlayerClass()->SetCustomModel( g_szPlayerRobotModels[nClassIndex], USE_CLASS_ANIMATIONS );
  3372. UpdateModel();
  3373. SetBloodColor( DONT_BLEED );
  3374. }
  3375. else
  3376. {
  3377. GetPlayerClass()->SetCustomModel( NULL );
  3378. UpdateModel();
  3379. SetBloodColor( BLOOD_COLOR_RED );
  3380. }
  3381. #endif
  3382. }
  3383. //-----------------------------------------------------------------------------
  3384. // Purpose:
  3385. //-----------------------------------------------------------------------------
  3386. void CTFPlayer::CreateViewModel( int iViewModel )
  3387. {
  3388. Assert( iViewModel >= 0 && iViewModel < MAX_VIEWMODELS );
  3389. if ( GetViewModel( iViewModel ) )
  3390. return;
  3391. CTFViewModel *pViewModel = ( CTFViewModel * )CreateEntityByName( "tf_viewmodel" );
  3392. if ( pViewModel )
  3393. {
  3394. pViewModel->SetAbsOrigin( GetAbsOrigin() );
  3395. pViewModel->SetOwner( this );
  3396. pViewModel->SetIndex( iViewModel );
  3397. DispatchSpawn( pViewModel );
  3398. pViewModel->FollowEntity( this, false );
  3399. m_hViewModel.Set( iViewModel, pViewModel );
  3400. }
  3401. }
  3402. //-----------------------------------------------------------------------------
  3403. // Purpose: Gets the view model for the player's off hand
  3404. //-----------------------------------------------------------------------------
  3405. CBaseViewModel *CTFPlayer::GetOffHandViewModel()
  3406. {
  3407. // off hand model is slot 1
  3408. return GetViewModel( 1 );
  3409. }
  3410. //-----------------------------------------------------------------------------
  3411. // Purpose: Sends the specified animation activity to the off hand view model
  3412. //-----------------------------------------------------------------------------
  3413. void CTFPlayer::SendOffHandViewModelActivity( Activity activity )
  3414. {
  3415. CBaseViewModel *pViewModel = GetOffHandViewModel();
  3416. if ( pViewModel )
  3417. {
  3418. int sequence = pViewModel->SelectWeightedSequence( activity );
  3419. pViewModel->SendViewModelMatchingSequence( sequence );
  3420. }
  3421. }
  3422. //-----------------------------------------------------------------------------
  3423. // Purpose: Set the player up with the default weapons, ammo, etc.
  3424. //-----------------------------------------------------------------------------
  3425. void CTFPlayer::GiveDefaultItems()
  3426. {
  3427. // Get the player class data.
  3428. TFPlayerClassData_t *pData = m_PlayerClass.GetData();
  3429. if ( GetTeamNumber() == TEAM_SPECTATOR )
  3430. {
  3431. RemoveAllWeapons();
  3432. return;
  3433. }
  3434. // Give weapons.
  3435. ManageRegularWeapons( pData );
  3436. if ( !TFGameRules() || !TFGameRules()->IsInMedievalMode() )
  3437. {
  3438. // Give a builder weapon for each object the playerclass is allowed to build
  3439. ManageBuilderWeapons( pData );
  3440. }
  3441. // Weapons that added greater ammo than base require us to now fill the player up to max ammo
  3442. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3443. {
  3444. GiveAmmo( GetMaxAmmo(iAmmo), iAmmo, true, kAmmoSource_Resupply );
  3445. }
  3446. // Clear the player's banner buffs.
  3447. m_Shared.RemoveCond( TF_COND_OFFENSEBUFF );
  3448. m_Shared.RemoveCond( TF_COND_DEFENSEBUFF );
  3449. m_Shared.RemoveCond( TF_COND_REGENONDAMAGEBUFF );
  3450. m_Shared.RemoveCond( TF_COND_NOHEALINGDAMAGEBUFF );
  3451. m_Shared.RemoveCond( TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK );
  3452. m_Shared.RemoveCond( TF_COND_DEFENSEBUFF_HIGH );
  3453. }
  3454. //-----------------------------------------------------------------------------
  3455. // Purpose:
  3456. //-----------------------------------------------------------------------------
  3457. void CTFPlayer::ManageBuilderWeapons( TFPlayerClassData_t *pData )
  3458. {
  3459. // Collect all builders and validate them against the list of objects (below)
  3460. CUtlVector< CTFWeaponBuilder* > vecBuilderDestroyList;
  3461. for ( int i = 0; i < MAX_WEAPONS; ++i )
  3462. {
  3463. CTFWeaponBuilder *pBuilder = dynamic_cast< CTFWeaponBuilder* >( GetWeapon( i ) );
  3464. if ( !pBuilder )
  3465. continue;
  3466. vecBuilderDestroyList.AddToTail( pBuilder );
  3467. }
  3468. CEconItemView *pLoadoutBuilderItemView = NULL;
  3469. // Go through each object and see if we need to create or remove builders
  3470. for ( int i = 0; i < OBJ_LAST; ++i )
  3471. {
  3472. if ( !GetPlayerClass()->CanBuildObject( i ) )
  3473. continue;
  3474. // TODO: Need to add support for "n" builders, rather hard-wired for two.
  3475. // Currently, the only class that uses more than one is the spy:
  3476. // - BUILDER is OBJ_ATTACHMENT_SAPPER, which is invoked via weapon selection (see objects.txt).
  3477. // - BUILDER2 is OBJ_SPY_TRAP, which is invoked via a build command from PDA3 (spy-specific).
  3478. #ifdef STAGING_ONLY
  3479. int nLoadoutPos = ( GetObjectInfo( i )->m_bRequiresOwnBuilder ) ? LOADOUT_POSITION_BUILDING2 : LOADOUT_POSITION_BUILDING;
  3480. #else
  3481. int nLoadoutPos = LOADOUT_POSITION_BUILDING;
  3482. #endif
  3483. pLoadoutBuilderItemView = GetLoadoutItem( GetPlayerClass()->GetClassIndex(), nLoadoutPos, true );
  3484. // Do we have a specific builder for this object?
  3485. CTFWeaponBuilder *pBuilder = CTFPlayerSharedUtils::GetBuilderForObjectType( this, i );
  3486. if ( pBuilder )
  3487. {
  3488. // We may have a different builder back-end item now. If so, destroy and make a new one below.
  3489. CEconItemView *pCurrentBuilderItemView = pBuilder->GetAttributeContainer()->GetItem();
  3490. if ( pCurrentBuilderItemView == NULL || pLoadoutBuilderItemView == NULL || !ItemsMatch( pData, pCurrentBuilderItemView, pLoadoutBuilderItemView, pBuilder ) )
  3491. {
  3492. // Manually nuke the item from the weapon list here so that we don't find it
  3493. vecBuilderDestroyList.FindAndRemove( pBuilder );
  3494. Weapon_Detach( pBuilder );
  3495. UTIL_Remove( pBuilder );
  3496. // Wrong builder item, so pretend we didn't find one
  3497. pBuilder = NULL;
  3498. }
  3499. }
  3500. else if ( !GetObjectInfo( i )->m_bRequiresOwnBuilder )
  3501. {
  3502. // Do we have a default builder, and an object that doesn't require a specific builder?
  3503. pBuilder = CTFPlayerSharedUtils::GetBuilderForObjectType( this, -1 );
  3504. if ( pBuilder )
  3505. {
  3506. // Flag it as supported by this builder (ugly, but necessary for legacy system)
  3507. pBuilder->SetObjectTypeAsBuildable( i );
  3508. }
  3509. }
  3510. // Is a new builder required?
  3511. if ( !pBuilder || ( GetObjectInfo( i )->m_bRequiresOwnBuilder && !( CTFPlayerSharedUtils::GetBuilderForObjectType( this, i ) ) ) )
  3512. {
  3513. pBuilder = dynamic_cast< CTFWeaponBuilder* >( GiveNamedItem( "tf_weapon_builder", i, pLoadoutBuilderItemView ) );
  3514. if ( pBuilder )
  3515. {
  3516. pBuilder->DefaultTouch( this );
  3517. }
  3518. }
  3519. // Builder settings
  3520. if ( pBuilder )
  3521. {
  3522. if ( m_bRegenerating == false )
  3523. {
  3524. pBuilder->WeaponReset();
  3525. }
  3526. pBuilder->GiveDefaultAmmo();
  3527. pBuilder->ChangeTeam( GetTeamNumber() );
  3528. pBuilder->SetObjectTypeAsBuildable( i );
  3529. pBuilder->m_nSkin = GetTeamNumber() - 2; // color the w_model to the team
  3530. // Pull it out of the "destroy" list
  3531. vecBuilderDestroyList.FindAndRemove( pBuilder );
  3532. }
  3533. }
  3534. // Anything left should be destroyed
  3535. FOR_EACH_VEC( vecBuilderDestroyList, i )
  3536. {
  3537. Assert( vecBuilderDestroyList[i] );
  3538. Weapon_Detach( vecBuilderDestroyList[i] );
  3539. UTIL_Remove( vecBuilderDestroyList[i] );
  3540. }
  3541. }
  3542. //-----------------------------------------------------------------------------
  3543. // Purpose:
  3544. //-----------------------------------------------------------------------------
  3545. bool CTFPlayer::ItemsMatch( TFPlayerClassData_t *pData, CEconItemView *pCurWeaponItem, CEconItemView *pNewWeaponItem, CTFWeaponBase *pWpnEntity )
  3546. {
  3547. if ( !pNewWeaponItem || !pNewWeaponItem->IsValid() )
  3548. return false;
  3549. // If we already have a weapon in this slot but is not the same type, nuke it (changed classes)
  3550. // We don't need to do this for non-base items because they've already been verified above.
  3551. bool bHasNonBaseWeapon = pNewWeaponItem ? pNewWeaponItem->GetItemQuality() != AE_NORMAL : false;
  3552. if ( bHasNonBaseWeapon )
  3553. {
  3554. // If the item isn't the one we're supposed to have, nuke it
  3555. if ( pCurWeaponItem->GetItemID() != pNewWeaponItem->GetItemID() )
  3556. {
  3557. /*
  3558. Msg("Removing %s because its global index (%d) doesn't match the loadout's (%d)\n", pWeapon->GetDebugName(),
  3559. pCurWeaponItem->GetItemID(),
  3560. pNewWeaponItem->GetItemID() );
  3561. */
  3562. return false;
  3563. }
  3564. // Some items create different entities when wielded by different classes. If so, we need to say
  3565. // the items don't match so the item gets recreated as the right entity.
  3566. if ( pWpnEntity )
  3567. {
  3568. const char *pszCurWeaponClass = pWpnEntity->GetClassname(),
  3569. *pszNewWeaponTransClass = TranslateWeaponEntForClass( pNewWeaponItem->GetStaticData()->GetItemClass(), GetPlayerClass()->GetClassIndex() );
  3570. if ( !pszCurWeaponClass || !pszNewWeaponTransClass || Q_stricmp( pszCurWeaponClass, pszNewWeaponTransClass ) )
  3571. return false;
  3572. }
  3573. }
  3574. else
  3575. {
  3576. if ( pCurWeaponItem->GetItemQuality() != AE_NORMAL || (pCurWeaponItem->GetItemDefIndex() != pNewWeaponItem->GetItemDefIndex()) )
  3577. {
  3578. //Msg("Removing %s because it's not the right type for the class.\n", pWeapon->GetDebugName() );
  3579. return false;
  3580. }
  3581. CSteamID ownerSteamID;
  3582. GetSteamID( &ownerSteamID );
  3583. // If the owner is not the same, then they're different as well. This catches
  3584. // cases of stock items comparing
  3585. if ( pCurWeaponItem->GetAccountID() != ownerSteamID.GetAccountID() )
  3586. {
  3587. return false;
  3588. }
  3589. }
  3590. return true;
  3591. }
  3592. //-----------------------------------------------------------------------------
  3593. // Purpose:
  3594. //-----------------------------------------------------------------------------
  3595. bool CTFPlayer::ItemIsAllowed( CEconItemView *pItem )
  3596. {
  3597. if ( !pItem || !pItem->GetStaticData() )
  3598. return false;
  3599. int iClass = GetPlayerClass()->GetClassIndex();
  3600. int iSlot = pItem->GetStaticData()->GetLoadoutSlot(iClass);
  3601. // Passtime hack to allow passtime gun
  3602. if ( V_stristr( pItem->GetItemDefinition()->GetDefinitionName(), "passtime" ) )
  3603. {
  3604. return TFGameRules() && TFGameRules()->IsPasstimeMode();
  3605. }
  3606. // Holiday Restriction
  3607. CEconItemDefinition* pData = pItem->GetStaticData();
  3608. if ( TFGameRules() && pData && pData->GetHolidayRestriction() )
  3609. {
  3610. int iHolidayRestriction = UTIL_GetHolidayForString( pData->GetHolidayRestriction() );
  3611. if ( iHolidayRestriction != kHoliday_None && !TFGameRules()->IsHolidayActive( iHolidayRestriction ) )
  3612. return false;
  3613. }
  3614. if ( TFGameRules()->InStalemate() && mp_stalemate_meleeonly.GetBool() )
  3615. {
  3616. bool bMeleeOnlyAllowed = (iSlot == LOADOUT_POSITION_MELEE)
  3617. || (iClass == TF_CLASS_SPY && (iSlot == LOADOUT_POSITION_PDA || iSlot == LOADOUT_POSITION_PDA2));
  3618. if ( !bMeleeOnlyAllowed )
  3619. return false;
  3620. }
  3621. if ( TFGameRules()->IsInMedievalMode() )
  3622. {
  3623. bool bMedievalModeAllowed = false;
  3624. // Allow all melee-class weapons, non-weapons, and the spy equipment.
  3625. switch ( iSlot )
  3626. {
  3627. case LOADOUT_POSITION_MELEE:
  3628. case LOADOUT_POSITION_HEAD:
  3629. case LOADOUT_POSITION_MISC:
  3630. case LOADOUT_POSITION_MISC2:
  3631. case LOADOUT_POSITION_ACTION:
  3632. case LOADOUT_POSITION_TAUNT:
  3633. case LOADOUT_POSITION_TAUNT2:
  3634. case LOADOUT_POSITION_TAUNT3:
  3635. case LOADOUT_POSITION_TAUNT4:
  3636. case LOADOUT_POSITION_TAUNT5:
  3637. case LOADOUT_POSITION_TAUNT6:
  3638. case LOADOUT_POSITION_TAUNT7:
  3639. case LOADOUT_POSITION_TAUNT8:
  3640. #ifdef STAGING_ONLY
  3641. case LOADOUT_POSITION_PDA3:
  3642. case LOADOUT_POSITION_BUILDING2:
  3643. #endif
  3644. bMedievalModeAllowed = true;
  3645. break;
  3646. case LOADOUT_POSITION_PDA:
  3647. case LOADOUT_POSITION_PDA2:
  3648. if ( iClass == TF_CLASS_SPY )
  3649. bMedievalModeAllowed = true;
  3650. break;
  3651. }
  3652. if ( !bMedievalModeAllowed )
  3653. {
  3654. static CSchemaAttributeDefHandle pAttrib_AllowedInMedievalMode( "allowed in medieval mode" );
  3655. if ( !pItem->FindAttribute( pAttrib_AllowedInMedievalMode ) )
  3656. {
  3657. return false;
  3658. }
  3659. }
  3660. }
  3661. return true;
  3662. }
  3663. //-----------------------------------------------------------------------------
  3664. // Purpose:
  3665. //-----------------------------------------------------------------------------
  3666. void CTFPlayer::ManageRegularWeapons( TFPlayerClassData_t *pData )
  3667. {
  3668. // Reset ammo.
  3669. RemoveAllAmmo();
  3670. // Remove our disguise weapon.
  3671. m_Shared.RemoveDisguiseWeapon();
  3672. CUtlVector<const char *> precacheStrings;
  3673. CBaseCombatWeapon* pCurrentWeapon = m_hActiveWeapon;
  3674. // Give ammo. Must be done before weapons, so weapons know the player has ammo for them.
  3675. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3676. {
  3677. GiveAmmo( GetMaxAmmo(iAmmo), iAmmo, true, kAmmoSource_Resupply );
  3678. }
  3679. if ( IsX360() )
  3680. {
  3681. ManageRegularWeaponsLegacy( pData );
  3682. }
  3683. else
  3684. {
  3685. // Loop through our current wearables and ensure we're supposed to have them.
  3686. ValidateWearables( pData );
  3687. // Loop through all our current weapons, and ensure we're supposed to have them.
  3688. ValidateWeapons( pData, true );
  3689. // Create a copy of currently equipped items, if we equip something new report player loadout
  3690. bool bItemsChanged = false;
  3691. // Now Loop through our inventory for the current class, and give us any items we don't have.
  3692. int iClass = GetPlayerClass()->GetClassIndex();
  3693. if ( iClass > TF_CLASS_UNDEFINED && iClass < TF_CLASS_COUNT )
  3694. {
  3695. CSteamID ownerSteamID;
  3696. GetSteamID( &ownerSteamID );
  3697. for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
  3698. {
  3699. // bots don't need the action slot item for MvM (canteen)
  3700. if ( ( i == LOADOUT_POSITION_ACTION ) && IsBot() && TFGameRules() && TFGameRules()->IsMannVsMachineMode() && ( GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  3701. continue;
  3702. m_EquippedLoadoutItemIndices[i] = LOADOUT_SLOT_USE_BASE_ITEM;
  3703. // use base items in training mode
  3704. CEconItemView *pItem = GetLoadoutItem( iClass, i, true );
  3705. if ( !pItem || !pItem->IsValid() )
  3706. continue;
  3707. if ( !ItemIsAllowed( pItem ) )
  3708. continue;
  3709. // Only do this for taunts, because other items will be caught by the dynamic model loading system.
  3710. if ( IsTauntSlot( i ) )
  3711. {
  3712. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Precaching taunts, etc", __FUNCTION__ );
  3713. // This has to be done before the continue for "no_entity", because we're trying to precache taunts which
  3714. // explicitly bail out there.
  3715. precacheStrings.RemoveAll();
  3716. pItem->GetItemDefinition()->GeneratePrecacheModelStrings( false, &precacheStrings );
  3717. FOR_EACH_VEC( precacheStrings, iModel )
  3718. {
  3719. if ( precacheStrings[iModel] && ( *precacheStrings[iModel] ) )
  3720. {
  3721. PrecacheModel( precacheStrings[iModel], false );
  3722. }
  3723. }
  3724. }
  3725. m_EquippedLoadoutItemIndices[i] = pItem->GetItemID();
  3726. Assert( pItem->GetStaticData()->GetItemClass() );
  3727. if ( pItem->GetStaticData()->GetItemClass() && FStrEq( pItem->GetStaticData()->GetItemClass(), "no_entity" ) )
  3728. continue;
  3729. CTFWeaponBase *pCurrentWeaponOfType = NULL;
  3730. bool bAlreadyHave = false;
  3731. // Don't need to check weapons if it's a wearable-only slot
  3732. if ( !IsWearableSlot(i) || pItem->GetItemDefinition()->IsActingAsAWeapon() )
  3733. {
  3734. // Weapon slot. Check out weapons to see if we have it.
  3735. for ( int wpn = 0; wpn < MAX_WEAPONS; wpn++ )
  3736. {
  3737. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(wpn);
  3738. if ( !pWeapon )
  3739. continue;
  3740. if ( ItemsMatch( pData, pWeapon->GetAttributeContainer()->GetItem(), pItem, pWeapon ) )
  3741. {
  3742. pCurrentWeaponOfType = pWeapon;
  3743. bAlreadyHave = true;
  3744. break;
  3745. }
  3746. }
  3747. }
  3748. CEconWearable *pWearable = NULL;
  3749. if ( !bAlreadyHave )
  3750. {
  3751. // We couldn't find a matching weapon. See if we have a matching wearable.
  3752. for ( int wbl = 0; wbl < m_hMyWearables.Count(); wbl++ )
  3753. {
  3754. pWearable = m_hMyWearables[wbl];
  3755. if ( !pWearable )
  3756. continue;
  3757. CEconItemView *pWearableView = pWearable->GetAttributeContainer()->GetItem();
  3758. if ( ItemsMatch( pData, pWearableView, pItem ) )
  3759. {
  3760. bAlreadyHave = true;
  3761. break;
  3762. }
  3763. }
  3764. }
  3765. if ( !bAlreadyHave && pItem->GetStaticData()->GetItemClass() )
  3766. {
  3767. CEconEntity *pNewItem = dynamic_cast<CEconEntity*>(GiveNamedItem( pItem->GetStaticData()->GetItemClass(), 0, pItem ));
  3768. Assert( pNewItem );
  3769. if ( pNewItem )
  3770. {
  3771. pNewItem->GetAttributeContainer()->GetItem()->SetOverrideAccountID( ownerSteamID.GetAccountID() );
  3772. CTFWeaponBuilder *pBuilder = dynamic_cast<CTFWeaponBuilder*>( (CBaseEntity*)pNewItem );
  3773. if ( pBuilder )
  3774. {
  3775. pBuilder->SetSubType( pData->m_aBuildable[0] );
  3776. }
  3777. CBaseCombatWeapon* pWeapon = dynamic_cast< CBaseCombatWeapon* >( pNewItem );
  3778. if ( pWeapon )
  3779. {
  3780. pWeapon->SetSoundsEnabled( false );
  3781. }
  3782. pNewItem->GiveTo( this );
  3783. if ( pWeapon )
  3784. {
  3785. pWeapon->SetSoundsEnabled( true );
  3786. }
  3787. }
  3788. }
  3789. else
  3790. {
  3791. if ( pCurrentWeaponOfType )
  3792. {
  3793. pCurrentWeaponOfType->UpdateExtraWearables();
  3794. // We need to ensure all hands pointers are updated for all weapons.
  3795. // Otherwise we could end up using animation sequences from the wrong class hands.
  3796. pCurrentWeaponOfType->UpdateHands();
  3797. }
  3798. }
  3799. bItemsChanged |= !bAlreadyHave;
  3800. } // For each item in load out
  3801. }
  3802. if ( bItemsChanged )
  3803. {
  3804. CTF_GameStats.Event_PlayerLoadoutChanged( this, false );
  3805. }
  3806. // We may have added weapons that make others invalid. Recheck.
  3807. ValidateWeapons( pData, false );
  3808. if ( m_hActiveWeapon.Get() != pCurrentWeapon && m_hActiveWeapon )
  3809. {
  3810. m_hActiveWeapon->WeaponSound( DEPLOY );
  3811. }
  3812. CSingleUserRecipientFilter filter( this );
  3813. UserMessageBegin( filter, "PlayerLoadoutUpdated" );
  3814. WRITE_BYTE( entindex() );
  3815. MessageEnd();
  3816. }
  3817. // On equip, legacy source code will autoswitch to new weapons.
  3818. // Instead of refactoring, we check here to see if we are allowed to have certain weapons switched to
  3819. // TF2: Not allowed to have a actionslot item as last or active on regenerate / respawn
  3820. // HACK Don't allow the parachute to be an active weapon
  3821. CTFWeaponBase *pCurr = GetActiveTFWeapon();
  3822. CTFWeaponBase *pPrev = dynamic_cast<CTFWeaponBase*>( GetLastWeapon() );
  3823. if ( ( pCurr && pCurr->GetAttributeContainer()->GetItem()->GetEquippedPositionForClass( GetPlayerClass()->GetClassIndex() ) == LOADOUT_POSITION_ACTION )
  3824. || ( pPrev && pPrev->GetAttributeContainer()->GetItem()->GetEquippedPositionForClass( GetPlayerClass()->GetClassIndex() ) == LOADOUT_POSITION_ACTION )
  3825. || ( pCurr && pCurr->GetWeaponID() == TF_WEAPON_PARACHUTE )
  3826. ) {
  3827. m_bRegenerating = false;
  3828. m_iLastWeaponSlot = 0;
  3829. }
  3830. if ( m_bRegenerating == false )
  3831. {
  3832. bool bWepSwitched = false;
  3833. if ( m_bRememberActiveWeapon && m_iActiveWeaponTypePriorToDeath )
  3834. {
  3835. CTFWeaponBase *pWeapon = Weapon_OwnsThisID( m_iActiveWeaponTypePriorToDeath );
  3836. if ( pWeapon && pWeapon->GetAttributeContainer()->GetItem()->GetEquippedPositionForClass( GetPlayerClass()->GetClassIndex() ) != LOADOUT_POSITION_ACTION )
  3837. {
  3838. bWepSwitched = Weapon_Switch( pWeapon );
  3839. }
  3840. }
  3841. if ( !bWepSwitched )
  3842. {
  3843. SetActiveWeapon( NULL );
  3844. // Find a weapon to switch to, starting with primary.
  3845. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>( GetEntityForLoadoutSlot( LOADOUT_POSITION_PRIMARY ) );
  3846. if ( !pWeapon || !pWeapon->CanBeSelected() || !Weapon_Switch( pWeapon ) )
  3847. {
  3848. pWeapon = dynamic_cast<CTFWeaponBase*>( GetEntityForLoadoutSlot( LOADOUT_POSITION_SECONDARY ) );
  3849. if ( !pWeapon || pWeapon->CanBeSelected() || !Weapon_Switch( pWeapon ) )
  3850. {
  3851. pWeapon = dynamic_cast<CTFWeaponBase*>( GetEntityForLoadoutSlot( LOADOUT_POSITION_MELEE ) );
  3852. Weapon_Switch( pWeapon );
  3853. }
  3854. }
  3855. }
  3856. if ( (m_iLastWeaponSlot == 0 || !m_bRememberLastWeapon) && !m_bRememberActiveWeapon )
  3857. {
  3858. m_iLastWeaponSlot = 1;
  3859. }
  3860. if ( !Weapon_GetSlot( m_iLastWeaponSlot ) )
  3861. {
  3862. Weapon_SetLast( Weapon_GetSlot( TF_WPN_TYPE_MELEE ) );
  3863. }
  3864. else
  3865. {
  3866. Weapon_SetLast( Weapon_GetSlot( m_iLastWeaponSlot ) );
  3867. }
  3868. }
  3869. // Now make sure we don't have too much ammo. This can happen if an item has reduced our max ammo.
  3870. for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
  3871. {
  3872. int iMax = GetMaxAmmo(iAmmo);
  3873. if ( iMax < GetAmmoCount(iAmmo) )
  3874. {
  3875. RemoveAmmo( GetAmmoCount(iAmmo) - iMax, iAmmo );
  3876. }
  3877. }
  3878. // If our max health dropped below current due to item changes, drop our current health.
  3879. // If we're not being buffed, clamp it to max. Otherwise, clamp it to the max buffed health
  3880. int iMaxHealth = m_Shared.InCond( TF_COND_HEALTH_BUFF ) ? m_Shared.GetMaxBuffedHealth() : GetMaxHealth();
  3881. if ( m_iHealth > iMaxHealth )
  3882. {
  3883. // Modify health manually to prevent showing all the "you got hurt" UI.
  3884. m_iHealth = iMaxHealth;
  3885. }
  3886. if ( TFGameRules()->InStalemate() && mp_stalemate_meleeonly.GetBool() )
  3887. {
  3888. CBaseCombatWeapon *meleeWeapon = Weapon_GetSlot( TF_WPN_TYPE_MELEE );
  3889. if ( meleeWeapon )
  3890. {
  3891. Weapon_Switch( meleeWeapon );
  3892. }
  3893. }
  3894. // In testing mode, switch bots to the weapon being tested
  3895. if ( TFGameRules()->IsInItemTestingMode() && IsFakeClient() )
  3896. {
  3897. // Our first player should be the human tester
  3898. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( 1 ) );
  3899. if ( pPlayer && !pPlayer->IsFakeClient() )
  3900. {
  3901. // Loop through all the items we're testing
  3902. FOR_EACH_VEC( pPlayer->m_ItemsToTest, i )
  3903. {
  3904. CEconItemView *pItem = &pPlayer->m_ItemsToTest[i].scriptItem;
  3905. if ( !pItem )
  3906. continue;
  3907. int iSlot = pItem->GetStaticData()->GetLoadoutSlot( GetPlayerClass()->GetClassIndex() );
  3908. if ( IsWearableSlot( iSlot ) )
  3909. continue;
  3910. CBaseCombatWeapon *pWeapon = Weapon_GetSlot( iSlot );
  3911. if ( pWeapon )
  3912. {
  3913. Weapon_Switch( pWeapon );
  3914. break;
  3915. }
  3916. }
  3917. }
  3918. }
  3919. if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && !IsBot() )
  3920. {
  3921. if ( m_Inventory.ClassLoadoutHasChanged( GetPlayerClass()->GetClassIndex() )
  3922. || ( m_bSwitchedClass )
  3923. || ( g_pPopulationManager && g_pPopulationManager->IsRestoringCheckpoint() ) )
  3924. {
  3925. ReapplyPlayerUpgrades();
  3926. }
  3927. // Calculate how much money is being used on active class / items
  3928. int nSpending = 0;
  3929. int iClass = GetPlayerClass()->GetClassIndex();
  3930. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  3931. if ( upgrades )
  3932. {
  3933. for( int u = 0; u < upgrades->Count(); ++u )
  3934. {
  3935. // Class Match, Check to see if we have this item equipped
  3936. if ( iClass == upgrades->Element(u).m_iPlayerClass)
  3937. {
  3938. // Player upgrade
  3939. if ( upgrades->Element( u ).m_itemDefIndex == INVALID_ITEM_DEF_INDEX )
  3940. {
  3941. nSpending += upgrades->Element(u).m_nCost;
  3942. continue;
  3943. }
  3944. // Item upgrade, look at equipment only not miscs or bottle
  3945. for ( int itemIndex = 0; itemIndex <= LOADOUT_POSITION_PDA2; itemIndex++ )
  3946. {
  3947. CEconItemView *pItem = GetLoadoutItem( iClass, itemIndex, true );
  3948. if ( upgrades->Element(u).m_itemDefIndex == pItem->GetItemDefIndex() )
  3949. {
  3950. nSpending += upgrades->Element(u).m_nCost;
  3951. break;
  3952. }
  3953. }
  3954. }
  3955. }
  3956. }
  3957. CMannVsMachineStats *pStats = MannVsMachineStats_GetInstance();
  3958. if ( pStats )
  3959. {
  3960. pStats->NotifyPlayerActiveUpgradeCosts( this, nSpending );
  3961. }
  3962. }
  3963. PostInventoryApplication();
  3964. }
  3965. //-----------------------------------------------------------------------------
  3966. // Purpose:
  3967. //-----------------------------------------------------------------------------
  3968. CEconItemView *CTFPlayer::GetLoadoutItem( int iClass, int iSlot, bool bReportWhitelistFails )
  3969. {
  3970. if ( TFGameRules()->IsInItemTestingMode() )
  3971. {
  3972. CEconItemView *pItem = ItemTesting_GetTestItem( iClass, iSlot );
  3973. if ( pItem )
  3974. return pItem;
  3975. }
  3976. if ( TFGameRules()->IsInTraining() || TFGameRules()->IsInItemTestingMode() )
  3977. {
  3978. CTFInventoryManager *pInventoryManager = TFInventoryManager();
  3979. return pInventoryManager->GetBaseItemForClass( iClass, iSlot );
  3980. }
  3981. CEconItemView *pItem = m_Inventory.GetItemInLoadout( iClass, iSlot );
  3982. // Check to see if this item passes the tournament rules (in whitelist/or normal quality).
  3983. // If it doesn't, we fall back to the base item for the loadout slot.
  3984. if ( (pItem && pItem->IsValid()) && (pItem->GetItemQuality() != AE_NORMAL) && !pItem->GetStaticData()->IsAllowedInMatch() && TFGameRules()->IsInTournamentMode() )
  3985. {
  3986. if ( bReportWhitelistFails )
  3987. {
  3988. ClientPrint( this, HUD_PRINTNOTIFY, "#Item_BlacklistedInMatch", pItem->GetStaticData()->GetItemBaseName() );
  3989. }
  3990. pItem = TFInventoryManager()->GetBaseItemForClass( iClass, iSlot );
  3991. }
  3992. return pItem;
  3993. }
  3994. //-----------------------------------------------------------------------------
  3995. // Purpose: Handles pressing the use action slot item key.
  3996. //-----------------------------------------------------------------------------
  3997. void CTFPlayer::UseActionSlotItemPressed( void )
  3998. {
  3999. m_bUsingActionSlot = true;
  4000. if ( TryToPickupDroppedWeapon() )
  4001. return;
  4002. int iNoiseMaker = 0;
  4003. CALL_ATTRIB_HOOK_INT( iNoiseMaker, enable_misc2_noisemaker );
  4004. if ( iNoiseMaker )
  4005. {
  4006. DoNoiseMaker();
  4007. return;
  4008. }
  4009. CBaseEntity *pActionSlotEntity = GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION );
  4010. if ( !pActionSlotEntity )
  4011. return;
  4012. // get the equipped item and see what it is
  4013. CTFPowerupBottle *pPowerupBottle = dynamic_cast< CTFPowerupBottle* >( pActionSlotEntity );
  4014. if ( pPowerupBottle )
  4015. {
  4016. // @todo send event to clients so that they know what's going on
  4017. pPowerupBottle->Use();
  4018. return;
  4019. }
  4020. // is it a throwable?
  4021. CTFThrowable *pThrowable = dynamic_cast< CTFThrowable* >( pActionSlotEntity );
  4022. if ( pThrowable )
  4023. {
  4024. if ( !Weapon_ShouldSelectItem( pThrowable ) )
  4025. return;
  4026. if ( GetActiveWeapon() )
  4027. {
  4028. if ( !GetActiveWeapon()->CanHolster() )
  4029. return;
  4030. ResetAutoaim( );
  4031. }
  4032. // Check if this is the spellbook so we can save off info to preserve weapon switching
  4033. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( pThrowable );
  4034. if ( pSpellBook )
  4035. {
  4036. if ( !pSpellBook->CanCastSpell( this ) )
  4037. {
  4038. // if no spell force a roll if cheat is active
  4039. if ( tf_halloween_unlimited_spells.GetBool() && !pSpellBook->HasASpellWithCharges() )
  4040. {
  4041. pSpellBook->RollNewSpell( 0 );
  4042. }
  4043. else if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  4044. {
  4045. // if I'm in a halloween Vehicle, cast the spell immediately
  4046. //pSpellBook->CastKartSpell();
  4047. pSpellBook->PrimaryAttack();
  4048. }
  4049. else
  4050. {
  4051. EmitSound_t params;
  4052. params.m_flSoundTime = 0;
  4053. params.m_pflSoundDuration = 0;
  4054. params.m_pSoundName = "Player.DenyWeaponSelection";
  4055. CSingleUserRecipientFilter filter( this );
  4056. EmitSound( filter, entindex(), params );
  4057. }
  4058. return;
  4059. }
  4060. // Notify the spellbook of the current last used weapon
  4061. pSpellBook->SaveLastWeapon( GetLastWeapon() );
  4062. }
  4063. // Equip it
  4064. Weapon_Switch( pThrowable );
  4065. return;
  4066. }
  4067. if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() )
  4068. {
  4069. CTFGrapplingHook *pGrapplingHook = dynamic_cast< CTFGrapplingHook* >( pActionSlotEntity );
  4070. if ( pGrapplingHook )
  4071. {
  4072. Weapon_Switch( pGrapplingHook );
  4073. return;
  4074. }
  4075. }
  4076. }
  4077. //-----------------------------------------------------------------------------
  4078. // Purpose: Handles releasing the use action slot item key.
  4079. //-----------------------------------------------------------------------------
  4080. void CTFPlayer::UseActionSlotItemReleased( void )
  4081. {
  4082. m_bUsingActionSlot = false;
  4083. if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() )
  4084. {
  4085. // if we're using the hook, switch back to the last weapon
  4086. if ( GetActiveTFWeapon() && GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_GRAPPLINGHOOK )
  4087. {
  4088. CBaseCombatWeapon *pLastWeapon = GetLastWeapon();
  4089. if ( pLastWeapon && Weapon_CanSwitchTo( pLastWeapon ) )
  4090. {
  4091. Weapon_Switch( pLastWeapon );
  4092. }
  4093. else
  4094. {
  4095. // in case we failed to switch back to last weapon for some reason, just find the next best
  4096. SwitchToNextBestWeapon( pLastWeapon );
  4097. }
  4098. return;
  4099. }
  4100. }
  4101. }
  4102. //-----------------------------------------------------------------------------
  4103. // Purpose: Handles pressing the inspect key.
  4104. //-----------------------------------------------------------------------------
  4105. void CTFPlayer::InspectButtonPressed()
  4106. {
  4107. m_flInspectTime = gpGlobals->curtime;
  4108. }
  4109. //-----------------------------------------------------------------------------
  4110. // Purpose: Handles releasing the inspect key.
  4111. //-----------------------------------------------------------------------------
  4112. void CTFPlayer::InspectButtonReleased()
  4113. {
  4114. m_flInspectTime = 0.f;
  4115. }
  4116. //-----------------------------------------------------------------------------
  4117. // Purpose:
  4118. //-----------------------------------------------------------------------------
  4119. bool CTFPlayer::AddToSpyKnife( float value, bool force )
  4120. {
  4121. CTFKnife *pWpn = (CTFKnife *)Weapon_OwnsThisID( TF_WEAPON_KNIFE );
  4122. if ( !pWpn )
  4123. return false;
  4124. return pWpn->DecreaseRegenerationTime( value, force );
  4125. }
  4126. //-----------------------------------------------------------------------------
  4127. // Purpose:
  4128. //-----------------------------------------------------------------------------
  4129. void CTFPlayer::RemoveAllItems()
  4130. {
  4131. // Nuke items.
  4132. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4133. {
  4134. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(i);
  4135. if ( !pWeapon )
  4136. continue;
  4137. Weapon_Detach( pWeapon );
  4138. UTIL_Remove( pWeapon );
  4139. }
  4140. // Nuke wearables.
  4141. for ( int wbl = m_hMyWearables.Count()-1; wbl >= 0; wbl-- )
  4142. {
  4143. CEconWearable *pWearable = m_hMyWearables[wbl];
  4144. Assert( pWearable );
  4145. if ( !pWearable )
  4146. continue;
  4147. RemoveWearable( pWearable );
  4148. }
  4149. }
  4150. //-----------------------------------------------------------------------------
  4151. // Purpose:
  4152. //-----------------------------------------------------------------------------
  4153. void CTFPlayer::ValidateWeapons( TFPlayerClassData_t *pData, bool bResetWeapons )
  4154. {
  4155. CSteamID steamIDForPlayer;
  4156. GetSteamID( &steamIDForPlayer );
  4157. bool bFoundBuffItem = false;
  4158. bool bOverrideRemoval = false;
  4159. if ( bResetWeapons && m_bForceItemRemovalOnRespawn )
  4160. {
  4161. bOverrideRemoval = true;
  4162. m_bForceItemRemovalOnRespawn = false;
  4163. }
  4164. // Disable sounds for all weapons. We're about to switch weapons MANY times,
  4165. // and we don't want the deploy sounds to play for any of them, since none
  4166. // of the deploys are actually visible to the player
  4167. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4168. {
  4169. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(i);
  4170. if ( !pWeapon )
  4171. continue;
  4172. pWeapon->SetSoundsEnabled( false );
  4173. }
  4174. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4175. {
  4176. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(i);
  4177. if ( !pWeapon )
  4178. continue;
  4179. int iLoadoutSlot = pWeapon->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( GetPlayerClass()->GetClassIndex() );
  4180. CEconItemView *pItem = GetLoadoutItem( GetPlayerClass()->GetClassIndex(), iLoadoutSlot );
  4181. // See if gamerules says this item isn't allowed right now
  4182. bool bForceRemoved = bOverrideRemoval || !ItemIsAllowed( pItem );
  4183. if ( bForceRemoved || !ItemsMatch( pData, pWeapon->GetAttributeContainer()->GetItem(), pItem, pWeapon ) )
  4184. {
  4185. // we can't hold this weapon anymore, switch to the next best weapon before removing it
  4186. if ( GetActiveTFWeapon() == pWeapon )
  4187. {
  4188. SwitchToNextBestWeapon( pWeapon );
  4189. }
  4190. // drop weapon that belongs to other player, unless we're not regenerating
  4191. // which happens at round restart
  4192. if ( !bForceRemoved && m_bRegenerating )
  4193. {
  4194. CEconItemView *pDroppedItem = pWeapon->GetAttributeContainer()->GetItem();
  4195. CSteamID steamID;
  4196. GetSteamID( &steamID );
  4197. if ( pDroppedItem->GetAccountID() != steamID.GetAccountID() )
  4198. {
  4199. // Find the position and angle of the weapons so the "ammo box" matches.
  4200. Vector vecPackOrigin;
  4201. QAngle vecPackAngles;
  4202. if( !CalculateAmmoPackPositionAndAngles( pWeapon, vecPackOrigin, vecPackAngles ) )
  4203. return;
  4204. CTFDroppedWeapon *pDroppedWeapon = CTFDroppedWeapon::Create( this, vecPackOrigin, vecPackAngles, pWeapon->GetWorldModel(), pDroppedItem );
  4205. if ( pDroppedWeapon )
  4206. {
  4207. pDroppedWeapon->InitDroppedWeapon( this, pWeapon, false );
  4208. }
  4209. }
  4210. }
  4211. // We shouldn't have this weapon. Remove it.
  4212. Weapon_Detach( pWeapon );
  4213. UTIL_Remove( pWeapon );
  4214. }
  4215. else if ( bResetWeapons )
  4216. {
  4217. // We should have this weapon. Reset it.
  4218. pWeapon->ChangeTeam( GetTeamNumber() );
  4219. pWeapon->GiveDefaultAmmo();
  4220. pWeapon->ClearKillComboCount();
  4221. if ( m_bRegenerating == false )
  4222. {
  4223. pWeapon->WeaponReset();
  4224. }
  4225. else
  4226. {
  4227. pWeapon->WeaponRegenerate();
  4228. }
  4229. }
  4230. int nBuffType = 0;
  4231. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, nBuffType, set_buff_type );
  4232. if ( pWeapon->GetWeaponID() == TF_WEAPON_BUFF_ITEM || nBuffType )
  4233. {
  4234. bFoundBuffItem = true;
  4235. }
  4236. }
  4237. // Reenable sounds for all weapons
  4238. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4239. {
  4240. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(i);
  4241. if ( !pWeapon )
  4242. continue;
  4243. pWeapon->SetSoundsEnabled( true );
  4244. }
  4245. // Prevent a rage exploit with changing items outside of a spawn room
  4246. if ( ( IsPlayerClass( TF_CLASS_SOLDIER ) || IsPlayerClass( TF_CLASS_PYRO ) || IsPlayerClass( TF_CLASS_SNIPER ) ) && !bFoundBuffItem )
  4247. {
  4248. m_Shared.SetRageMeter( 0.0f );
  4249. }
  4250. }
  4251. //-----------------------------------------------------------------------------
  4252. // Purpose:
  4253. //-----------------------------------------------------------------------------
  4254. void CTFPlayer::ValidateWearables( TFPlayerClassData_t *pData )
  4255. {
  4256. CSteamID steamIDForPlayer;
  4257. GetSteamID( &steamIDForPlayer );
  4258. bool bIsDisguisedSpy = IsPlayerClass( TF_CLASS_SPY ) && m_Shared.InCond( TF_COND_DISGUISED );
  4259. // Need to move backwards because we'll be removing them as we find them.
  4260. for ( int wbl = m_hMyWearables.Count()-1; wbl >= 0; wbl-- )
  4261. {
  4262. CEconWearable *pWearable = m_hMyWearables[wbl];
  4263. Assert( pWearable );
  4264. if ( !pWearable )
  4265. {
  4266. // Integrity is failing, remove NULLs
  4267. m_hMyWearables.Remove( wbl );
  4268. continue;
  4269. }
  4270. CTFWearable *pTFWearable = assert_cast< CTFWearable* >( pWearable );
  4271. if ( bIsDisguisedSpy && pTFWearable->IsDisguiseWearable() )
  4272. continue;
  4273. bool itemMatch = false;
  4274. // If you are an extra wearable, just make sure your associated weapon is valid instead
  4275. CBaseEntity *pEntity = pTFWearable->GetWeaponAssociatedWith();
  4276. if ( pEntity )
  4277. {
  4278. CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( pTFWearable->GetWeaponAssociatedWith() );
  4279. int iLoadoutSlot = pWeapon->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( GetPlayerClass()->GetClassIndex() );
  4280. if (iLoadoutSlot >= 0 )
  4281. {
  4282. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( GetPlayerClass()->GetClassIndex(), iLoadoutSlot, &steamIDForPlayer );
  4283. itemMatch |= ItemsMatch( pData, pWeapon->GetAttributeContainer()->GetItem(), pItem );
  4284. }
  4285. }
  4286. else
  4287. {
  4288. // Regular Wearable
  4289. int iLoadoutSlot = pWearable->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( GetPlayerClass()->GetClassIndex() );
  4290. if ( iLoadoutSlot >= 0 )
  4291. {
  4292. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( GetPlayerClass()->GetClassIndex(), iLoadoutSlot, &steamIDForPlayer );
  4293. itemMatch |= ItemsMatch( pData, pWearable->GetAttributeContainer()->GetItem(), pItem );
  4294. // Item says what slot it wants to be in, but Misc's and Taunts can be in multiple places, check against all
  4295. bool bLoadoutMisc = iLoadoutSlot == LOADOUT_POSITION_MISC;
  4296. bool bLoadoutTaunt = iLoadoutSlot == LOADOUT_POSITION_TAUNT;
  4297. if ( bLoadoutMisc || bLoadoutTaunt )
  4298. {
  4299. for ( int i = LOADOUT_POSITION_INVALID + 1; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
  4300. {
  4301. if ( ( bLoadoutMisc && IsMiscSlot( i ) ) || ( bLoadoutTaunt && IsTauntSlot( i ) ) )
  4302. {
  4303. pItem = TFInventoryManager()->GetItemInLoadoutForClass( GetPlayerClass()->GetClassIndex(), i, &steamIDForPlayer );
  4304. itemMatch |= ItemsMatch( pData, pWearable->GetAttributeContainer()->GetItem(), pItem );
  4305. }
  4306. }
  4307. }
  4308. }
  4309. }
  4310. if ( !itemMatch || pWearable->GetTeamNumber() != GetTeamNumber() || m_bForceItemRemovalOnRespawn || m_bSwitchedClass )
  4311. {
  4312. if ( !pWearable->AlwaysAllow() )
  4313. {
  4314. // We shouldn't have this wearable. Remove it.
  4315. RemoveWearable( pWearable );
  4316. }
  4317. }
  4318. }
  4319. }
  4320. //-----------------------------------------------------------------------------
  4321. // Purpose:
  4322. //-----------------------------------------------------------------------------
  4323. void CTFPlayer::PostInventoryApplication( void )
  4324. {
  4325. m_Shared.RecalculatePlayerBodygroups();
  4326. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  4327. {
  4328. // Using weapons lockers destroys our disguise weapon, so we might need a new one.
  4329. m_Shared.DetermineDisguiseWeapon( false );
  4330. }
  4331. // Apply set bonuses.
  4332. ApplySetBonuses();
  4333. // Remove our disguise if we can't disguise.
  4334. if ( !CanDisguise() )
  4335. {
  4336. RemoveDisguise();
  4337. }
  4338. // Notify the client.
  4339. IGameEvent *event = gameeventmanager->CreateEvent( "post_inventory_application" );
  4340. if ( event )
  4341. {
  4342. event->SetInt( "userid", GetUserID() );
  4343. gameeventmanager->FireEvent( event );
  4344. }
  4345. // Iterate over all of our wearables
  4346. int iPlayerSkinOverride = 0;
  4347. for ( int i=0; i< GetNumWearables(); ++i )
  4348. {
  4349. CTFWearable *pWearable = dynamic_cast<CTFWearable *>( GetWearable( i ) );
  4350. if ( pWearable == NULL || pWearable->IsDisguiseWearable() )
  4351. continue;
  4352. // Check if we have an item that activates the skin override we want
  4353. // find first skin override
  4354. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWearable, iPlayerSkinOverride, player_skin_override );
  4355. if ( iPlayerSkinOverride != 0 ) // Zombie
  4356. {
  4357. break;
  4358. }
  4359. }
  4360. m_iPlayerSkinOverride = iPlayerSkinOverride;
  4361. m_Inventory.ClearClassLoadoutChangeTracking();
  4362. }
  4363. //-----------------------------------------------------------------------------
  4364. // Purpose:
  4365. //-----------------------------------------------------------------------------
  4366. void CTFPlayer::ManageRegularWeaponsLegacy( TFPlayerClassData_t *pData )
  4367. {
  4368. CSteamID steamIDForPlayer;
  4369. GetSteamID( &steamIDForPlayer );
  4370. for ( int iWeapon = 0; iWeapon < TF_PLAYER_WEAPON_COUNT; ++iWeapon )
  4371. {
  4372. if ( pData->m_aWeapons[iWeapon] != TF_WEAPON_NONE )
  4373. {
  4374. int iWeaponID = pData->m_aWeapons[iWeapon];
  4375. const char *pszWeaponName = WeaponIdToAlias( iWeaponID );
  4376. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon( iWeapon );
  4377. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pszWeaponName );
  4378. Assert( hWpnInfo != GetInvalidWeaponInfoHandle() );
  4379. CTFWeaponInfo *pWeaponInfo = dynamic_cast<CTFWeaponInfo*>( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  4380. int iLoadoutSlot = pWeaponInfo->m_iWeaponType;
  4381. // HACK: Convert engineer's second PDA to using the second pda slot
  4382. if ( iWeaponID == TF_WEAPON_PDA_ENGINEER_DESTROY || iWeaponID == TF_WEAPON_INVIS )
  4383. {
  4384. iLoadoutSlot = LOADOUT_POSITION_PDA2;
  4385. }
  4386. #ifdef STAGING_ONLY
  4387. if ( iWeaponID == TF_WEAPON_PDA_SPY_BUILD )
  4388. {
  4389. iLoadoutSlot = LOADOUT_POSITION_PDA3;
  4390. }
  4391. #endif
  4392. // Do we have a custom weapon in this slot?
  4393. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( GetPlayerClass()->GetClassIndex(), iLoadoutSlot, &steamIDForPlayer );
  4394. bool bHasNonBaseWeapon = pItem ? pItem->GetItemQuality() != AE_NORMAL : false;
  4395. if ( pWeapon )
  4396. {
  4397. bool bShouldRemove = false;
  4398. if ( pItem )
  4399. {
  4400. // If the item isn't the one we're supposed to have, nuke it
  4401. if ( pWeapon->GetAttributeContainer()->GetItem()->GetItemID() != pItem->GetItemID() )
  4402. {
  4403. bShouldRemove = true;
  4404. /*
  4405. Msg("Removing %s because its global index (%d) doesn't match the loadout's (%d)\n", pWeapon->GetDebugName(),
  4406. pWeapon->GetAttributeContainer()->GetItem()->GetItemID(),
  4407. pItem->GetItemID() );
  4408. */
  4409. }
  4410. }
  4411. else
  4412. {
  4413. // We should have a base item in our loadout.
  4414. if ( pWeapon->GetAttributeContainer()->GetItem()->GetItemQuality() != AE_NORMAL )
  4415. {
  4416. bShouldRemove = true;
  4417. //Msg("Removing %s because it's a non-base item, and the loadout specifies a base item.\n", pWeapon->GetDebugName() );
  4418. }
  4419. }
  4420. // If we already have a weapon in this slot but is not the same type, nuke it (changed classes)
  4421. // We don't do this if the weapon in this slot isn't a base item, because items like the flaregun
  4422. // don't have matching weaponIDs, yet they shouldn't be removed. The inventory system has already
  4423. // ensured that the weapon is valid in this slot.
  4424. if ( !bShouldRemove && pWeapon->GetWeaponID() != iWeaponID && !bHasNonBaseWeapon )
  4425. {
  4426. bShouldRemove = true;
  4427. //Msg("Removing %s because it's not the right type for the class.\n", pWeapon->GetDebugName() );
  4428. }
  4429. if ( bShouldRemove )
  4430. {
  4431. Weapon_Detach( pWeapon );
  4432. UTIL_Remove( pWeapon );
  4433. pWeapon = NULL;
  4434. }
  4435. }
  4436. if ( !bHasNonBaseWeapon )
  4437. {
  4438. pWeapon = dynamic_cast<CTFWeaponBase*>(Weapon_OwnsThisID( iWeaponID ));
  4439. }
  4440. if ( pWeapon )
  4441. {
  4442. Assert( pWeapon->GetAttributeContainer()->GetItem()->GetItemID() == ( pItem ? pItem->GetItemID() : INVALID_ITEM_ID ) );
  4443. pWeapon->ChangeTeam( GetTeamNumber() );
  4444. pWeapon->GiveDefaultAmmo();
  4445. if ( m_bRegenerating == false )
  4446. {
  4447. pWeapon->WeaponReset();
  4448. }
  4449. //char tempstr[1024];
  4450. //g_pVGuiLocalize->ConvertUnicodeToANSI( pWeapon->GetAttributeContainer()->GetItem()->GetItemName(), tempstr, sizeof(tempstr) );
  4451. //Msg("Updated %s for %s\n", tempstr, GetPlayerName() );
  4452. }
  4453. else
  4454. {
  4455. CEconEntity* pNewItem = dynamic_cast<CEconEntity*>(GiveNamedItem( pszWeaponName, 0, pItem ));
  4456. Assert( pNewItem );
  4457. if ( pNewItem )
  4458. {
  4459. //char tempstr[1024];
  4460. //g_pVGuiLocalize->ConvertUnicodeToANSI( pWeapon->GetAttributeContainer()->GetItem()->GetItemName(), tempstr, sizeof(tempstr) );
  4461. //Msg("Created %s for %s\n", tempstr, GetPlayerName() );
  4462. //pWeapon->DebugDescribe();
  4463. pNewItem->GiveTo( this );
  4464. }
  4465. }
  4466. }
  4467. else
  4468. {
  4469. //I shouldn't have any weapons in this slot, so get rid of it
  4470. CTFWeaponBase *pCarriedWeapon = (CTFWeaponBase *)GetWeapon( iWeapon );
  4471. //Don't nuke builders since they will be nuked if we don't need them later.
  4472. if ( pCarriedWeapon && pCarriedWeapon->GetWeaponID() != TF_WEAPON_BUILDER )
  4473. {
  4474. Weapon_Detach( pCarriedWeapon );
  4475. UTIL_Remove( pCarriedWeapon );
  4476. }
  4477. }
  4478. }
  4479. // If we lack a primary or secondary weapon, start with our melee weapon ready.
  4480. // This is for supporting new unlockables that take up weapon slots and leave our character with nothing to wield.
  4481. int iMainWeaponCount = 0;
  4482. CTFWeaponBase* pMeleeWeapon = NULL;
  4483. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4484. {
  4485. CTFWeaponBase *pWeapon = (CTFWeaponBase*) GetWeapon(i);
  4486. if ( pWeapon == NULL )
  4487. continue;
  4488. if ( pWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_PRIMARY ||
  4489. pWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_SECONDARY )
  4490. {
  4491. ++iMainWeaponCount;
  4492. }
  4493. else if ( pWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_MELEE )
  4494. {
  4495. pMeleeWeapon = pWeapon;
  4496. }
  4497. }
  4498. if ( pMeleeWeapon && (iMainWeaponCount==0) )
  4499. {
  4500. Weapon_Switch( pMeleeWeapon );
  4501. }
  4502. }
  4503. //-----------------------------------------------------------------------------
  4504. // Purpose: Create and give the named item to the player. Then return it.
  4505. //-----------------------------------------------------------------------------
  4506. CBaseEntity *CTFPlayer::GiveNamedItem( const char *pszName, int iSubType, const CEconItemView *pScriptItem, bool bForce )
  4507. {
  4508. // We need to support players putting any shotgun into a shotgun slot, pistol into a pistol slot, etc.
  4509. // For legacy reasons, different classes actually spawn different entities for their shotguns/pistols/etc.
  4510. // To deal with this, we translate entities into the right one for the class we're playing.
  4511. if ( !bForce )
  4512. {
  4513. // We don't do this if force is set, since a spy might be disguising as this character, etc.
  4514. pszName = TranslateWeaponEntForClass( pszName, GetPlayerClass()->GetClassIndex() );
  4515. }
  4516. if ( !pszName )
  4517. return NULL;
  4518. // If I already own this type don't create one
  4519. if ( Weapon_OwnsThisType(pszName, iSubType) && !bForce)
  4520. {
  4521. Assert(0);
  4522. return NULL;
  4523. }
  4524. CBaseEntity *pItem = NULL;
  4525. if ( pScriptItem )
  4526. {
  4527. // Generate a weapon directly from that item
  4528. pItem = ItemGeneration()->GenerateItemFromScriptData( pScriptItem, GetLocalOrigin(), vec3_angle, pszName );
  4529. }
  4530. else
  4531. {
  4532. // Generate a base item of the specified type
  4533. CItemSelectionCriteria criteria;
  4534. criteria.SetQuality( AE_NORMAL );
  4535. criteria.BAddCondition( "name", k_EOperator_String_EQ, pszName, true );
  4536. pItem = ItemGeneration()->GenerateRandomItem( &criteria, GetAbsOrigin(), vec3_angle );
  4537. }
  4538. if ( pItem == NULL )
  4539. {
  4540. Msg( "Failed to generate base item: %s\n", pszName );
  4541. return NULL;
  4542. }
  4543. pItem->AddSpawnFlags( SF_NORESPAWN );
  4544. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pItem );
  4545. if ( pWeapon )
  4546. {
  4547. pWeapon->SetSubType( iSubType );
  4548. }
  4549. DispatchSpawn( pItem );
  4550. if ( pItem != NULL && !(pItem->IsMarkedForDeletion()) )
  4551. {
  4552. pItem->Touch( this );
  4553. }
  4554. return pItem;
  4555. }
  4556. //-----------------------------------------------------------------------------
  4557. // Purpose: Destroy all attributes on this player that match the bSetBonuses flag
  4558. //-----------------------------------------------------------------------------
  4559. void CTFPlayer::RemovePlayerAttributes( bool bSetBonuses )
  4560. {
  4561. const int iAttribs = m_AttributeList.GetNumAttributes();
  4562. for ( int i = iAttribs-1; i >= 0; i-- )
  4563. {
  4564. const CEconItemAttribute *pAttrib = m_AttributeList.GetAttribute(i);
  4565. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( pAttrib->GetAttribIndex() );
  4566. if ( !pAttrDef || (pAttrDef->BIsSetBonusAttribute() == bSetBonuses) )
  4567. {
  4568. m_AttributeList.RemoveAttributeByIndex( i );
  4569. }
  4570. }
  4571. GetAttributeManager()->OnAttributeValuesChanged();
  4572. }
  4573. //-----------------------------------------------------------------------------
  4574. // Purpose:
  4575. //-----------------------------------------------------------------------------
  4576. void CTFPlayer::ApplySetBonuses( void )
  4577. {
  4578. RemovePlayerAttributes( true );
  4579. CUtlVector<const CEconItemSetDefinition *> pActiveSets;
  4580. GetActiveSets( &pActiveSets );
  4581. FOR_EACH_VEC( pActiveSets, set )
  4582. {
  4583. for ( int i = 0; i < pActiveSets[set]->m_iAttributes.Count(); i++ )
  4584. {
  4585. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( pActiveSets[set]->m_iAttributes[i].m_iAttribDefIndex );
  4586. if ( pAttrDef )
  4587. {
  4588. Assert( pAttrDef->GetAttributeType() );
  4589. Assert( pAttrDef->GetAttributeType()->BSupportsGameplayModificationAndNetworking() ); // is an assert instead of a check because we're in client code here -- this means someone set up a set with bad data
  4590. Assert( pAttrDef->BIsSetBonusAttribute() );
  4591. float flAttrValue = pActiveSets[set]->m_iAttributes[i].m_flValue;
  4592. GetAttributeList()->SetRuntimeAttributeValue( pAttrDef, flAttrValue );
  4593. }
  4594. }
  4595. }
  4596. }
  4597. #ifdef TF_RAID_MODE
  4598. //-----------------------------------------------------------------------------
  4599. // Return true if the given entity can be used by a dead Raider
  4600. // as a respawn point in Raid mode.
  4601. bool IsValidRaidRespawnTarget( CBaseEntity *entity )
  4602. {
  4603. if ( !entity->IsPlayer() )
  4604. {
  4605. CObjectSentrygun *pSentry = dynamic_cast< CObjectSentrygun* >( entity );
  4606. if ( pSentry && pSentry->GetTeamNumber() == TF_TEAM_BLUE )
  4607. {
  4608. if ( pSentry->GetOwner() && !pSentry->GetOwner()->IsBot() )
  4609. {
  4610. return true;
  4611. }
  4612. }
  4613. return false;
  4614. }
  4615. if ( entity->GetTeamNumber() != TF_TEAM_BLUE )
  4616. return false;
  4617. CTFPlayer *player = ToTFPlayer( entity );
  4618. CTFBot *bot = ToTFBot( player );
  4619. return !bot || !bot->HasAttribute( CTFBot::IS_NPC );
  4620. }
  4621. #endif // TF_RAID_MODE
  4622. //-----------------------------------------------------------------------------
  4623. // Purpose: Find a spawn point for the player.
  4624. //-----------------------------------------------------------------------------
  4625. CBaseEntity* CTFPlayer::EntSelectSpawnPoint()
  4626. {
  4627. CBaseEntity *pSpot = g_pLastSpawnPoints[ GetTeamNumber() ];
  4628. const char *pSpawnPointName = "";
  4629. #ifdef TF_RAID_MODE
  4630. if ( TFGameRules()->IsRaidMode() )
  4631. {
  4632. if ( GetTeamNumber() == TF_TEAM_BLUE )
  4633. {
  4634. // only spawn next to friends if the round is not restarting
  4635. if ( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING )
  4636. {
  4637. if ( tf_raid_use_rescue_closets.GetBool() )
  4638. {
  4639. // find a valid rescue closet to spawn into
  4640. CBaseEntity *rescueSpawn = g_pRaidLogic->GetRescueRespawn();
  4641. if ( rescueSpawn )
  4642. {
  4643. return rescueSpawn;
  4644. }
  4645. }
  4646. else if ( tf_boss_battle_respawn_on_friends.GetBool() )
  4647. {
  4648. // the raiders are in the wild - respawn next to them
  4649. float timeSinceInjured = -1.0f;
  4650. CBaseEntity *respawnEntity = NULL;
  4651. // if we are observing a friend, spawn into them
  4652. CBaseEntity *watchEntity = GetObserverTarget();
  4653. if ( watchEntity && IsValidRaidRespawnTarget( watchEntity ) )
  4654. {
  4655. respawnEntity = watchEntity;
  4656. }
  4657. else
  4658. {
  4659. // spawn on the least recently damaged friend
  4660. CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE );
  4661. for( int i=0; i<raidingTeam->GetNumPlayers(); ++i )
  4662. {
  4663. CTFPlayer *buddy = (CTFPlayer *)raidingTeam->GetPlayer(i);
  4664. // we can't use IsAlive(), because that has already been reset since
  4665. // this code is mid-spawn. Use m_Shared state instead.
  4666. if ( buddy != this && buddy->m_Shared.InState( TF_STATE_ACTIVE ) && IsValidRaidRespawnTarget( buddy ) )
  4667. {
  4668. // pick the friend who has been hurt least recently
  4669. if ( buddy->GetTimeSinceLastInjury( TF_TEAM_RED ) > timeSinceInjured )
  4670. {
  4671. timeSinceInjured = buddy->GetTimeSinceLastInjury( TF_TEAM_RED );
  4672. respawnEntity = buddy;
  4673. }
  4674. }
  4675. }
  4676. }
  4677. if ( respawnEntity )
  4678. {
  4679. CPVSFilter filter( respawnEntity->GetAbsOrigin() );
  4680. TE_TFParticleEffect( filter, 0.0, "teleported_blue", respawnEntity->GetAbsOrigin(), vec3_angle );
  4681. TE_TFParticleEffect( filter, 0.0, "player_sparkles_blue", respawnEntity->GetAbsOrigin(), vec3_angle, this, PATTACH_POINT );
  4682. return respawnEntity;
  4683. }
  4684. }
  4685. }
  4686. }
  4687. }
  4688. #endif // TF_RAID_MODE
  4689. bool bMatchSummary = TFGameRules() && TFGameRules()->ShowMatchSummary();
  4690. // See if the map is asking to force this player to spawn at a specific location
  4691. if ( GetRespawnLocationOverride() && !bMatchSummary )
  4692. {
  4693. if ( SelectSpawnSpotByName( GetRespawnLocationOverride(), pSpot ) )
  4694. {
  4695. m_pSpawnPoint = dynamic_cast< CTFTeamSpawn* >( pSpot ); // Is this even used anymore?
  4696. return pSpot;
  4697. }
  4698. // If the entity doesn't exist - or isn't valid - let the regular system handle it
  4699. }
  4700. switch( GetTeamNumber() )
  4701. {
  4702. case TF_TEAM_RED:
  4703. case TF_TEAM_BLUE:
  4704. {
  4705. pSpawnPointName = "info_player_teamspawn";
  4706. if ( SelectSpawnSpotByType( pSpawnPointName, pSpot ) )
  4707. {
  4708. g_pLastSpawnPoints[ GetTeamNumber() ] = pSpot;
  4709. }
  4710. else if ( pSpot )
  4711. {
  4712. int iClass = GetPlayerClass()->GetClassIndex();
  4713. if ( iClass >= 0 && iClass < ARRAYSIZE( g_aPlayerClassNames ) )
  4714. {
  4715. Warning( "EntSelectSpawnPoint(): No valid spawns for class %s on team %i found, even though at least one spawn entity exists.\n", g_aPlayerClassNames[iClass], GetTeamNumber() );
  4716. }
  4717. }
  4718. // need to save this for later so we can apply and modifiers to the armor and grenades...after the call to InitClass()
  4719. m_pSpawnPoint = dynamic_cast<CTFTeamSpawn*>( pSpot );
  4720. break;
  4721. }
  4722. case TEAM_SPECTATOR:
  4723. case TEAM_UNASSIGNED:
  4724. default:
  4725. {
  4726. pSpot = CBaseEntity::Instance( INDEXENT(0) );
  4727. break;
  4728. }
  4729. }
  4730. if ( !pSpot )
  4731. {
  4732. Warning( "PutClientInServer: no %s on level\n", pSpawnPointName );
  4733. return CBaseEntity::Instance( INDEXENT(0) );
  4734. }
  4735. return pSpot;
  4736. }
  4737. //-----------------------------------------------------------------------------
  4738. // Purpose:
  4739. //-----------------------------------------------------------------------------
  4740. bool CTFPlayer::SelectSpawnSpotByType( const char *pEntClassName, CBaseEntity* &pSpot )
  4741. {
  4742. bool bMatchSummary = TFGameRules()->ShowMatchSummary();
  4743. CBaseEntity *pMatchSummaryFallback = NULL;
  4744. // Get an initial spawn point.
  4745. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4746. if ( !pSpot )
  4747. {
  4748. // Sometimes the first spot can be NULL????
  4749. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4750. }
  4751. // First we try to find a spawn point that is fully clear. If that fails,
  4752. // we look for a spawn point that's clear except for another players. We
  4753. // don't collide with our team members, so we should be fine.
  4754. bool bIgnorePlayers = false;
  4755. // When dealing with a standard spawn ent, try to obey any class spawn flags
  4756. bool bRestrictByClass = !V_strcmp( pEntClassName, "info_player_teamspawn" );
  4757. CBaseEntity *pFirstSpot = pSpot;
  4758. do
  4759. {
  4760. if ( pSpot )
  4761. {
  4762. // Check to see if this is a valid team spawn (player is on this team, etc.).
  4763. if ( TFGameRules()->IsSpawnPointValid( pSpot, this, bIgnorePlayers ) )
  4764. {
  4765. // Check for a bad spawn entity.
  4766. if ( pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
  4767. {
  4768. goto next_spawn_point;
  4769. }
  4770. // SpawnFlags were only recently added to the .fgd (Feb 2016), which means older maps won't have any flags at all (they default to on).
  4771. // So this means we only look for restrictions when we find flags, which a map compiled after this change would/should have.
  4772. else if ( bRestrictByClass && pSpot->GetSpawnFlags() )
  4773. {
  4774. int nClass = GetPlayerClass()->GetClassIndex() - 1;
  4775. if ( !pSpot->HasSpawnFlags( ( 1 << nClass ) ) )
  4776. {
  4777. goto next_spawn_point;
  4778. }
  4779. }
  4780. // Found a valid spawn point.
  4781. return true;
  4782. }
  4783. }
  4784. next_spawn_point:;
  4785. // Let's save off a fallback spot for competitive mode
  4786. if ( bMatchSummary && !pMatchSummaryFallback )
  4787. {
  4788. CTFTeamSpawn *pCTFSpawn = dynamic_cast<CTFTeamSpawn*>( pSpot );
  4789. if ( pCTFSpawn )
  4790. {
  4791. if ( ( pCTFSpawn->GetTeamNumber() == pCTFSpawn->GetTeamNumber() ) && ( pCTFSpawn->GetMatchSummaryType() == PlayerTeamSpawn_MatchSummary_None ) )
  4792. {
  4793. pMatchSummaryFallback = pCTFSpawn;
  4794. }
  4795. }
  4796. }
  4797. // Get the next spawning point to check.
  4798. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4799. // Exhausted the list
  4800. if ( pSpot == pFirstSpot )
  4801. {
  4802. // Loop through again, ignoring class restrictions (but check against players)
  4803. if ( bRestrictByClass )
  4804. {
  4805. bRestrictByClass = false;
  4806. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4807. }
  4808. // Loop through again, ignoring players and classes
  4809. else if ( !bRestrictByClass && !bIgnorePlayers )
  4810. {
  4811. bIgnorePlayers = true;
  4812. pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
  4813. }
  4814. }
  4815. }
  4816. // Continue until a valid spawn point is found or we hit the start.
  4817. while ( pSpot != pFirstSpot );
  4818. // Return a fallback spot for competitive mode
  4819. if ( bMatchSummary && pMatchSummaryFallback )
  4820. {
  4821. pSpot = pMatchSummaryFallback;
  4822. return true;
  4823. }
  4824. return false;
  4825. }
  4826. //-----------------------------------------------------------------------------
  4827. // Purpose: We're being asked to use a spawn with a specific name
  4828. //-----------------------------------------------------------------------------
  4829. bool CTFPlayer::SelectSpawnSpotByName( const char *pEntName, CBaseEntity* &pSpot )
  4830. {
  4831. if ( pEntName && pEntName[0] )
  4832. {
  4833. pSpot = gEntList.FindEntityByName( pSpot, pEntName );
  4834. while ( pSpot )
  4835. {
  4836. if ( TFGameRules()->IsSpawnPointValid( pSpot, this, true, PlayerTeamSpawnMode_Triggered ) )
  4837. return true;
  4838. pSpot = gEntList.FindEntityByName( pSpot, pEntName );
  4839. }
  4840. }
  4841. return false;
  4842. }
  4843. //-----------------------------------------------------------------------------
  4844. // Purpose:
  4845. //-----------------------------------------------------------------------------
  4846. void CTFPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  4847. {
  4848. MDLCACHE_CRITICAL_SECTION();
  4849. m_PlayerAnimState->DoAnimationEvent( event, nData );
  4850. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  4851. }
  4852. //-----------------------------------------------------------------------------
  4853. // Purpose:
  4854. //-----------------------------------------------------------------------------
  4855. void CTFPlayer::HandleAnimEvent( animevent_t *pEvent )
  4856. {
  4857. if ( pEvent->event == AE_TAUNT_ENABLE_MOVE )
  4858. {
  4859. m_bAllowMoveDuringTaunt = true;
  4860. }
  4861. else if ( pEvent->event == AE_TAUNT_DISABLE_MOVE )
  4862. {
  4863. m_bAllowMoveDuringTaunt = false;
  4864. }
  4865. else if ( pEvent->event == AE_WPN_HIDE )
  4866. {
  4867. // does nothing for now.
  4868. }
  4869. else
  4870. BaseClass::HandleAnimEvent( pEvent );
  4871. }
  4872. //-----------------------------------------------------------------------------
  4873. // Purpose:
  4874. //-----------------------------------------------------------------------------
  4875. void CTFPlayer::PhysObjectSleep()
  4876. {
  4877. IPhysicsObject *pObj = VPhysicsGetObject();
  4878. if ( pObj )
  4879. pObj->Sleep();
  4880. }
  4881. //-----------------------------------------------------------------------------
  4882. // Purpose:
  4883. //-----------------------------------------------------------------------------
  4884. void CTFPlayer::PhysObjectWake()
  4885. {
  4886. IPhysicsObject *pObj = VPhysicsGetObject();
  4887. if ( pObj )
  4888. pObj->Wake();
  4889. }
  4890. //-----------------------------------------------------------------------------
  4891. // Purpose:
  4892. //-----------------------------------------------------------------------------
  4893. int CTFPlayer::GetAutoTeam( int nPreferedTeam /*= TF_TEAM_AUTOASSIGN*/ )
  4894. {
  4895. int iTeam = TEAM_SPECTATOR;
  4896. CTFTeam *pBlue = TFTeamMgr()->GetTeam( TF_TEAM_BLUE );
  4897. CTFTeam *pRed = TFTeamMgr()->GetTeam( TF_TEAM_RED );
  4898. if ( pBlue && pRed )
  4899. {
  4900. if ( TFGameRules() )
  4901. {
  4902. if ( TFGameRules()->IsInHighlanderMode() )
  4903. {
  4904. if ( ( pBlue->GetNumPlayers() >= TF_LAST_NORMAL_CLASS - 1 ) &&
  4905. ( pRed->GetNumPlayers() >= TF_LAST_NORMAL_CLASS - 1 ) )
  4906. {
  4907. // teams are full....join team Spectator for now
  4908. return TEAM_SPECTATOR;
  4909. }
  4910. }
  4911. bool bReturnDefenders = false;
  4912. #ifdef TF_RAID_MODE
  4913. if ( TFGameRules()->IsBossBattleMode() )
  4914. {
  4915. bReturnDefenders = true;
  4916. }
  4917. #endif // TF_RAID_MODE
  4918. if ( TFGameRules()->IsMannVsMachineMode() )
  4919. {
  4920. bReturnDefenders = true;
  4921. }
  4922. if ( bReturnDefenders )
  4923. {
  4924. // If joining a MVM game that's in-progress, give us the max per-player collected value
  4925. if ( TFGameRules()->IsMannVsMachineMode() && g_pPopulationManager )
  4926. {
  4927. int nRoundCurrency = MannVsMachineStats_GetAcquiredCredits();
  4928. nRoundCurrency += g_pPopulationManager->GetStartingCurrency();
  4929. // Check to see if this player has an upgrade history and apply it to them
  4930. // deduct any cash that has already been spent
  4931. int spentCurrency = g_pPopulationManager->GetPlayerCurrencySpent( this );
  4932. if ( m_nCurrency < nRoundCurrency )
  4933. {
  4934. SetCurrency( nRoundCurrency - spentCurrency );
  4935. }
  4936. if ( g_pPopulationManager )
  4937. {
  4938. // See if the team's earned any respec credits
  4939. if ( TFGameRules()->IsMannVsMachineRespecEnabled() && !g_pPopulationManager->GetNumRespecsAvailableForPlayer( this ) )
  4940. {
  4941. uint16 nRespecs = g_pPopulationManager->GetNumRespecsEarned();
  4942. if ( nRespecs )
  4943. {
  4944. g_pPopulationManager->SetNumRespecsForPlayer( this, nRespecs );
  4945. }
  4946. }
  4947. // Set buyback credits - if they aren't reconnecting
  4948. if ( !g_pPopulationManager->IsPlayerBeingTrackedForBuybacks( this ) )
  4949. {
  4950. g_pPopulationManager->SetBuybackCreditsForPlayer( this, tf_mvm_buybacks_per_wave.GetInt() );
  4951. }
  4952. }
  4953. }
  4954. return TFGameRules()->GetTeamAssignmentOverride( this, TF_TEAM_PVE_DEFENDERS );
  4955. }
  4956. }
  4957. CTFBot *pPlayerBot = dynamic_cast<CTFBot*>( this );
  4958. if ( FStrEq( tf_bot_quota_mode.GetString(), "fill" ) && ( tf_bot_quota.GetInt() > 0 ) && !( pPlayerBot && pPlayerBot->HasAttribute( CTFBot::QUOTA_MANANGED ) ) )
  4959. {
  4960. // We're using 'tf_bot_quota_mode fill' to keep the teams even so balance based on the human players on each team
  4961. int nPlayerCountRed = 0;
  4962. int nPlayerCountBlue = 0;
  4963. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  4964. {
  4965. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  4966. if ( pPlayer == NULL )
  4967. continue;
  4968. if ( FNullEnt( pPlayer->edict() ) )
  4969. continue;
  4970. if ( !pPlayer->IsConnected() )
  4971. continue;
  4972. if ( !pPlayer->IsPlayer() )
  4973. continue;
  4974. CTFBot* pBot = dynamic_cast<CTFBot*>( pPlayer );
  4975. if ( pBot && pBot->HasAttribute( CTFBot::QUOTA_MANANGED ) )
  4976. continue;
  4977. if ( pPlayer->GetTeamNumber() == TF_TEAM_RED )
  4978. {
  4979. nPlayerCountRed++;
  4980. }
  4981. else if( pPlayer->GetTeamNumber() == TF_TEAM_BLUE )
  4982. {
  4983. nPlayerCountBlue++;
  4984. }
  4985. }
  4986. if ( nPlayerCountRed < nPlayerCountBlue )
  4987. {
  4988. iTeam = TF_TEAM_RED;
  4989. }
  4990. else if ( nPlayerCountBlue < nPlayerCountRed )
  4991. {
  4992. iTeam = TF_TEAM_BLUE;
  4993. }
  4994. else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT || pRed->GetRole() == TEAM_ROLE_DEFENDERS )
  4995. {
  4996. // AutoTeam should give new players to the attackers on A/D maps if the teams are even
  4997. iTeam = TF_TEAM_BLUE;
  4998. }
  4999. else
  5000. {
  5001. // teams have an even number of human players, pick a random team
  5002. iTeam = RandomInt( 0, 1 ) ? TF_TEAM_RED : TF_TEAM_BLUE;
  5003. }
  5004. bool bKick = false;
  5005. // Now we have a team we want to join to balance the human players, can we join it?
  5006. if ( iTeam == TF_TEAM_RED )
  5007. {
  5008. if ( pBlue->GetNumPlayers() < pRed->GetNumPlayers() )
  5009. {
  5010. bKick = true;
  5011. }
  5012. }
  5013. else
  5014. {
  5015. if ( pRed->GetNumPlayers() < pBlue->GetNumPlayers() )
  5016. {
  5017. bKick = true;
  5018. }
  5019. }
  5020. if ( !bKick || TheTFBots().RemoveBotFromTeamAndKick( iTeam ) )
  5021. {
  5022. return iTeam;
  5023. }
  5024. // If kick needed but failed, fall through to default logic
  5025. }
  5026. if ( pBlue->GetNumPlayers() < pRed->GetNumPlayers() )
  5027. {
  5028. iTeam = TF_TEAM_BLUE;
  5029. }
  5030. else if ( pRed->GetNumPlayers() < pBlue->GetNumPlayers() )
  5031. {
  5032. iTeam = TF_TEAM_RED;
  5033. }
  5034. else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_ESCORT || pRed->GetRole() == TEAM_ROLE_DEFENDERS )
  5035. {
  5036. // AutoTeam should give new players to the attackers on A/D maps if the teams are even
  5037. iTeam = TF_TEAM_BLUE;
  5038. }
  5039. else
  5040. {
  5041. if ( nPreferedTeam == TF_TEAM_AUTOASSIGN )
  5042. {
  5043. iTeam = RandomInt( 0, 1 ) ? TF_TEAM_RED : TF_TEAM_BLUE;
  5044. }
  5045. else
  5046. {
  5047. Assert( nPreferedTeam >= FIRST_GAME_TEAM );
  5048. iTeam = nPreferedTeam;
  5049. }
  5050. }
  5051. }
  5052. return iTeam;
  5053. }
  5054. //-----------------------------------------------------------------------------
  5055. // Purpose:
  5056. //-----------------------------------------------------------------------------
  5057. bool CTFPlayer::ShouldForceAutoTeam( void )
  5058. {
  5059. if ( mp_forceautoteam.GetBool() )
  5060. return true;
  5061. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  5062. return true;
  5063. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  5064. return true;
  5065. bool bForce = false;
  5066. // On official servers, and in normal game modes, see if we should re-assign returning players
  5067. if ( TFGameRules() && TFGameRules()->IsDefaultGameMode() )
  5068. {
  5069. int nTimeSinceLast = TFGameRules()->PlayerHistory_GetTimeSinceLastSeen( this );
  5070. bForce = ( tf_mm_trusted.GetBool() && nTimeSinceLast > 0 && nTimeSinceLast < 60 );
  5071. }
  5072. return bForce;
  5073. }
  5074. //-----------------------------------------------------------------------------
  5075. // Purpose:
  5076. //-----------------------------------------------------------------------------
  5077. void CTFPlayer::HandleCommand_JoinTeam( const char *pTeamName )
  5078. {
  5079. if ( TFGameRules()->State_Get() == GR_STATE_GAME_OVER )
  5080. return;
  5081. if ( GetTeamNumber() == TF_TEAM_RED || GetTeamNumber() == TF_TEAM_BLUE )
  5082. {
  5083. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  5084. if ( pMatchDesc && !pMatchDesc->m_params.m_bAllowTeamChange )
  5085. {
  5086. ClientPrint( this, HUD_PRINTCENTER, "#TF_Ladder_NoTeamChange" );
  5087. return;
  5088. }
  5089. #ifdef STAGING_ONLY
  5090. else if ( TFGameRules()->ArePlayersInHell() )
  5091. #else
  5092. else if ( TFGameRules()->ArePlayersInHell() || TFGameRules()->IsPowerupMode() )
  5093. #endif // STAGING_ONLY
  5094. {
  5095. ClientPrint( this, HUD_PRINTCENTER, "#TF_CantChangeTeamNow" );
  5096. return;
  5097. }
  5098. }
  5099. bool bAutoTeamed = false;
  5100. bool bArenaSpectator = false;
  5101. int iTeam = TF_TEAM_RED;
  5102. if ( stricmp( pTeamName, "auto" ) == 0 )
  5103. {
  5104. iTeam = GetAutoTeam();
  5105. bAutoTeamed = true;
  5106. }
  5107. else if ( stricmp( pTeamName, "spectate" ) == 0 )
  5108. {
  5109. iTeam = TEAM_SPECTATOR;
  5110. }
  5111. else if ( stricmp( pTeamName, "spectatearena" ) == 0 )
  5112. {
  5113. iTeam = TEAM_SPECTATOR;
  5114. if ( mp_allowspectators.GetBool() == true )
  5115. {
  5116. bArenaSpectator = true;
  5117. }
  5118. }
  5119. else
  5120. {
  5121. for ( int i = 0; i < TF_TEAM_COUNT; ++i )
  5122. {
  5123. COMPILE_TIME_ASSERT( TF_TEAM_COUNT == ARRAYSIZE( g_aTeamNames ) );
  5124. if ( stricmp( pTeamName, g_aTeamNames[i] ) == 0 )
  5125. {
  5126. iTeam = i;
  5127. break;
  5128. }
  5129. }
  5130. }
  5131. // now check if we're limited in our team selection (unless we want to be on the spectator team)
  5132. if ( !IsBot() && iTeam != TEAM_SPECTATOR )
  5133. {
  5134. int iHumanTeam = TFGameRules()->GetAssignedHumanTeam();
  5135. if ( iHumanTeam != TEAM_ANY )
  5136. {
  5137. iTeam = iHumanTeam;
  5138. bAutoTeamed = true;
  5139. }
  5140. }
  5141. // invalid team selection
  5142. if ( iTeam < TEAM_SPECTATOR )
  5143. {
  5144. return;
  5145. }
  5146. if ( IsCoaching() && ( iTeam != TEAM_SPECTATOR ) )
  5147. return;
  5148. #ifdef TF_RAID_MODE
  5149. if ( TFGameRules()->IsRaidMode() )
  5150. {
  5151. if ( !IsBot() && iTeam != TEAM_SPECTATOR )
  5152. {
  5153. // human raiders can only be on the blue team
  5154. CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE );
  5155. int humanCount = 0;
  5156. for( int i=0; i<raidingTeam->GetNumPlayers(); ++i )
  5157. {
  5158. if ( raidingTeam->GetPlayer(i)->IsBot() )
  5159. continue;
  5160. ++humanCount;
  5161. }
  5162. if ( humanCount < tf_raid_team_size.GetInt() )
  5163. {
  5164. iTeam = TF_TEAM_BLUE;
  5165. }
  5166. else
  5167. {
  5168. // no room
  5169. iTeam = TEAM_SPECTATOR;
  5170. }
  5171. }
  5172. }
  5173. if ( TFGameRules()->IsBossBattleMode() )
  5174. {
  5175. if ( !IsBot() && iTeam != TEAM_SPECTATOR )
  5176. {
  5177. // players can only be on the blue team
  5178. if ( GetGlobalTeam( TF_TEAM_BLUE )->GetNumPlayers() < tf_boss_battle_team_size.GetInt() )
  5179. {
  5180. iTeam = TF_TEAM_BLUE;
  5181. }
  5182. else
  5183. {
  5184. // no room
  5185. iTeam = TEAM_SPECTATOR;
  5186. }
  5187. }
  5188. DuelMiniGame_NotifyPlayerChangedTeam( this, iTeam, true );
  5189. ChangeTeam( iTeam, true );
  5190. return;
  5191. }
  5192. #endif // TF_RAID_MODE
  5193. // Some game modes will overrule our player-based logic
  5194. iTeam = TFGameRules()->GetTeamAssignmentOverride( this, iTeam );
  5195. if ( iTeam == TEAM_SPECTATOR || ( TFGameRules()->IsInArenaMode() && tf_arena_use_queue.GetBool() && GetTeamNumber() <= LAST_SHARED_TEAM ) )
  5196. {
  5197. // Prevent this is the cvar is set
  5198. if ( ( mp_allowspectators.GetBool() == false ) && !IsHLTV() && !IsReplay() && TFGameRules()->IsInArenaMode() == false )
  5199. {
  5200. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
  5201. return;
  5202. }
  5203. // Deny spectator access if it would unbalance the teams
  5204. if ( ( mp_spectators_restricted.GetBool() || tf_mm_trusted.GetBool() ) && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  5205. {
  5206. if ( GetTeamNumber() == TF_TEAM_RED || GetTeamNumber() == TF_TEAM_BLUE )
  5207. {
  5208. CTeam *pRedTeam = GetGlobalTeam( TF_TEAM_RED );
  5209. CTeam *pBlueTeam = GetGlobalTeam( TF_TEAM_BLUE );
  5210. if ( pRedTeam && pBlueTeam )
  5211. {
  5212. int nRedCount = pRedTeam->GetNumPlayers();
  5213. int nBlueCount = pBlueTeam->GetNumPlayers();
  5214. int nGap = GetTeamNumber() == TF_TEAM_RED ? ( nBlueCount - nRedCount ) : ( nRedCount - nBlueCount );
  5215. if ( nGap >= mp_teams_unbalance_limit.GetInt() )
  5216. {
  5217. ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator_Unbalance" );
  5218. return;
  5219. }
  5220. }
  5221. }
  5222. }
  5223. if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() )
  5224. {
  5225. CommitSuicide( false, true );
  5226. }
  5227. m_bArenaSpectator = bArenaSpectator;
  5228. DuelMiniGame_NotifyPlayerChangedTeam( this, TEAM_SPECTATOR, true );
  5229. ChangeTeam( TEAM_SPECTATOR );
  5230. if ( m_bArenaSpectator == true )
  5231. {
  5232. SetDesiredPlayerClassIndex( TF_CLASS_UNDEFINED );
  5233. TFGameRules()->Arena_ClientDisconnect( GetPlayerName() );
  5234. TFGameRules()->RemovePlayerFromQueue( this );
  5235. }
  5236. // do we have fadetoblack on? (need to fade their screen back in)
  5237. if ( mp_fadetoblack.GetBool() )
  5238. {
  5239. color32_s clr = { 0,0,0,255 };
  5240. UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
  5241. }
  5242. if ( TFGameRules()->IsInArenaMode() == true && m_bArenaSpectator == false )
  5243. {
  5244. ShowViewPortPanel( PANEL_CLASS_BLUE );
  5245. }
  5246. }
  5247. else
  5248. {
  5249. if ( iTeam == GetTeamNumber() )
  5250. {
  5251. return; // we wouldn't change the team
  5252. }
  5253. if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() )
  5254. {
  5255. CTFTeam *pTeam = TFTeamMgr()->GetTeam( iTeam );
  5256. if ( pTeam )
  5257. {
  5258. if ( pTeam->GetNumPlayers() >= TF_LAST_NORMAL_CLASS - 1 )
  5259. {
  5260. // if this join would put too many players on the team in Highlander mode, refuse
  5261. // come up with a better way to tell the player they tried to join a full team!
  5262. ShowViewPortPanel( PANEL_TEAM );
  5263. return;
  5264. }
  5265. }
  5266. }
  5267. // if this join would unbalance the teams, refuse
  5268. // come up with a better way to tell the player they tried to join a full team!
  5269. if ( TFGameRules()->WouldChangeUnbalanceTeams( iTeam, GetTeamNumber() ) )
  5270. {
  5271. ShowViewPortPanel( PANEL_TEAM );
  5272. return;
  5273. }
  5274. DuelMiniGame_NotifyPlayerChangedTeam( this, iTeam, true );
  5275. bool bSilent = TFGameRules() && TFGameRules()->IsPVEModeActive() && IsBot();
  5276. #ifndef _DEBUG
  5277. TFGameRules()->SetPlayerReadyState( entindex(), false );
  5278. TFGameRules()->SetTeamReadyState( false, GetTeamNumber() );
  5279. #endif // _DEBUG
  5280. ChangeTeam( iTeam, bAutoTeamed, bSilent );
  5281. if ( tf_arena_force_class.GetBool() == false )
  5282. {
  5283. ShowViewPortPanel( ( iTeam == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  5284. }
  5285. }
  5286. }
  5287. //-----------------------------------------------------------------------------
  5288. // Purpose: Join a team without using the game menus
  5289. //-----------------------------------------------------------------------------
  5290. void CTFPlayer::HandleCommand_JoinTeam_NoMenus( const char *pTeamName )
  5291. {
  5292. Assert( IsX360() );
  5293. Msg( "Client command HandleCommand_JoinTeam_NoMenus: %s\n", pTeamName );
  5294. // Only expected to be used on the 360 when players leave the lobby to start a new game
  5295. if ( !IsInCommentaryMode() )
  5296. {
  5297. Assert( GetTeamNumber() == TEAM_UNASSIGNED );
  5298. Assert( IsX360() );
  5299. }
  5300. int iTeam = TEAM_SPECTATOR;
  5301. if ( Q_stricmp( pTeamName, "spectate" ) )
  5302. {
  5303. for ( int i = 0; i < TF_TEAM_COUNT; ++i )
  5304. {
  5305. COMPILE_TIME_ASSERT( TF_TEAM_COUNT == ARRAYSIZE( g_aTeamNames ) );
  5306. if ( stricmp( pTeamName, g_aTeamNames[i] ) == 0 )
  5307. {
  5308. iTeam = i;
  5309. break;
  5310. }
  5311. }
  5312. }
  5313. ForceChangeTeam( iTeam );
  5314. }
  5315. //-----------------------------------------------------------------------------
  5316. // Purpose: Player has been forcefully changed to another team
  5317. //-----------------------------------------------------------------------------
  5318. void CTFPlayer::ForceChangeTeam( int iTeamNum, bool bFullTeamSwitch )
  5319. {
  5320. int iNewTeam = iTeamNum;
  5321. if ( iNewTeam == TF_TEAM_AUTOASSIGN )
  5322. {
  5323. iNewTeam = GetAutoTeam();
  5324. }
  5325. if ( !GetGlobalTeam( iNewTeam ) )
  5326. {
  5327. Warning( "CTFPlayer::ForceChangeTeam( %d ) - invalid team index.\n", iNewTeam );
  5328. return;
  5329. }
  5330. // Some game modes will overrule our player-based logic
  5331. iNewTeam = TFGameRules()->GetTeamAssignmentOverride( this, iNewTeam );
  5332. int iOldTeam = GetTeamNumber();
  5333. // if this is our current team, just abort
  5334. if ( iNewTeam == iOldTeam )
  5335. return;
  5336. // can't change teams if in a duel
  5337. if ( DuelMiniGame_IsInDuel( this ) )
  5338. {
  5339. if ( !m_bIsCoaching )
  5340. return;
  5341. DuelMiniGame_NotifyPlayerChangedTeam( this, iTeamNum, true );
  5342. }
  5343. // can't change teams if coaching
  5344. if ( m_bIsCoaching && m_hStudent != NULL && iTeamNum != TEAM_SPECTATOR )
  5345. return;
  5346. RemoveAllOwnedEntitiesFromWorld( true );
  5347. m_iPreviousteam = iOldTeam;
  5348. BaseClass::ChangeTeam( iNewTeam, false, true );
  5349. if ( !bFullTeamSwitch )
  5350. {
  5351. RemoveNemesisRelationships();
  5352. if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() )
  5353. {
  5354. if ( IsAlive() )
  5355. {
  5356. CommitSuicide( false, true );
  5357. }
  5358. ResetPlayerClass();
  5359. }
  5360. }
  5361. if ( iNewTeam == TEAM_UNASSIGNED )
  5362. {
  5363. StateTransition( TF_STATE_OBSERVER );
  5364. }
  5365. else if ( iNewTeam == TEAM_SPECTATOR )
  5366. {
  5367. StateTransition( TF_STATE_OBSERVER );
  5368. RemoveAllWeapons();
  5369. DestroyViewModels();
  5370. if ( TFGameRules()->IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true )
  5371. {
  5372. TFGameRules()->AddPlayerToQueueHead( this );
  5373. }
  5374. }
  5375. DropFlag();
  5376. // Don't modify living players in any way
  5377. }
  5378. //-----------------------------------------------------------------------------
  5379. // Purpose:
  5380. //-----------------------------------------------------------------------------
  5381. void CTFPlayer::HandleFadeToBlack( void )
  5382. {
  5383. if ( mp_fadetoblack.GetBool() )
  5384. {
  5385. SetObserverMode( OBS_MODE_CHASE );
  5386. color32_s clr = { 0,0,0,255 };
  5387. UTIL_ScreenFade( this, clr, 0.75, 0, FFADE_OUT | FFADE_STAYOUT );
  5388. }
  5389. }
  5390. //-----------------------------------------------------------------------------
  5391. // Purpose:
  5392. //-----------------------------------------------------------------------------
  5393. void CTFPlayer::ChangeTeam( int iTeamNum, bool bAutoTeam, bool bSilent, bool bAutoBalance /*= false*/ )
  5394. {
  5395. if ( !GetGlobalTeam( iTeamNum ) )
  5396. {
  5397. Warning( "CTFPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
  5398. return;
  5399. }
  5400. // game rules don't allow to change team
  5401. if ( TFGameRules() && !TFGameRules()->CanChangeTeam( GetTeamNumber() ) )
  5402. {
  5403. return;
  5404. }
  5405. // Not allowed to change teams when a ghost
  5406. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  5407. {
  5408. return;
  5409. }
  5410. // Not allowed to change teams in bumper kart
  5411. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  5412. {
  5413. return;
  5414. }
  5415. // can only be on TEAM_SPECTATOR when coaching
  5416. if ( IsCoaching() && ( iTeamNum >= FIRST_GAME_TEAM ) )
  5417. {
  5418. return;
  5419. }
  5420. // Some game modes will overrule our player-based logic
  5421. iTeamNum = TFGameRules()->GetTeamAssignmentOverride( this, iTeamNum, bAutoBalance );
  5422. int iOldTeam = GetTeamNumber();
  5423. // if this is our current team, just abort
  5424. if ( iTeamNum == iOldTeam )
  5425. return;
  5426. RemoveAllOwnedEntitiesFromWorld( true );
  5427. bool bNoTeam = GetTeamNumber() == TEAM_UNASSIGNED;
  5428. m_iPreviousteam = iOldTeam;
  5429. CTF_GameStats.Event_TeamChange( this, iOldTeam, iTeamNum );
  5430. m_iTeamChanges++;
  5431. // If joining the underdog team, make next spawn instant (autobalance, paladins)
  5432. if ( TFGameRules() && TFGameRules()->IsDefaultGameMode() && GetTeamNumber() >= FIRST_GAME_TEAM )
  5433. {
  5434. int nStackedTeam, nWeakTeam;
  5435. if ( TFGameRules()->AreTeamsUnbalanced( nStackedTeam, nWeakTeam ) )
  5436. {
  5437. if ( iTeamNum == nWeakTeam )
  5438. {
  5439. AllowInstantSpawn();
  5440. }
  5441. }
  5442. }
  5443. BaseClass::ChangeTeam( iTeamNum, bAutoTeam, bSilent, bAutoBalance );
  5444. if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() )
  5445. {
  5446. if ( IsAlive() )
  5447. {
  5448. CommitSuicide( false, true );
  5449. }
  5450. ResetPlayerClass();
  5451. }
  5452. RemoveNemesisRelationships();
  5453. if ( iTeamNum == TEAM_UNASSIGNED )
  5454. {
  5455. StateTransition( TF_STATE_OBSERVER );
  5456. }
  5457. else if ( iTeamNum == TEAM_SPECTATOR )
  5458. {
  5459. StateTransition( TF_STATE_OBSERVER );
  5460. RemoveAllWeapons();
  5461. DestroyViewModels();
  5462. if ( TFGameRules()->IsInArenaMode() == true && bNoTeam == false && tf_arena_use_queue.GetBool() == true )
  5463. {
  5464. TFGameRules()->AddPlayerToQueue( this );
  5465. }
  5466. }
  5467. else // active player
  5468. {
  5469. bool bKill = true;
  5470. #ifdef STAGING_ONLY
  5471. bKill = ( m_Shared.InCond( TF_COND_REPROGRAMMED ) ) ? false : true;
  5472. #endif // STAGING_ONLY
  5473. if ( bKill && !IsDead() && (iOldTeam == TF_TEAM_RED || iOldTeam == TF_TEAM_BLUE) )
  5474. {
  5475. // Kill player if switching teams while alive
  5476. CommitSuicide( false, true );
  5477. }
  5478. else if ( IsDead() && iOldTeam < FIRST_GAME_TEAM )
  5479. {
  5480. HandleFadeToBlack();
  5481. }
  5482. // let any spies disguising as me know that I've changed teams
  5483. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  5484. {
  5485. CTFPlayer *pTemp = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  5486. if ( pTemp && pTemp != this )
  5487. {
  5488. if ( ( pTemp->m_Shared.GetDisguiseTarget() == this ) || // they were disguising as me and I've changed teams
  5489. ( !pTemp->m_Shared.GetDisguiseTarget() && pTemp->m_Shared.GetDisguiseTeam() == iTeamNum ) ) // they don't have a disguise and I'm joining the team they're disguising as
  5490. {
  5491. // choose someone else...
  5492. pTemp->m_Shared.FindDisguiseTarget();
  5493. }
  5494. }
  5495. }
  5496. }
  5497. m_Shared.RemoveAllCond();
  5498. DuelMiniGame_NotifyPlayerChangedTeam( this, iTeamNum, false );
  5499. #ifdef STAGING_ONLY
  5500. if ( TFGameRules() && TFGameRules()->GameModeUsesExperience() )
  5501. {
  5502. RefundExperiencePoints();
  5503. }
  5504. #endif // STAGING_ONLY
  5505. }
  5506. //-----------------------------------------------------------------------------
  5507. // Purpose:
  5508. //-----------------------------------------------------------------------------
  5509. void CTFPlayer::ResetPlayerClass( void )
  5510. {
  5511. if ( GetPlayerClass() )
  5512. {
  5513. GetPlayerClass()->Reset();
  5514. }
  5515. SetDesiredPlayerClassIndex( TF_CLASS_UNDEFINED );
  5516. }
  5517. //-----------------------------------------------------------------------------
  5518. // Purpose:
  5519. //-----------------------------------------------------------------------------
  5520. void CTFPlayer::HandleCommand_JoinClass( const char *pClassName, bool bAllowSpawn /* = true */ )
  5521. {
  5522. VPROF_BUDGET( "CTFPlayer::HandleCommand_JoinClass", VPROF_BUDGETGROUP_PLAYER );
  5523. if ( TFGameRules()->State_Get() == GR_STATE_GAME_OVER )
  5524. {
  5525. return;
  5526. }
  5527. // if ( TFGameRules()->ArePlayersInHell() && ( m_Shared.m_iDesiredPlayerClass > TF_CLASS_UNDEFINED ) )
  5528. // {
  5529. // ClientPrint( this, HUD_PRINTCENTER, "#TF_CantChangeClassNow" );
  5530. // return;
  5531. // }
  5532. if ( TFGameRules()->IsCompetitiveMode() )
  5533. {
  5534. if ( !tf_tournament_classchange_allowed.GetBool() &&
  5535. TFGameRules()->State_Get() == GR_STATE_RND_RUNNING )
  5536. {
  5537. ClientPrint( this, HUD_PRINTCENTER, "#TF_Ladder_NoClassChangeRound" );
  5538. return;
  5539. }
  5540. if ( !tf_tournament_classchange_ready_allowed.GetBool() &&
  5541. TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS &&
  5542. TFGameRules()->IsPlayerReady( entindex() ) )
  5543. {
  5544. ClientPrint( this, HUD_PRINTCENTER, "#TF_Ladder_NoClassChangeReady" );
  5545. return;
  5546. }
  5547. }
  5548. if ( IsCoaching() )
  5549. return;
  5550. // can only join a class after you join a valid team
  5551. if ( GetTeamNumber() <= LAST_SHARED_TEAM && TFGameRules()->IsInArenaMode() == false )
  5552. return;
  5553. // In case we don't get the class menu message before the spawn timer
  5554. // comes up, fake that we've closed the menu.
  5555. SetClassMenuOpen( false );
  5556. if ( TFGameRules()->InStalemate() && TFGameRules()->IsInArenaMode() == false )
  5557. {
  5558. if ( IsAlive() && !TFGameRules()->CanChangeClassInStalemate() )
  5559. {
  5560. char szTime[6];
  5561. Q_snprintf( szTime, sizeof( szTime ), "%d", tf_stalematechangeclasstime.GetInt() );
  5562. ClientPrint( this, HUD_PRINTTALK, "#game_stalemate_cant_change_class", szTime );
  5563. return;
  5564. }
  5565. }
  5566. if ( TFGameRules()->IsInArenaMode() == true && IsAlive() == true )
  5567. {
  5568. if ( GetTeamNumber() > LAST_SHARED_TEAM && TFGameRules()->InStalemate() == true )
  5569. {
  5570. ClientPrint( this, HUD_PRINTTALK, "#TF_Arena_NoClassChange" );
  5571. return;
  5572. }
  5573. }
  5574. #ifdef TF_RAID_MODE
  5575. if ( TFGameRules()->IsRaidMode() && GetTeamNumber() == TF_TEAM_BLUE && !tf_raid_allow_class_change.GetBool() )
  5576. {
  5577. CTFNavArea *area = (CTFNavArea *)GetLastKnownArea();
  5578. if ( area && !area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE ) )
  5579. {
  5580. ClientPrint( this, HUD_PRINTTALK, "No class changes after leaving the safe room" );
  5581. return;
  5582. }
  5583. }
  5584. #endif // TF_RAID_MODE
  5585. if ( TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_DEFENDERS )
  5586. {
  5587. if ( m_nCanPurchaseUpgradesCount > 0 )
  5588. {
  5589. ClientPrint( this, HUD_PRINTCENTER, "#TF_MVM_NoClassUpgradeUI" );
  5590. return;
  5591. }
  5592. if ( IsReadyToPlay() && !TFGameRules()->InSetup() && g_pPopulationManager && !g_pPopulationManager->IsInEndlessWaves() )
  5593. {
  5594. ClientPrint( this, HUD_PRINTTALK, "#TF_MVM_NoClassChangeAfterSetup" );
  5595. return;
  5596. }
  5597. }
  5598. int iClass = TF_CLASS_UNDEFINED;
  5599. bool bShouldNotRespawn = false;
  5600. if ( !bAllowSpawn || ( ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN ) && ( TFGameRules()->GetWinningTeam() != GetTeamNumber() ) ) )
  5601. {
  5602. m_bAllowInstantSpawn = false;
  5603. bShouldNotRespawn = true;
  5604. }
  5605. if ( stricmp( pClassName, "random" ) != 0 && stricmp( pClassName, "auto" ) != 0 )
  5606. {
  5607. int i = 0;
  5608. for ( i = TF_CLASS_SCOUT ; i < TF_CLASS_COUNT_ALL ; i++ )
  5609. {
  5610. if ( stricmp( pClassName, GetPlayerClassData( i )->m_szClassName ) == 0 )
  5611. {
  5612. iClass = i;
  5613. break;
  5614. }
  5615. }
  5616. bool bCivilianOkay = false;
  5617. if ( !bCivilianOkay && ( i >= TF_LAST_NORMAL_CLASS ) )
  5618. {
  5619. Warning( "HandleCommand_JoinClass( %s ) - invalid class name.\n", pClassName );
  5620. return;
  5621. }
  5622. // Check class limits
  5623. if ( !TFGameRules()->CanPlayerChooseClass(this, iClass) )
  5624. {
  5625. ShowViewPortPanel( ( GetTeamNumber() == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  5626. return;
  5627. }
  5628. }
  5629. else
  5630. {
  5631. int iTries = 20;
  5632. // The player has selected Random class...so let's pick one for them.
  5633. do{
  5634. // Don't let them be the same class twice in a row
  5635. iClass = random->RandomInt( TF_FIRST_NORMAL_CLASS, TF_LAST_NORMAL_CLASS - 1 ); // -1 to remove the civilian from the randomness
  5636. iTries--;
  5637. } while( iClass == GetPlayerClass()->GetClassIndex() || (iTries > 0 && !TFGameRules()->CanPlayerChooseClass(this,iClass)) );
  5638. if ( iTries <= 0 )
  5639. {
  5640. // We failed to find a random class. Bring up the class menu again.
  5641. ShowViewPortPanel( ( GetTeamNumber() == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  5642. return;
  5643. }
  5644. }
  5645. if ( TFGameRules() && TFGameRules()->State_Get() == GR_STATE_RND_RUNNING )
  5646. {
  5647. // Bit field of classes played during the game
  5648. CSteamID steamID;
  5649. GetSteamID( &steamID );
  5650. CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
  5651. if ( pMatch )
  5652. {
  5653. CMatchInfo::PlayerMatchData_t *pMatchPlayer = pMatch->GetMatchDataForPlayer( steamID );
  5654. if ( pMatchPlayer )
  5655. {
  5656. pMatchPlayer->UpdateClassesPlayed( GetPlayerClass()->GetClassIndex() );
  5657. }
  5658. }
  5659. }
  5660. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  5661. if ( mp_developer.GetBool() && !IsBot() )
  5662. {
  5663. Vector vPos = GetAbsOrigin();
  5664. QAngle qAngle = GetAbsAngles();
  5665. SetDesiredPlayerClassIndex( iClass );
  5666. ForceRespawn();
  5667. Teleport( &vPos, &qAngle, &vec3_origin );
  5668. return;
  5669. }
  5670. #endif // _DEBUG || STAGING_ONLY
  5671. // joining the same class?
  5672. if ( iClass != TF_CLASS_RANDOM && iClass == GetDesiredPlayerClassIndex() )
  5673. {
  5674. // If we're dead, and we have instant spawn, respawn us immediately. Catches the case
  5675. // were a player misses respawn wave because they're at the class menu, and then changes
  5676. // their mind and reselects their current class.
  5677. if ( m_bAllowInstantSpawn && !IsAlive() )
  5678. {
  5679. ForceRespawn();
  5680. }
  5681. return;
  5682. }
  5683. if ( TFGameRules()->IsInArenaMode() && tf_arena_use_queue.GetBool() == true && GetTeamNumber() <= LAST_SHARED_TEAM )
  5684. {
  5685. TFGameRules()->AddPlayerToQueue( this );
  5686. }
  5687. // @note Tom Bui: we need to restrict the UI somehow
  5688. // if there's a class restriction on duels...
  5689. int iDuelClass = DuelMiniGame_GetRequiredPlayerClass( this );
  5690. if ( iDuelClass >= TF_FIRST_NORMAL_CLASS && iDuelClass < TF_LAST_NORMAL_CLASS )
  5691. {
  5692. iClass = iDuelClass;
  5693. }
  5694. SetDesiredPlayerClassIndex( iClass );
  5695. IGameEvent * event = gameeventmanager->CreateEvent( "player_changeclass" );
  5696. if ( event )
  5697. {
  5698. event->SetInt( "userid", GetUserID() );
  5699. event->SetInt( "class", iClass );
  5700. gameeventmanager->FireEvent( event );
  5701. }
  5702. // are they TF_CLASS_RANDOM and trying to select the class they're currently playing as (so they can stay this class)?
  5703. if ( iClass == GetPlayerClass()->GetClassIndex() )
  5704. {
  5705. // If we're dead, and we have instant spawn, respawn us immediately. Catches the case
  5706. // were a player misses respawn wave because they're at the class menu, and then changes
  5707. // their mind and reselects their current class.
  5708. if ( m_bAllowInstantSpawn && !IsAlive() )
  5709. {
  5710. ForceRespawn();
  5711. }
  5712. return;
  5713. }
  5714. // We can respawn instantly if:
  5715. // - We're dead, and we're past the required post-death time
  5716. // - We're inside a respawn room
  5717. // - We're in the stalemate grace period
  5718. bool bInRespawnRoom = PointInRespawnRoom( this, WorldSpaceCenter() );
  5719. if ( bInRespawnRoom && !IsAlive() )
  5720. {
  5721. // If we're not spectating ourselves, ignore respawn rooms. Otherwise we'll get instant spawns
  5722. // by spectating someone inside a respawn room.
  5723. bInRespawnRoom = (GetObserverTarget() == this);
  5724. }
  5725. bool bDeadInstantSpawn = !IsAlive();
  5726. if ( bDeadInstantSpawn && m_flDeathTime )
  5727. {
  5728. // In death mode, don't allow class changes to force respawns ahead of respawn waves
  5729. float flWaveTime = TFGameRules()->GetNextRespawnWave( GetTeamNumber(), this );
  5730. bDeadInstantSpawn = (gpGlobals->curtime > flWaveTime);
  5731. }
  5732. bool bInStalemateClassChangeTime = false;
  5733. if ( TFGameRules()->InStalemate() && TFGameRules()->IsInWaitingForPlayers() == false )
  5734. {
  5735. // Stalemate overrides respawn rules. Only allow spawning if we're in the class change time.
  5736. bInStalemateClassChangeTime = TFGameRules()->CanChangeClassInStalemate();
  5737. bDeadInstantSpawn = false;
  5738. bInRespawnRoom = false;
  5739. }
  5740. if ( TFGameRules()->IsInArenaMode() == true )
  5741. {
  5742. if ( TFGameRules()->IsInWaitingForPlayers() == false )
  5743. {
  5744. bDeadInstantSpawn = false;
  5745. if ( TFGameRules()->InStalemate() == false && TFGameRules()->State_Get() != GR_STATE_TEAM_WIN )
  5746. {
  5747. bInRespawnRoom = true;
  5748. bShouldNotRespawn = false;
  5749. }
  5750. else
  5751. {
  5752. bShouldNotRespawn = true;
  5753. if ( tf_arena_use_queue.GetBool() == false )
  5754. return;
  5755. }
  5756. }
  5757. else if ( tf_arena_use_queue.GetBool() == false )
  5758. {
  5759. return;
  5760. }
  5761. }
  5762. if ( TFGameRules()->IsMannVsMachineMode() && TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS )
  5763. m_bAllowInstantSpawn = true;
  5764. if ( bShouldNotRespawn == false && ( m_bAllowInstantSpawn || bDeadInstantSpawn || bInRespawnRoom || bInStalemateClassChangeTime ) )
  5765. {
  5766. ForceRespawn();
  5767. #ifdef STAGING_ONLY
  5768. if ( TFGameRules() && TFGameRules()->GameModeUsesExperience() )
  5769. {
  5770. RefundExperiencePoints();
  5771. }
  5772. #endif // STAGING_ONLY
  5773. return;
  5774. }
  5775. if( iClass == TF_CLASS_RANDOM )
  5776. {
  5777. if( IsAlive() )
  5778. {
  5779. ClientPrint(this, HUD_PRINTTALK, "#game_respawn_asrandom" );
  5780. }
  5781. else
  5782. {
  5783. ClientPrint(this, HUD_PRINTTALK, "#game_spawn_asrandom" );
  5784. }
  5785. }
  5786. else
  5787. {
  5788. if( IsAlive() )
  5789. {
  5790. ClientPrint(this, HUD_PRINTTALK, "#game_respawn_as", GetPlayerClassData( iClass )->m_szLocalizableName );
  5791. }
  5792. else
  5793. {
  5794. ClientPrint(this, HUD_PRINTTALK, "#game_spawn_as", GetPlayerClassData( iClass )->m_szLocalizableName );
  5795. }
  5796. }
  5797. if ( IsAlive() && ( GetHudClassAutoKill() == true ) && bShouldNotRespawn == false )
  5798. {
  5799. CommitSuicide( false, true );
  5800. }
  5801. #ifdef STAGING_ONLY
  5802. if ( TFGameRules() && TFGameRules()->GameModeUsesExperience() )
  5803. {
  5804. SetExperiencePoints( 0 );
  5805. SetCurrency( 0 );
  5806. SetExperienceLevel( 1 );
  5807. }
  5808. #endif // STAGING_ONLY
  5809. }
  5810. //-----------------------------------------------------------------------------
  5811. // Purpose: The GC has told us this player wants to respawn now that their loadout has changed.
  5812. //-----------------------------------------------------------------------------
  5813. void CTFPlayer::CheckInstantLoadoutRespawn( void )
  5814. {
  5815. // Must be alive
  5816. if ( !IsAlive() )
  5817. return;
  5818. // In a respawn room
  5819. if ( !PointInRespawnRoom( this, WorldSpaceCenter() ) )
  5820. return;
  5821. // Not in stalemate (beyond the change class period)
  5822. if ( TFGameRules()->InStalemate() && !TFGameRules()->CanChangeClassInStalemate() )
  5823. return;
  5824. // Not in Arena mode
  5825. if ( TFGameRules()->IsInArenaMode() == true )
  5826. return;
  5827. // Not if we're on the losing team
  5828. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN && TFGameRules()->GetWinningTeam() != GetTeamNumber() )
  5829. return;
  5830. // Not if our current class's loadout hasn't changed
  5831. int iClass = GetPlayerClass() ? GetPlayerClass()->GetClassIndex() : TF_CLASS_UNDEFINED;
  5832. if ( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_LAST_NORMAL_CLASS )
  5833. {
  5834. if ( m_Inventory.ClassLoadoutHasChanged( iClass ) )
  5835. {
  5836. if ( m_Shared.InCond( TF_COND_AIMING ) )
  5837. {
  5838. // If we are in condition TF_COND_AIMING it will be removed during the ForceRespawn() so we need to reset the weapon
  5839. // (which is normally skipped while regenerating)...this only affects the Minigun and the Sniper Rifle.
  5840. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  5841. if ( pWeapon )
  5842. {
  5843. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) || WeaponID_IsSniperRifle( pWeapon->GetWeaponID() ) )
  5844. {
  5845. pWeapon->WeaponReset();
  5846. }
  5847. }
  5848. }
  5849. if ( IsPlayerClass( TF_CLASS_MEDIC ) )
  5850. {
  5851. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( GetActiveTFWeapon() );
  5852. if ( pMedigun )
  5853. {
  5854. pMedigun->Lower();
  5855. }
  5856. }
  5857. // We want to use ForceRespawn() here so the player is physically moved back
  5858. // into the spawn room and not just regenerated instantly in the doorway
  5859. ForceRegenerateAndRespawn();
  5860. }
  5861. }
  5862. }
  5863. class CGC_RespawnPostLoadoutChange : public GCSDK::CGCClientJob
  5864. {
  5865. public:
  5866. CGC_RespawnPostLoadoutChange( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  5867. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  5868. {
  5869. GCSDK::CGCMsg<MsgGCRespawnPostLoadoutChange_t> msg( pNetPacket );
  5870. CSteamID steamID = msg.Body().m_ulInitiatorSteamID;
  5871. // Find the player with this steamID
  5872. CSteamID tmpID;
  5873. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  5874. {
  5875. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  5876. if ( !pPlayer )
  5877. continue;
  5878. if ( !pPlayer->GetSteamID( &tmpID ) )
  5879. continue;
  5880. if ( tmpID == steamID )
  5881. {
  5882. pPlayer->CheckInstantLoadoutRespawn();
  5883. break;
  5884. }
  5885. }
  5886. return true;
  5887. }
  5888. };
  5889. GC_REG_JOB( GCSDK::CGCClient, CGC_RespawnPostLoadoutChange, "CGC_RespawnPostLoadoutChange", k_EMsgGCRespawnPostLoadoutChange, GCSDK::k_EServerTypeGCClient );
  5890. #if defined (_DEBUG)
  5891. //-----------------------------------------------------------------------------
  5892. // Purpose:
  5893. //-----------------------------------------------------------------------------
  5894. static void DebugEconItemView( const char *pszDescStr, CEconItemView *pEconItemView )
  5895. {
  5896. if ( !pEconItemView )
  5897. return;
  5898. const GameItemDefinition_t *pItemDef = pEconItemView->GetItemDefinition();
  5899. Assert( pItemDef );
  5900. Warning("%s: \"%s\"\n", pszDescStr, pItemDef->GetDefinitionName() );
  5901. }
  5902. #endif
  5903. bool CTFPlayer::ClientCommand( const CCommand &args )
  5904. {
  5905. const char *pcmd = args[0];
  5906. m_flLastAction = gpGlobals->curtime;
  5907. if ( FStrEq( pcmd, "addcond" ) )
  5908. {
  5909. if ( sv_cheats->GetBool() && args.ArgC() >= 2 )
  5910. {
  5911. ETFCond eCond = (ETFCond)clamp( atoi( args[1] ), 0, TF_COND_LAST-1 );
  5912. CTFPlayer *pTargetPlayer = this;
  5913. if ( args.ArgC() >= 4 )
  5914. {
  5915. // Find the matching netname
  5916. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  5917. {
  5918. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex(i) );
  5919. if ( pPlayer )
  5920. {
  5921. if ( Q_strstr( pPlayer->GetPlayerName(), args[3] ) )
  5922. {
  5923. pTargetPlayer = ToTFPlayer(pPlayer);
  5924. break;
  5925. }
  5926. }
  5927. }
  5928. }
  5929. if ( args.ArgC() >= 3 )
  5930. {
  5931. float flDuration = atof( args[2] );
  5932. pTargetPlayer->m_Shared.AddCond( eCond, flDuration );
  5933. }
  5934. else
  5935. {
  5936. pTargetPlayer->m_Shared.AddCond( eCond );
  5937. }
  5938. }
  5939. return true;
  5940. }
  5941. else if ( FStrEq( pcmd, "removecond" ) )
  5942. {
  5943. if ( sv_cheats->GetBool() && args.ArgC() >= 2 )
  5944. {
  5945. CTFPlayer *pTargetPlayer = this;
  5946. if ( args.ArgC() >= 3 )
  5947. {
  5948. // Find the matching netname
  5949. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  5950. {
  5951. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex(i) );
  5952. if ( pPlayer )
  5953. {
  5954. if ( Q_strstr( pPlayer->GetPlayerName(), args[2] ) )
  5955. {
  5956. pTargetPlayer = ToTFPlayer(pPlayer);
  5957. break;
  5958. }
  5959. }
  5960. }
  5961. }
  5962. ETFCond eCond = (ETFCond)clamp( atoi( args[1] ), 0, TF_COND_LAST-1 );
  5963. pTargetPlayer->m_Shared.RemoveCond( eCond );
  5964. }
  5965. return true;
  5966. }
  5967. #ifdef _DEBUG
  5968. else if ( FStrEq( pcmd, "burn" ) )
  5969. {
  5970. m_Shared.Burn( this, GetActiveTFWeapon() );
  5971. return true;
  5972. }
  5973. else if ( FStrEq( pcmd, "bleed" ) )
  5974. {
  5975. m_Shared.MakeBleed( this, GetActiveTFWeapon(), 10.0f );
  5976. return true;
  5977. }
  5978. else if ( FStrEq( pcmd, "dump_damagers" ) )
  5979. {
  5980. m_AchievementData.DumpDamagers();
  5981. return true;
  5982. }
  5983. else if ( FStrEq( pcmd, "stun" ) )
  5984. {
  5985. if ( args.ArgC() >= 4 )
  5986. {
  5987. m_Shared.StunPlayer( atof(args[1]), atof(args[2]), atof(args[3]) );
  5988. }
  5989. return true;
  5990. }
  5991. // else if ( FStrEq( pcmd, "decoy" ) )
  5992. // {
  5993. // CBotNPCDecoy *decoy = (CBotNPCDecoy *)CreateEntityByName( "bot_npc_decoy" );
  5994. // if ( decoy )
  5995. // {
  5996. // decoy->SetOwnerEntity( this );
  5997. // DispatchSpawn( decoy );
  5998. // }
  5999. // return true;
  6000. // }
  6001. else if ( FStrEq( pcmd, "tada" ) )
  6002. {
  6003. if ( ShouldRunRateLimitedCommand( args ) )
  6004. {
  6005. Taunt( TAUNT_SHOW_ITEM );
  6006. }
  6007. return true;
  6008. }
  6009. // else if ( FStrEq( pcmd, "player_disguise" ) )
  6010. // {
  6011. // CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByName( args[1] ) );
  6012. // pPlayer->m_Shared.Disguise( Q_atoi( args[2] ), Q_atoi( args[3] ) );
  6013. // return true;
  6014. // }
  6015. else
  6016. #endif
  6017. if ( FStrEq( pcmd, "jointeam" ) )
  6018. {
  6019. // don't let them spam the server with changes
  6020. if ( GetNextChangeTeamTime() > gpGlobals->curtime )
  6021. return true;
  6022. SetNextChangeTeamTime( gpGlobals->curtime + 2.0f ); // limit to one change every 2 secs
  6023. if ( args.ArgC() >= 2 )
  6024. {
  6025. HandleCommand_JoinTeam( args[1] );
  6026. }
  6027. return true;
  6028. }
  6029. else if ( FStrEq( pcmd, "jointeam_nomenus" ) )
  6030. {
  6031. if ( IsX360() )
  6032. {
  6033. if ( args.ArgC() >= 2 )
  6034. {
  6035. HandleCommand_JoinTeam_NoMenus( args[1] );
  6036. }
  6037. return true;
  6038. }
  6039. return false;
  6040. }
  6041. else if ( FStrEq( pcmd, "closedwelcomemenu" ) )
  6042. {
  6043. if ( ShouldRunRateLimitedCommand( args ) )
  6044. {
  6045. if ( GetTeamNumber() == TEAM_UNASSIGNED )
  6046. {
  6047. if ( ShouldForceAutoTeam() )
  6048. {
  6049. ChangeTeam( GetAutoTeam(), true, false );
  6050. ShowViewPortPanel( ( GetTeamNumber() == TF_TEAM_BLUE ) ? PANEL_CLASS_BLUE : PANEL_CLASS_RED );
  6051. }
  6052. else
  6053. {
  6054. ShowViewPortPanel( PANEL_TEAM, true );
  6055. }
  6056. }
  6057. else if ( IsPlayerClass( TF_CLASS_UNDEFINED ) )
  6058. {
  6059. if ( tf_arena_force_class.GetBool() == false )
  6060. {
  6061. switch( GetTeamNumber() )
  6062. {
  6063. case TF_TEAM_RED:
  6064. ShowViewPortPanel( PANEL_CLASS_RED, true );
  6065. break;
  6066. case TF_TEAM_BLUE:
  6067. ShowViewPortPanel( PANEL_CLASS_BLUE, true );
  6068. break;
  6069. default:
  6070. break;
  6071. }
  6072. }
  6073. }
  6074. }
  6075. return true;
  6076. }
  6077. else if ( FStrEq( pcmd, "joinclass" ) )
  6078. {
  6079. // don't let them spam the server with changes
  6080. if ( GetNextChangeClassTime() > gpGlobals->curtime )
  6081. return true;
  6082. SetNextChangeClassTime( gpGlobals->curtime + 0.5 ); // limit to one change every 0.5 secs
  6083. if ( tf_arena_force_class.GetBool() == false )
  6084. {
  6085. if ( args.ArgC() >= 2 )
  6086. {
  6087. HandleCommand_JoinClass( args[1] );
  6088. }
  6089. }
  6090. return true;
  6091. }
  6092. else if ( FStrEq( pcmd, "resetclass" ) )
  6093. {
  6094. if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() && ( GetTeamNumber() > LAST_SHARED_TEAM ) )
  6095. {
  6096. if ( IsAlive() )
  6097. {
  6098. CommitSuicide( false, true );
  6099. }
  6100. ResetPlayerClass();
  6101. ShowViewPortPanel( ( GetTeamNumber() == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  6102. }
  6103. return true;
  6104. }
  6105. else if ( FStrEq( pcmd, "mp_playgesture" ) )
  6106. {
  6107. if ( ShouldRunRateLimitedCommand( args ) )
  6108. {
  6109. if ( args.ArgC() == 1 )
  6110. {
  6111. Warning( "mp_playgesture: Gesture activity or sequence must be specified!\n" );
  6112. return true;
  6113. }
  6114. if ( sv_cheats->GetBool() )
  6115. {
  6116. if ( !PlayGesture( args[1] ) )
  6117. {
  6118. Warning( "mp_playgesture: unknown sequence or activity name \"%s\"\n", args[1] );
  6119. return true;
  6120. }
  6121. }
  6122. }
  6123. return true;
  6124. }
  6125. else if ( FStrEq( pcmd, "mp_playanimation" ) )
  6126. {
  6127. if ( ShouldRunRateLimitedCommand( args ) )
  6128. {
  6129. if ( args.ArgC() == 1 )
  6130. {
  6131. Warning( "mp_playanimation: Activity or sequence must be specified!\n" );
  6132. return true;
  6133. }
  6134. if ( sv_cheats->GetBool() )
  6135. {
  6136. if ( !PlaySpecificSequence( args[1] ) )
  6137. {
  6138. Warning( "mp_playanimation: Unknown sequence or activity name \"%s\"\n", args[1] );
  6139. return true;
  6140. }
  6141. }
  6142. }
  6143. return true;
  6144. }
  6145. else if ( FStrEq( pcmd, "menuopen" ) )
  6146. {
  6147. SetClassMenuOpen( true );
  6148. return true;
  6149. }
  6150. else if ( FStrEq( pcmd, "menuclosed" ) )
  6151. {
  6152. SetClassMenuOpen( false );
  6153. return true;
  6154. }
  6155. else if ( FStrEq( pcmd, "pda_click" ) )
  6156. {
  6157. if ( ShouldRunRateLimitedCommand( args ) )
  6158. {
  6159. // player clicked on the PDA, play attack animation
  6160. CTFWeaponBase *pWpn = GetActiveTFWeapon();
  6161. CTFWeaponPDA *pPDA = dynamic_cast<CTFWeaponPDA *>( pWpn );
  6162. if ( pPDA && !m_Shared.InCond( TF_COND_DISGUISED ) )
  6163. {
  6164. DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  6165. }
  6166. }
  6167. return true;
  6168. }
  6169. else if ( FStrEq( pcmd, "weapon_taunt" ) || FStrEq( pcmd, "taunt" ) )
  6170. {
  6171. if ( ShouldRunRateLimitedCommand( args ) )
  6172. {
  6173. int iTauntSlot = args.ArgC() == 2 ? atoi( args[1] ) : 0;
  6174. HandleTauntCommand( iTauntSlot );
  6175. }
  6176. return true;
  6177. }
  6178. else if ( FStrEq( pcmd, "stop_taunt" ) )
  6179. {
  6180. if( m_Shared.GetTauntIndex() == TAUNT_LONG && !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  6181. {
  6182. EndLongTaunt();
  6183. }
  6184. return true;
  6185. }
  6186. else if ( FStrEq( pcmd, "-taunt" ) )
  6187. {
  6188. // DO NOTHING
  6189. // We changed taunt key to be press to toggle instead of press and hold to do long taunt
  6190. return true;
  6191. }
  6192. else if ( FStrEq( pcmd, "td_buyback" ) )
  6193. {
  6194. if ( TFGameRules() && TFGameRules()->IsPVEModeActive() && IsObserver() && ( GetTeamNumber() > TEAM_SPECTATOR ) )
  6195. {
  6196. // Make sure we're not still in freezecam
  6197. int iObsMode = GetObserverMode();
  6198. if ( iObsMode == OBS_MODE_FREEZECAM || iObsMode == OBS_MODE_DEATHCAM )
  6199. return true;
  6200. float flWaveTime = TFGameRules()->GetNextRespawnWave( GetTeamNumber(), this );
  6201. bool bSuccess = false;
  6202. int iRespawnWait = (flWaveTime - gpGlobals->curtime);
  6203. int iCost = iRespawnWait * MVM_BUYBACK_COST_PER_SEC;
  6204. // New system (finite buybacks per-wave, not currency-based)
  6205. if ( tf_mvm_buybacks_method.GetBool() )
  6206. {
  6207. if ( g_pPopulationManager->GetNumBuybackCreditsForPlayer( this ) )
  6208. {
  6209. bSuccess = true;
  6210. iCost = 1;
  6211. g_pPopulationManager->RemoveBuybackCreditFromPlayer( this );
  6212. }
  6213. }
  6214. // Old system (currency-based)
  6215. else
  6216. {
  6217. if ( GetCurrency() >= iCost )
  6218. {
  6219. bSuccess = true;
  6220. RemoveCurrency( iCost );
  6221. MannVsMachineStats_PlayerEvent_BoughtInstantRespawn( this, iCost );
  6222. }
  6223. }
  6224. if ( bSuccess )
  6225. {
  6226. ForceRespawn();
  6227. IGameEvent *event = gameeventmanager->CreateEvent( "player_buyback" );
  6228. if ( event )
  6229. {
  6230. event->SetInt( "player", entindex() );
  6231. event->SetInt( "cost", iCost );
  6232. gameeventmanager->FireEvent( event );
  6233. }
  6234. }
  6235. else
  6236. {
  6237. CSingleUserRecipientFilter filter( this );
  6238. EmitSound_t params;
  6239. params.m_pSoundName = "Player.DenyWeaponSelection";
  6240. EmitSound( filter, entindex(), params );
  6241. }
  6242. }
  6243. return true;
  6244. }
  6245. else if ( FStrEq( pcmd, "build" ) )
  6246. {
  6247. if ( ShouldRunRateLimitedCommand( args ) )
  6248. {
  6249. if ( TFGameRules()->InStalemate() && mp_stalemate_meleeonly.GetBool() )
  6250. return true;
  6251. // can't issue a build command while carrying an object
  6252. if ( m_Shared.IsCarryingObject() )
  6253. return true;
  6254. if ( IsTaunting() )
  6255. return true;
  6256. int iBuilding = 0;
  6257. int iMode = 0;
  6258. bool bArgsChecked = false;
  6259. // Fixup old binds.
  6260. if ( args.ArgC() == 2 )
  6261. {
  6262. iBuilding = atoi( args[ 1 ] );
  6263. if ( iBuilding == 3 ) // Teleport exit is now a mode.
  6264. {
  6265. iBuilding = 1;
  6266. iMode = 1;
  6267. }
  6268. bArgsChecked = true;
  6269. }
  6270. else if ( args.ArgC() == 3 )
  6271. {
  6272. iBuilding = atoi( args[ 1 ] );
  6273. iMode = atoi( args[ 2 ] );
  6274. bArgsChecked = true;
  6275. }
  6276. if ( bArgsChecked )
  6277. {
  6278. StartBuildingObjectOfType( iBuilding, iMode );
  6279. }
  6280. else
  6281. {
  6282. Warning( "Usage: build <building> <mode>\n" );
  6283. }
  6284. }
  6285. return true;
  6286. }
  6287. else if ( FStrEq( pcmd, "destroy" ) )
  6288. {
  6289. if ( ShouldRunRateLimitedCommand( args ) )
  6290. {
  6291. if ( IsPlayerClass( TF_CLASS_ENGINEER ) ) // Spies can't destroy buildings (sappers)
  6292. {
  6293. int iBuilding = 0;
  6294. int iMode = 0;
  6295. bool bArgsChecked = false;
  6296. // Fixup old binds.
  6297. if ( args.ArgC() == 2 )
  6298. {
  6299. iBuilding = atoi( args[ 1 ] );
  6300. if ( iBuilding == 3 ) // Teleport exit is now a mode.
  6301. {
  6302. iBuilding = 1;
  6303. iMode = 1;
  6304. }
  6305. bArgsChecked = true;
  6306. }
  6307. else if ( args.ArgC() == 3 )
  6308. {
  6309. iBuilding = atoi( args[ 1 ] );
  6310. iMode = atoi( args[ 2 ] );
  6311. bArgsChecked = true;
  6312. }
  6313. if ( bArgsChecked )
  6314. {
  6315. DetonateObjectOfType( iBuilding, iMode );
  6316. }
  6317. else
  6318. {
  6319. Warning( "Usage: destroy <building> <mode>\n" );
  6320. }
  6321. }
  6322. }
  6323. return true;
  6324. }
  6325. else if ( FStrEq( pcmd, "eureka_teleport" ) )
  6326. {
  6327. if ( ShouldRunRateLimitedCommand( args ) )
  6328. {
  6329. CTFWeaponBase* pWeapon = GetActiveTFWeapon();
  6330. if ( !pWeapon )
  6331. return true;
  6332. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  6333. return true;
  6334. int iAltFireTeleportToSpawn = 0;
  6335. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iAltFireTeleportToSpawn, alt_fire_teleport_to_spawn );
  6336. if ( IsPlayerClass( TF_CLASS_ENGINEER ) && iAltFireTeleportToSpawn )
  6337. {
  6338. if ( args.ArgC() == 2 )
  6339. {
  6340. m_eEurekaTeleportTarget = (eEurekaTeleportTargets)atoi( args[1] );
  6341. }
  6342. else
  6343. {
  6344. m_eEurekaTeleportTarget = EUREKA_TELEPORT_HOME;
  6345. }
  6346. // Do the Eureka Effect teleport taunt
  6347. Taunt( TAUNT_SPECIAL, MP_CONCEPT_TAUNT_EUREKA_EFFECT_TELEPORT );
  6348. }
  6349. }
  6350. }
  6351. else if ( FStrEq( pcmd, "arena_changeclass" ) )
  6352. {
  6353. if ( ShouldRunRateLimitedCommand( args ) )
  6354. {
  6355. if ( TFGameRules() && TFGameRules()->IsInArenaMode() && ( tf_arena_force_class.GetBool() == true ) )
  6356. {
  6357. if ( TFGameRules()->State_Get() == GR_STATE_PREROUND )
  6358. {
  6359. if ( m_Shared.GetArenaNumChanges() < tf_arena_change_limit.GetInt() )
  6360. {
  6361. CommitSuicide( true, false );
  6362. m_Shared.IncrementArenaNumChanges();
  6363. }
  6364. }
  6365. }
  6366. }
  6367. return true;
  6368. }
  6369. else if ( FStrEq( pcmd, "extendfreeze" ) )
  6370. {
  6371. if ( ShouldRunRateLimitedCommand( args ) )
  6372. {
  6373. m_flDeathTime += 2.0f;
  6374. }
  6375. return true;
  6376. }
  6377. else if ( FStrEq( pcmd, "show_motd" ) )
  6378. {
  6379. if ( ShouldRunRateLimitedCommand( args ) )
  6380. {
  6381. if ( ShouldForceAutoTeam() )
  6382. {
  6383. int nPreferedTeam = TF_TEAM_AUTOASSIGN;
  6384. PlayerHistoryInfo_t *pPlayerInfo = ( TFGameRules() ) ? TFGameRules()->PlayerHistory_GetPlayerInfo( this ) : NULL;
  6385. if ( pPlayerInfo && pPlayerInfo->nTeam >= FIRST_GAME_TEAM )
  6386. {
  6387. nPreferedTeam = pPlayerInfo->nTeam;
  6388. }
  6389. int iTeam = GetAutoTeam( nPreferedTeam );
  6390. ChangeTeam( iTeam, true, false );
  6391. ShowViewPortPanel( ( iTeam == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  6392. }
  6393. #ifdef TF_RAID_MODE
  6394. else if ( TFGameRules()->IsBossBattleMode() )
  6395. {
  6396. int iTeam = GetAutoTeam();
  6397. ChangeTeam( iTeam, true );
  6398. ShowViewPortPanel( ( iTeam == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  6399. }
  6400. #endif
  6401. else
  6402. {
  6403. ShowViewPortPanel( PANEL_TEAM, false );
  6404. }
  6405. ShowViewPortPanel( PANEL_ARENA_TEAM, false );
  6406. char pszWelcome[128];
  6407. Q_snprintf( pszWelcome, sizeof(pszWelcome), "#TF_Welcome" );
  6408. if ( UTIL_GetActiveHolidayString() )
  6409. {
  6410. Q_snprintf( pszWelcome, sizeof(pszWelcome), "#TF_Welcome_%s", UTIL_GetActiveHolidayString() );
  6411. }
  6412. KeyValues *data = new KeyValues( "data" );
  6413. data->SetString( "title", pszWelcome ); // info panel title
  6414. data->SetString( "type", "1" ); // show userdata from stringtable entry
  6415. data->SetString( "msg", "motd" ); // use this stringtable entry
  6416. data->SetString( "msg_fallback", "motd_text" ); // use this stringtable entry if the base is HTML, and client has disabled HTML motds
  6417. data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );
  6418. ShowViewPortPanel( PANEL_INFO, true, data );
  6419. data->deleteThis();
  6420. }
  6421. return true;
  6422. }
  6423. else if ( FStrEq( pcmd, "show_htmlpage" ) )
  6424. {
  6425. if ( ShouldRunRateLimitedCommand( args ) )
  6426. {
  6427. if ( args.ArgC() != 2 )
  6428. {
  6429. Warning( "Usage: show_htmlpage <url>\n" );
  6430. return true;
  6431. }
  6432. KeyValues *data = new KeyValues( "data" );
  6433. data->SetString( "title", "#TF_Welcome" ); // info panel title
  6434. data->SetString( "type", "2" ); // show url
  6435. data->SetString( "msg", args[1] );
  6436. data->SetString( "msg_fallback", "motd_text" ); // use this stringtable entry if the base is HTML, and client has disabled HTML motds
  6437. data->SetInt( "cmd", TEXTWINDOW_CMD_CLOSED_HTMLPAGE ); // exec this command if panel closed
  6438. data->SetString( "customsvr", "1" );
  6439. data->SetBool( "unload", false );
  6440. ShowViewPortPanel( PANEL_INFO, true, data );
  6441. data->deleteThis();
  6442. }
  6443. return true;
  6444. }
  6445. else if ( FStrEq( pcmd, "closed_htmlpage" ) )
  6446. {
  6447. // Does nothing, it's for server plugins to hook.
  6448. return true;
  6449. }
  6450. else if ( FStrEq( pcmd, "condump_on" ) )
  6451. {
  6452. if ( ShouldRunRateLimitedCommand( args ) )
  6453. {
  6454. if ( !PlayerHasPowerplay() )
  6455. {
  6456. Msg("Console dumping on.\n");
  6457. return true;
  6458. }
  6459. else
  6460. {
  6461. if ( args.ArgC() == 2 && GetTeam() )
  6462. {
  6463. for ( int i = 0; i < GetTeam()->GetNumPlayers(); i++ )
  6464. {
  6465. CTFPlayer *pTeamPlayer = ToTFPlayer( GetTeam()->GetPlayer(i) );
  6466. if ( pTeamPlayer )
  6467. {
  6468. pTeamPlayer->SetPowerplayEnabled( true );
  6469. }
  6470. }
  6471. return true;
  6472. }
  6473. else
  6474. {
  6475. if ( SetPowerplayEnabled( true ) )
  6476. return true;
  6477. }
  6478. }
  6479. }
  6480. }
  6481. else if ( FStrEq( pcmd, "condump_off" ) )
  6482. {
  6483. if ( ShouldRunRateLimitedCommand( args ) )
  6484. {
  6485. if ( !PlayerHasPowerplay() )
  6486. {
  6487. Msg("Console dumping off.\n");
  6488. return true;
  6489. }
  6490. else
  6491. {
  6492. if ( args.ArgC() == 2 && GetTeam() )
  6493. {
  6494. for ( int i = 0; i < GetTeam()->GetNumPlayers(); i++ )
  6495. {
  6496. CTFPlayer *pTeamPlayer = ToTFPlayer( GetTeam()->GetPlayer(i) );
  6497. if ( pTeamPlayer )
  6498. {
  6499. pTeamPlayer->SetPowerplayEnabled( false );
  6500. }
  6501. }
  6502. return true;
  6503. }
  6504. else
  6505. {
  6506. if ( SetPowerplayEnabled( false ) )
  6507. return true;
  6508. }
  6509. }
  6510. }
  6511. }
  6512. else if ( FStrEq( pcmd, "spec_next" ) ) // chase next player
  6513. {
  6514. if ( m_bIsCoaching )
  6515. {
  6516. return true;
  6517. }
  6518. // if ( !ShouldRunRateLimitedCommand( args ) )
  6519. // return true;
  6520. // intentionally falling through to the bottom so the baseclass version is called
  6521. m_bArenaIsAFK = false;
  6522. }
  6523. else if ( FStrEq( pcmd, "spec_prev" ) ) // chase prev player
  6524. {
  6525. if ( m_bIsCoaching )
  6526. {
  6527. return true;
  6528. }
  6529. // if ( !ShouldRunRateLimitedCommand( args ) )
  6530. // return true;
  6531. // intentionally falling through to the bottom so the baseclass version is called
  6532. m_bArenaIsAFK = false;
  6533. }
  6534. else if ( FStrEq( pcmd, "spec_mode" ) ) // set obs mode
  6535. {
  6536. // if ( !ShouldRunRateLimitedCommand( args ) )
  6537. // return true;
  6538. // intentionally falling through to the bottom so the baseclass version is called
  6539. m_bArenaIsAFK = false;
  6540. }
  6541. else if ( FStrEq( pcmd, "showroundinfo" ) )
  6542. {
  6543. if ( ShouldRunRateLimitedCommand( args ) )
  6544. {
  6545. // don't let the player open the round info menu until they're a spectator or they're on a regular team and have picked a class
  6546. if ( ( GetTeamNumber() == TEAM_SPECTATOR ) || ( ( GetTeamNumber() != TEAM_UNASSIGNED ) && ( GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED ) ) )
  6547. {
  6548. if ( TFGameRules() )
  6549. {
  6550. TFGameRules()->ShowRoundInfoPanel( this );
  6551. }
  6552. }
  6553. }
  6554. return true;
  6555. }
  6556. #ifdef STAGING_ONLY
  6557. else if ( FStrEq( pcmd, "feigndeath") )
  6558. {
  6559. m_Shared.SetFeignDeathReady( true );
  6560. }
  6561. #endif // STAGING_ONLY
  6562. else if ( FStrEq( pcmd, "autoteam" ) )
  6563. {
  6564. if ( !IsCoaching() )
  6565. {
  6566. int iTeam = GetAutoTeam();
  6567. ChangeTeam( iTeam, true, false );
  6568. if ( iTeam > LAST_SHARED_TEAM )
  6569. {
  6570. ShowViewPortPanel( ( iTeam == TF_TEAM_RED ) ? PANEL_CLASS_RED : PANEL_CLASS_BLUE );
  6571. }
  6572. }
  6573. return true;
  6574. }
  6575. else if ( FStrEq( pcmd, "coach_command" ) )
  6576. {
  6577. if ( m_bIsCoaching && m_hStudent && args.ArgC() > 1 )
  6578. {
  6579. eCoachCommand command = (eCoachCommand)atoi( args[1] );
  6580. HandleCoachCommand( this, command );
  6581. return true;
  6582. }
  6583. }
  6584. else if ( FStrEq( pcmd, "boo" ) && m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  6585. {
  6586. if ( m_booTimer.IsElapsed() )
  6587. {
  6588. m_booTimer.Start( 1.f );
  6589. EmitSound( "Halloween.GhostBoo" );
  6590. }
  6591. return true;
  6592. }
  6593. else if ( FStrEq( pcmd, "loot_response" ) )
  6594. {
  6595. // Only allowed to speak these during post-game MvM
  6596. if ( !TFGameRules()
  6597. || !TFGameRules()->IsMannVsMachineMode()
  6598. || !( TFGameRules()->State_Get() == GR_STATE_GAME_OVER ) )
  6599. {
  6600. return true;
  6601. }
  6602. if ( FStrEq( args[1], "common" ) )
  6603. {
  6604. SpeakConceptIfAllowed( MP_CONCEPT_MVM_LOOT_COMMON );
  6605. return true;
  6606. }
  6607. else if ( FStrEq( args[1], "rare" ) )
  6608. {
  6609. SpeakConceptIfAllowed( MP_CONCEPT_MVM_LOOT_RARE );
  6610. return true;
  6611. }
  6612. else if ( FStrEq( args[1], "ultra_rare" ) )
  6613. {
  6614. SpeakConceptIfAllowed( MP_CONCEPT_MVM_LOOT_ULTRARARE );
  6615. return true;
  6616. }
  6617. }
  6618. else if ( FStrEq( pcmd, "done_viewing_loot" ) )
  6619. {
  6620. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && g_pPopulationManager )
  6621. {
  6622. g_pPopulationManager->PlayerDoneViewingLoot( this );
  6623. }
  6624. return true;
  6625. }
  6626. else if ( FStrEq( pcmd, "spectate" ) )
  6627. {
  6628. HandleCommand_JoinTeam( "spectate" );
  6629. return true;
  6630. }
  6631. else if ( FStrEq( pcmd, "team_ui_setup" ) )
  6632. {
  6633. bool bAutoTeam = ShouldForceAutoTeam();
  6634. #ifdef TF_RAID_MODE
  6635. bAutoTeam |= TFGameRules()->IsBossBattleMode();
  6636. #endif
  6637. // For autoteam, display the appropriate team's CLASS selection ui
  6638. if ( bAutoTeam )
  6639. {
  6640. ChangeTeam( GetAutoTeam(), true, false );
  6641. ShowViewPortPanel( ( GetTeamNumber() == TF_TEAM_BLUE ) ? PANEL_CLASS_BLUE : PANEL_CLASS_RED );
  6642. }
  6643. // Otherwise, show TEAM selection ui
  6644. else
  6645. {
  6646. ShowViewPortPanel( PANEL_TEAM );
  6647. }
  6648. return true;
  6649. }
  6650. else if ( FStrEq( "next_map_vote", pcmd ) )
  6651. {
  6652. CTFGameRules::EUserNextMapVote eVoteState = (CTFGameRules::EUserNextMapVote)atoi( args[1] );
  6653. switch( eVoteState )
  6654. {
  6655. case CTFGameRules::USER_NEXT_MAP_VOTE_MAP_0:
  6656. case CTFGameRules::USER_NEXT_MAP_VOTE_MAP_1:
  6657. case CTFGameRules::USER_NEXT_MAP_VOTE_MAP_2:
  6658. // Valid
  6659. break;
  6660. default:
  6661. // Invalid
  6662. Assert( false );
  6663. return true;
  6664. }
  6665. // No flip flop!
  6666. if ( TFGameRules()->PlayerNextMapVoteState( entindex() ) != CTFGameRules::USER_NEXT_MAP_VOTE_UNDECIDED )
  6667. return true;
  6668. // Needs to do next-map voting
  6669. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  6670. if ( !pMatchDesc || !pMatchDesc->BUsesMapVoteAfterMatchEnds() )
  6671. return true;
  6672. if ( TFGameRules()->State_Get() != GR_STATE_GAME_OVER )
  6673. return true;
  6674. CMatchInfo* pMatch = GTFGCClientSystem()->GetMatch();
  6675. if ( !pMatch )
  6676. return true;
  6677. TFGameRules()->SetPlayerNextMapVote( entindex(), eVoteState );
  6678. DevMsg( "Settings player %d to rematch vote state %d.\n", entindex(), eVoteState );
  6679. return true;
  6680. }
  6681. #ifdef STAGING_ONLY
  6682. else if ( FStrEq( pcmd, "reload_extra_models" ) )
  6683. {
  6684. for ( int i = 0; i < CExtraMapEntity::AutoList().Count(); i++ )
  6685. {
  6686. CExtraMapEntity *pEntity = static_cast<CExtraMapEntity*>( CExtraMapEntity::AutoList()[i] );
  6687. UTIL_Remove( pEntity );
  6688. }
  6689. CExtraMapEntity::SpawnExtraModel();
  6690. return true;
  6691. }
  6692. #endif // STAGING_ONLY
  6693. return BaseClass::ClientCommand( args );
  6694. }
  6695. //-----------------------------------------------------------------------------
  6696. // Purpose:
  6697. //-----------------------------------------------------------------------------
  6698. void CTFPlayer::SetClassMenuOpen( bool bOpen )
  6699. {
  6700. m_bIsClassMenuOpen = bOpen;
  6701. }
  6702. //-----------------------------------------------------------------------------
  6703. // Purpose:
  6704. //-----------------------------------------------------------------------------
  6705. bool CTFPlayer::IsClassMenuOpen( void )
  6706. {
  6707. return m_bIsClassMenuOpen;
  6708. }
  6709. //-----------------------------------------------------------------------------
  6710. // Purpose:
  6711. //-----------------------------------------------------------------------------
  6712. void CTFPlayer::MerasmusPlayerBombExplode( bool bExcludeMe /*= true */ )
  6713. {
  6714. float flDamage = 40.0f;
  6715. // bomb head damage is 100 only for fighting Merasmus, lower for all other scenarios
  6716. if ( TFGameRules() && TFGameRules()->GetActiveBoss() && ( TFGameRules()->GetActiveBoss()->GetBossType() == HALLOWEEN_BOSS_MERASMUS ) )
  6717. {
  6718. flDamage = 100.0f;
  6719. }
  6720. // explode!
  6721. Vector vecExplosion = EyePosition();
  6722. CPVSFilter filter( vecExplosion );
  6723. TE_TFExplosion( filter, 0.0f, vecExplosion, Vector(0,0,1), NULL, entindex() );
  6724. int iDmgType = DMG_BLAST | DMG_USEDISTANCEMOD;
  6725. CTakeDamageInfo info( this, this, NULL, vecExplosion, vecExplosion, flDamage, iDmgType, TF_DMG_CUSTOM_MERASMUS_PLAYER_BOMB, &vecExplosion );
  6726. CBaseEntity *pIgnoreEnt = NULL;
  6727. if ( bExcludeMe )
  6728. {
  6729. pIgnoreEnt = this;
  6730. }
  6731. CTFRadiusDamageInfo radiusinfo( &info, vecExplosion, 100.f, pIgnoreEnt );
  6732. TFGameRules()->RadiusDamage( radiusinfo );
  6733. UTIL_ScreenShake( vecExplosion, 15.0f, 5.0f, 2.f, 750.f, SHAKE_START, true );
  6734. }
  6735. //-----------------------------------------------------------------------------
  6736. // Purpose:
  6737. //-----------------------------------------------------------------------------
  6738. void CTFPlayer::DropDeathCallingCard( CTFPlayer* pTFAttacker, CTFPlayer* pTFVictim )
  6739. {
  6740. int iCallingCard = 0;
  6741. CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFAttacker, iCallingCard, calling_card_on_kill );
  6742. if ( iCallingCard )
  6743. {
  6744. CEffectData data;
  6745. data.m_vOrigin = pTFVictim->GetAbsOrigin();
  6746. data.m_vAngles = pTFVictim->GetAbsAngles();
  6747. data.m_nAttachmentIndex = pTFVictim->entindex(); // Victim
  6748. data.m_nHitBox = entindex(); // iShooter
  6749. data.m_fFlags = iCallingCard; // Index to the Calling card
  6750. DispatchEffect( "TFDeathCallingCard", data );
  6751. }
  6752. }
  6753. //-----------------------------------------------------------------------------
  6754. // Purpose:
  6755. //-----------------------------------------------------------------------------
  6756. bool CTFPlayer::PlayGesture( const char *pGestureName )
  6757. {
  6758. Activity nActivity = (Activity)LookupActivity( pGestureName );
  6759. if ( nActivity != ACT_INVALID )
  6760. {
  6761. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, nActivity );
  6762. return true;
  6763. }
  6764. int nSequence = LookupSequence( pGestureName );
  6765. if ( nSequence != -1 )
  6766. {
  6767. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE, nSequence );
  6768. return true;
  6769. }
  6770. return false;
  6771. }
  6772. //-----------------------------------------------------------------------------
  6773. // Purpose:
  6774. //-----------------------------------------------------------------------------
  6775. bool CTFPlayer::PlaySpecificSequence( const char *pAnimationName )
  6776. {
  6777. Activity nActivity = (Activity)LookupActivity( pAnimationName );
  6778. if ( nActivity != ACT_INVALID )
  6779. {
  6780. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM, nActivity );
  6781. return true;
  6782. }
  6783. int nSequence = LookupSequence( pAnimationName );
  6784. if ( nSequence != -1 )
  6785. {
  6786. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_SEQUENCE, nSequence );
  6787. return true;
  6788. }
  6789. return false;
  6790. }
  6791. //-----------------------------------------------------------------------------
  6792. // Purpose:
  6793. //-----------------------------------------------------------------------------
  6794. void CTFPlayer::DetonateObjectOfType( int iType, int iMode, bool bIgnoreSapperState )
  6795. {
  6796. CBaseObject *pObj = GetObjectOfType( iType, iMode );
  6797. if( !pObj )
  6798. return;
  6799. if( !bIgnoreSapperState && ( pObj->HasSapper() || pObj->IsPlasmaDisabled() ) )
  6800. return;
  6801. IGameEvent *event = gameeventmanager->CreateEvent( "object_removed" );
  6802. if ( event )
  6803. {
  6804. event->SetInt( "userid", GetUserID() ); // user ID of the object owner
  6805. event->SetInt( "objecttype", iType ); // type of object removed
  6806. event->SetInt( "index", pObj->entindex() ); // index of the object removed
  6807. gameeventmanager->FireEvent( event );
  6808. }
  6809. if ( TFGameRules() && TFGameRules()->GetTrainingModeLogic() && IsFakeClient() == false )
  6810. {
  6811. TFGameRules()->GetTrainingModeLogic()->OnPlayerDetonateBuilding( this, pObj );
  6812. }
  6813. SpeakConceptIfAllowed( MP_CONCEPT_DETONATED_OBJECT, pObj->GetResponseRulesModifier() );
  6814. pObj->DetonateObject();
  6815. const CObjectInfo *pInfo = GetObjectInfo( iType );
  6816. if ( pInfo )
  6817. {
  6818. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"killedobject\" (object \"%s\") (weapon \"%s\") (objectowner \"%s<%i><%s><%s>\") (attacker_position \"%d %d %d\")\n",
  6819. GetPlayerName(),
  6820. GetUserID(),
  6821. GetNetworkIDString(),
  6822. GetTeam()->GetName(),
  6823. pInfo->m_pObjectName,
  6824. "pda_engineer",
  6825. GetPlayerName(),
  6826. GetUserID(),
  6827. GetNetworkIDString(),
  6828. GetTeam()->GetName(),
  6829. (int)GetAbsOrigin().x,
  6830. (int)GetAbsOrigin().y,
  6831. (int)GetAbsOrigin().z );
  6832. }
  6833. }
  6834. //-----------------------------------------------------------------------------
  6835. // Purpose:
  6836. //-----------------------------------------------------------------------------
  6837. float CTFPlayer::GetObjectBuildSpeedMultiplier( int iObjectType, bool bIsRedeploy ) const
  6838. {
  6839. float flBuildRate = 1.f; // need a base value for mult
  6840. switch( iObjectType )
  6841. {
  6842. case OBJ_SENTRYGUN:
  6843. CALL_ATTRIB_HOOK_FLOAT( flBuildRate, sentry_build_rate_multiplier );
  6844. flBuildRate += bIsRedeploy ? 2.0 : 0.0f;
  6845. break;
  6846. case OBJ_TELEPORTER:
  6847. CALL_ATTRIB_HOOK_FLOAT( flBuildRate, teleporter_build_rate_multiplier );
  6848. flBuildRate += bIsRedeploy ? 3.0 : 0.0f;
  6849. break;
  6850. case OBJ_DISPENSER:
  6851. CALL_ATTRIB_HOOK_FLOAT( flBuildRate, teleporter_build_rate_multiplier );
  6852. flBuildRate += bIsRedeploy ? 3.0 : 0.0f;
  6853. break;
  6854. #ifdef STAGING_ONLY
  6855. // STAGING_ENGY
  6856. case OBJ_CATAPULT:
  6857. flBuildRate += 5.0f;
  6858. flBuildRate += bIsRedeploy ? 3.0 : 0.0f;
  6859. break;
  6860. #endif
  6861. }
  6862. return flBuildRate - 1.0f; // sub out the initial 1 so the final result is added
  6863. }
  6864. //-----------------------------------------------------------------------------
  6865. // Purpose:
  6866. //-----------------------------------------------------------------------------
  6867. void CTFPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  6868. {
  6869. if ( m_takedamage != DAMAGE_YES )
  6870. return;
  6871. CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() );
  6872. if ( pAttacker )
  6873. {
  6874. // Weapons that use uber ammo can transfer that uber into other medics
  6875. if ( pAttacker->IsPlayerClass( TF_CLASS_MEDIC ) && IsPlayerClass( TF_CLASS_MEDIC ) )
  6876. {
  6877. CTFWeaponBase *pWep = pAttacker->GetActiveTFWeapon();
  6878. if ( pWep )
  6879. {
  6880. float flUberTransfer = pWep->UberChargeAmmoPerShot();
  6881. if ( flUberTransfer > 0.0f )
  6882. {
  6883. float flTransferPercent = 0.0f;
  6884. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWep, flTransferPercent, ubercharge_transfer );
  6885. if ( flTransferPercent )
  6886. {
  6887. flUberTransfer *= ( flTransferPercent * 0.01f );
  6888. CWeaponMedigun *pMedigun = static_cast< CWeaponMedigun * >( Weapon_OwnsThisID( TF_WEAPON_MEDIGUN ) );
  6889. if ( pMedigun )
  6890. {
  6891. pMedigun->AddCharge( flUberTransfer );
  6892. }
  6893. }
  6894. }
  6895. }
  6896. }
  6897. // Prevent team damage here so blood doesn't appear
  6898. if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker, info ) )
  6899. {
  6900. return;
  6901. }
  6902. }
  6903. // Save this bone for the ragdoll.
  6904. m_nForceBone = ptr->physicsbone;
  6905. SetLastHitGroup( ptr->hitgroup );
  6906. // Ignore hitboxes for all weapons except the sniper rifle
  6907. CTakeDamageInfo info_modified = info;
  6908. bool bIsHeadshot = false;
  6909. if ( info_modified.GetDamageType() & DMG_USE_HITLOCATIONS )
  6910. {
  6911. if ( !m_Shared.InCond( TF_COND_INVULNERABLE ) && ptr->hitgroup == HITGROUP_HEAD )
  6912. {
  6913. CTFWeaponBase *pWpn = pAttacker->GetActiveTFWeapon();
  6914. bool bCritical = true;
  6915. bIsHeadshot = true;
  6916. if ( pWpn && !pWpn->CanFireCriticalShot( true ) )
  6917. {
  6918. bCritical = false;
  6919. }
  6920. int iBackheadshot = 0;
  6921. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetInflictor(), iBackheadshot, back_headshot );
  6922. if ( iBackheadshot )
  6923. {
  6924. // only allow if hit in the back of the head
  6925. Vector entForward;
  6926. AngleVectors( EyeAngles(), &entForward );
  6927. Vector toEnt = GetAbsOrigin() - pAttacker->GetAbsOrigin();
  6928. toEnt.NormalizeInPlace();
  6929. // did not backshot
  6930. //if ( DotProduct( toEnt, entForward ) <= 0.7071f ) // 0.7 os 45 degress from center
  6931. if ( DotProduct( toEnt, entForward ) < 0.5f ) // 60 degrees from center (total of 120)
  6932. {
  6933. bCritical = false;
  6934. bIsHeadshot = false;
  6935. }
  6936. }
  6937. // Check for headshot damage modifiers
  6938. float flHeadshotModifier = 1.0f;
  6939. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER ( pAttacker, flHeadshotModifier, headshot_damage_modify);
  6940. info_modified.ScaleDamage(flHeadshotModifier);
  6941. if ( bCritical )
  6942. {
  6943. info_modified.AddDamageType( DMG_CRITICAL );
  6944. int iDecapType = 0;
  6945. CALL_ATTRIB_HOOK_INT_ON_OTHER ( pAttacker, iDecapType, decapitate_type);
  6946. if ( iDecapType > 0 )
  6947. {
  6948. info_modified.SetDamageCustom( TF_DMG_CUSTOM_HEADSHOT_DECAPITATION );
  6949. }
  6950. else
  6951. {
  6952. info_modified.SetDamageCustom( TF_DMG_CUSTOM_HEADSHOT );
  6953. }
  6954. // play the critical shot sound to the shooter
  6955. if ( pWpn )
  6956. {
  6957. pWpn->WeaponSound( BURST );
  6958. }
  6959. }
  6960. }
  6961. }
  6962. if ( !bIsHeadshot && pAttacker )
  6963. {
  6964. // Check for bodyshot damage modifiers
  6965. CTFWeaponBase *pWpn = pAttacker->GetActiveTFWeapon();
  6966. float flBodyshotModifier = 1.0f;
  6967. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER ( pWpn, flBodyshotModifier, bodyshot_damage_modify);
  6968. info_modified.ScaleDamage( flBodyshotModifier );
  6969. }
  6970. if ( GetTeamNumber() == TF_TEAM_BLUE )
  6971. {
  6972. info_modified.SetDamage( info_modified.GetDamage() * tf_damage_multiplier_blue.GetFloat() );
  6973. }
  6974. else if ( GetTeamNumber() == TF_TEAM_RED )
  6975. {
  6976. info_modified.SetDamage( info_modified.GetDamage() * tf_damage_multiplier_red.GetFloat() );
  6977. }
  6978. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  6979. {
  6980. // no impact effects
  6981. }
  6982. else if ( m_Shared.IsInvulnerable() )
  6983. {
  6984. // Make bullet impacts
  6985. g_pEffects->Ricochet( ptr->endpos - (vecDir * 8), -vecDir );
  6986. }
  6987. else
  6988. {
  6989. // Since this code only runs on the server, make sure it shows the tempents it creates.
  6990. CDisablePredictionFiltering disabler;
  6991. // This does smaller splotches on the guy and splats blood on the world.
  6992. TraceBleed( info_modified.GetDamage(), vecDir, ptr, info_modified.GetDamageType() );
  6993. }
  6994. AddMultiDamage( info_modified, this );
  6995. }
  6996. //-----------------------------------------------------------------------------
  6997. // Purpose:
  6998. //-----------------------------------------------------------------------------
  6999. int CTFPlayer::TakeHealth( float flHealth, int bitsDamageType )
  7000. {
  7001. if ( m_Shared.InCond( TF_COND_NOHEALINGDAMAGEBUFF ) )
  7002. {
  7003. return 0; // No healing while in this state!
  7004. }
  7005. int nResult = 0;
  7006. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  7007. if ( pWeapon )
  7008. {
  7009. float flHealingBonus = 1.f;
  7010. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flHealingBonus, mult_healing_received );
  7011. flHealth *= flHealingBonus;
  7012. }
  7013. // Medigun healing and player/class regen use an accumulator, so they've already factored in debuffs.
  7014. // if ( m_Shared.InCond( TF_COND_HEALING_DEBUFF ) && !( bitsDamageType & DMG_IGNORE_DEBUFFS ) )
  7015. // {
  7016. // flHealth *= 0.75f;
  7017. // }
  7018. // If the bit's set, add over the max health
  7019. if ( bitsDamageType & DMG_IGNORE_MAXHEALTH )
  7020. {
  7021. int iTimeBasedDamage = g_pGameRules->Damage_GetTimeBased();
  7022. m_bitsDamageType &= ~(bitsDamageType & ~iTimeBasedDamage);
  7023. m_iHealth += flHealth;
  7024. nResult = flHealth;
  7025. }
  7026. else
  7027. {
  7028. float flHealthToAdd = flHealth;
  7029. float flMaxHealth = GetMaxHealth();
  7030. // don't want to add more than we're allowed to have
  7031. if ( flHealthToAdd > flMaxHealth - m_iHealth )
  7032. {
  7033. flHealthToAdd = flMaxHealth - m_iHealth;
  7034. }
  7035. if ( flHealthToAdd <= 0 )
  7036. {
  7037. nResult = 0;
  7038. }
  7039. else
  7040. {
  7041. nResult = BaseClass::TakeHealth( flHealthToAdd, bitsDamageType );
  7042. }
  7043. }
  7044. return nResult;
  7045. }
  7046. //-----------------------------------------------------------------------------
  7047. // Purpose:
  7048. //-----------------------------------------------------------------------------
  7049. void CTFPlayer::TFWeaponRemove( int iWeaponID )
  7050. {
  7051. // find the weapon that matches the id and remove it
  7052. int i;
  7053. for (i = 0; i < WeaponCount(); i++)
  7054. {
  7055. CTFWeaponBase *pWeapon = ( CTFWeaponBase *)GetWeapon( i );
  7056. if ( !pWeapon )
  7057. continue;
  7058. if ( pWeapon->GetWeaponID() != iWeaponID )
  7059. continue;
  7060. RemovePlayerItem( pWeapon );
  7061. UTIL_Remove( pWeapon );
  7062. }
  7063. }
  7064. //-----------------------------------------------------------------------------
  7065. // Purpose:
  7066. //-----------------------------------------------------------------------------
  7067. bool CTFPlayer::BumpWeapon( CBaseCombatWeapon *pWeapon )
  7068. {
  7069. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  7070. // Can I have this weapon type?
  7071. if ( !IsAllowedToPickupWeapons() )
  7072. return false;
  7073. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  7074. {
  7075. UTIL_Remove( pWeapon );
  7076. return false;
  7077. }
  7078. // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
  7079. if ( !pWeapon->FVisible( this, MASK_SOLID ) )
  7080. return false;
  7081. // ----------------------------------------
  7082. // If I already have it just take the ammo
  7083. // ----------------------------------------
  7084. if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()))
  7085. {
  7086. UTIL_Remove( pWeapon );
  7087. return true;
  7088. }
  7089. else
  7090. {
  7091. // -------------------------
  7092. // Otherwise take the weapon
  7093. // -------------------------
  7094. pWeapon->CheckRespawn();
  7095. pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
  7096. pWeapon->AddEffects( EF_NODRAW );
  7097. Weapon_Equip( pWeapon );
  7098. return true;
  7099. }
  7100. }
  7101. //-----------------------------------------------------------------------------
  7102. // Purpose:
  7103. //-----------------------------------------------------------------------------
  7104. bool CTFPlayer::DropCurrentWeapon( void )
  7105. {
  7106. return false;
  7107. }
  7108. //-----------------------------------------------------------------------------
  7109. // Purpose:
  7110. //-----------------------------------------------------------------------------
  7111. void CTFPlayer::DropFlag( bool bSilent /* = false */ )
  7112. {
  7113. if ( HasItem() )
  7114. {
  7115. CCaptureFlag *pFlag = dynamic_cast<CCaptureFlag*>( GetItem() );
  7116. if ( pFlag )
  7117. {
  7118. int nFlagTeamNumber = pFlag->GetTeamNumber();
  7119. pFlag->Drop( this, true, true, !bSilent );
  7120. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  7121. if ( event )
  7122. {
  7123. event->SetInt( "player", entindex() );
  7124. event->SetInt( "eventtype", TF_FLAGEVENT_DROPPED );
  7125. event->SetInt( "priority", 8 );
  7126. event->SetInt( "team", nFlagTeamNumber );
  7127. gameeventmanager->FireEvent( event );
  7128. }
  7129. }
  7130. }
  7131. }
  7132. //-----------------------------------------------------------------------------
  7133. // Purpose: Players can drop Powerup Runes
  7134. //-----------------------------------------------------------------------------
  7135. void CTFPlayer::DropRune( bool bApplyForce /* = true */, int nTeam /* = TEAM_ANY */ )
  7136. {
  7137. if ( m_Shared.IsCarryingRune() )
  7138. {
  7139. Vector forward;
  7140. EyeVectors( &forward );
  7141. RuneTypes_t nRuneType = m_Shared.GetCarryingRuneType();
  7142. // We expect that we are actually are carrying here, so assert that we are.
  7143. Assert( nRuneType >= 0 && nRuneType < RUNE_TYPES_MAX );
  7144. m_Shared.SetCarryingRuneType( RUNE_NONE );
  7145. bool bShouldRemoveMeleeOnly = !( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) && m_Shared.InCond( TF_COND_ENERGY_BUFF ) );
  7146. if ( bShouldRemoveMeleeOnly )
  7147. {
  7148. m_Shared.RemoveCond( TF_COND_CANNOT_SWITCH_FROM_MELEE ); // Knockout powerup sets this to on
  7149. }
  7150. TeamFortress_SetSpeed(); // Need to call this or speed bonus isn't removed immediately
  7151. CTFRune::CreateRune( GetAbsOrigin(), nRuneType, nTeam, true, bApplyForce, forward ); // Manually dropped powerups are always neutral
  7152. }
  7153. }
  7154. //-----------------------------------------------------------------------------
  7155. // Purpose:
  7156. //-----------------------------------------------------------------------------
  7157. EHANDLE CTFPlayer::TeamFortress_GetDisguiseTarget( int nTeam, int nClass )
  7158. {
  7159. if ( /*nTeam == GetTeamNumber() ||*/ nTeam == TF_SPY_UNDEFINED )
  7160. {
  7161. // we're not disguised as the enemy team
  7162. return NULL;
  7163. }
  7164. CUtlVector<int> potentialTargets;
  7165. CBaseEntity *pLastTarget = m_Shared.GetDisguiseTarget(); // don't redisguise self as this person
  7166. // Find a player on the team the spy is disguised as to pretend to be
  7167. CTFPlayer *pPlayer = NULL;
  7168. // Loop through players and attempt to find a player as the team/class we're disguising as
  7169. int i;
  7170. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  7171. {
  7172. pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  7173. if ( pPlayer && ( pPlayer != pLastTarget ) )
  7174. {
  7175. // First, try to find a player with the same color AND skin
  7176. if ( ( pPlayer->GetTeamNumber() == nTeam ) && ( pPlayer->GetPlayerClass()->GetClassIndex() == nClass ) )
  7177. {
  7178. potentialTargets.AddToHead( i );
  7179. }
  7180. }
  7181. }
  7182. // do we have any potential targets in the list?
  7183. if ( potentialTargets.Count() > 0 )
  7184. {
  7185. int iIndex = random->RandomInt( 0, potentialTargets.Count() - 1 );
  7186. return UTIL_PlayerByIndex( potentialTargets[iIndex] );
  7187. }
  7188. // we didn't find someone with the class, so just find someone with the same team color
  7189. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  7190. {
  7191. pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  7192. if ( pPlayer && ( pPlayer->GetTeamNumber() == nTeam ) )
  7193. {
  7194. potentialTargets.AddToHead( i );
  7195. }
  7196. }
  7197. if ( potentialTargets.Count() > 0 )
  7198. {
  7199. int iIndex = random->RandomInt( 0, potentialTargets.Count() - 1 );
  7200. return UTIL_PlayerByIndex( potentialTargets[iIndex] );
  7201. }
  7202. // we didn't find anyone
  7203. return NULL;
  7204. }
  7205. //-----------------------------------------------------------------------------
  7206. // Purpose:
  7207. //-----------------------------------------------------------------------------
  7208. float DamageForce( const Vector &size, float damage, float scale )
  7209. {
  7210. float force = damage * ((48 * 48 * 82.0) / (size.x * size.y * size.z)) * scale;
  7211. if ( force > 1000.0 )
  7212. {
  7213. force = 1000.0;
  7214. }
  7215. return force;
  7216. }
  7217. //-----------------------------------------------------------------------------
  7218. // Purpose:
  7219. //-----------------------------------------------------------------------------
  7220. void CTFPlayer::SetBlastJumpState( int iState, bool bPlaySound /*= false*/ )
  7221. {
  7222. m_iBlastJumpState |= iState;
  7223. const char *pszEvent = NULL;
  7224. if ( iState == TF_PLAYER_STICKY_JUMPED )
  7225. {
  7226. pszEvent = "sticky_jump";
  7227. }
  7228. else if ( iState == TF_PLAYER_ROCKET_JUMPED )
  7229. {
  7230. pszEvent = "rocket_jump";
  7231. }
  7232. if ( pszEvent )
  7233. {
  7234. IGameEvent * event = gameeventmanager->CreateEvent( pszEvent );
  7235. if ( event )
  7236. {
  7237. event->SetInt( "userid", GetUserID() );
  7238. event->SetBool( "playsound", bPlaySound );
  7239. gameeventmanager->FireEvent( event );
  7240. }
  7241. }
  7242. m_Shared.AddCond( TF_COND_BLASTJUMPING );
  7243. }
  7244. //-----------------------------------------------------------------------------
  7245. // Purpose:
  7246. //-----------------------------------------------------------------------------
  7247. void CTFPlayer::ClearBlastJumpState( void )
  7248. {
  7249. m_bCreatedRocketJumpParticles = false;
  7250. m_iBlastJumpState = 0;
  7251. m_flBlastJumpLandTime = gpGlobals->curtime;
  7252. m_Shared.RemoveCond( TF_COND_BLASTJUMPING );
  7253. }
  7254. //-----------------------------------------------------------------------------
  7255. // Purpose:
  7256. //-----------------------------------------------------------------------------
  7257. void HandleRageGain( CTFPlayer *pPlayer, unsigned int iRequiredBuffFlags, float flDamage, float fInverseRageGainScale )
  7258. {
  7259. if ( !pPlayer )
  7260. return;
  7261. if ( pPlayer->IsPlayerClass( TF_CLASS_SOLDIER ) )
  7262. {
  7263. CTFBuffItem *pBuffItem = dynamic_cast<CTFBuffItem*>( pPlayer->Weapon_OwnsThisID( TF_WEAPON_BUFF_ITEM ) );
  7264. unsigned int iBuffId = pBuffItem ? pBuffItem->GetBuffType() : 0;
  7265. if ( iBuffId < ARRAYSIZE( g_RageBuffTypes ) )
  7266. {
  7267. if ( g_RageBuffTypes[iBuffId].m_iBuffFlags & iRequiredBuffFlags )
  7268. {
  7269. pPlayer->m_Shared.ModifyRage( g_RageBuffTypes[iBuffId].m_fRageScale * ( flDamage / fInverseRageGainScale ) );
  7270. }
  7271. }
  7272. }
  7273. else if ( pPlayer->IsPlayerClass( TF_CLASS_PYRO ) )
  7274. {
  7275. CTFFlameThrower *pFlameThrower = dynamic_cast<CTFFlameThrower*>( pPlayer->Weapon_OwnsThisID( TF_WEAPON_FLAMETHROWER ) );
  7276. unsigned int iBuffId = pFlameThrower ? pFlameThrower->GetBuffType() : 0;
  7277. if ( iBuffId < ARRAYSIZE( g_RageBuffTypes ) )
  7278. {
  7279. if ( g_RageBuffTypes[iBuffId].m_iBuffFlags & iRequiredBuffFlags )
  7280. {
  7281. if ( TFGameRules() && TFGameRules()->IsPowerupMode() && pPlayer->m_Shared.GetCarryingRuneType() != RUNE_NONE )
  7282. {
  7283. pPlayer->m_Shared.ModifyRage(g_RageBuffTypes[iBuffId].m_fRageScale * ( ( flDamage / 10 ) / fInverseRageGainScale) );
  7284. }
  7285. else
  7286. {
  7287. pPlayer->m_Shared.ModifyRage( g_RageBuffTypes[iBuffId].m_fRageScale * ( flDamage / fInverseRageGainScale ) );
  7288. }
  7289. }
  7290. }
  7291. }
  7292. // General
  7293. int iRage = 0;
  7294. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iRage, generate_rage_on_dmg );
  7295. if ( iRage )
  7296. {
  7297. if ( pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) && ( kRageBuffFlag_OnDamageDealt & iRequiredBuffFlags ) )
  7298. {
  7299. pPlayer->m_Shared.ModifyRage( flDamage );
  7300. }
  7301. else if ( pPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) && ( kRageBuffFlag_OnDamageDealt & iRequiredBuffFlags ) )
  7302. {
  7303. pPlayer->m_Shared.ModifyRage( 0.22f * ( flDamage / fInverseRageGainScale ) );
  7304. }
  7305. }
  7306. int iHealRage = 0;
  7307. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iHealRage, generate_rage_on_heal ); // ...lol
  7308. if ( iHealRage )
  7309. {
  7310. if ( pPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && ( kRageBuffFlag_OnHeal & iRequiredBuffFlags ) )
  7311. {
  7312. pPlayer->m_Shared.ModifyRage( 0.25f * flDamage );
  7313. }
  7314. }
  7315. #ifdef STAGING_ONLY
  7316. if ( TFGameRules()->GameModeUsesUpgrades() && pPlayer->IsPlayerClass( TF_CLASS_SPY ) && ( kRageBuffFlag_OnDamageDealt & iRequiredBuffFlags ) )
  7317. {
  7318. // Don't allow when radius cloak is in effect
  7319. if ( !pPlayer->m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF ) )
  7320. {
  7321. pPlayer->m_Shared.ModifyRage( 0.025f * flDamage );
  7322. }
  7323. }
  7324. #endif // STAGING_ONLY
  7325. }
  7326. // we want to ship this...do not remove
  7327. ConVar tf_debug_damage( "tf_debug_damage", "0", FCVAR_CHEAT );
  7328. //-----------------------------------------------------------------------------
  7329. // Purpose:
  7330. //-----------------------------------------------------------------------------
  7331. int CTFPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  7332. {
  7333. CTakeDamageInfo info = inputInfo;
  7334. bool bIsObject = info.GetInflictor() && info.GetInflictor()->IsBaseObject();
  7335. // need to check this now, before dying
  7336. bool bHadBallBeforeDamage = false;
  7337. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  7338. {
  7339. bHadBallBeforeDamage = m_Shared.HasPasstimeBall();
  7340. }
  7341. // damage may not come from a weapon (ie: Bosses, etc)
  7342. // The existing code below already checked for NULL pWeapon, anyways
  7343. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( inputInfo.GetWeapon() );
  7344. if ( GetFlags() & FL_GODMODE )
  7345. return 0;
  7346. if ( IsInCommentaryMode() )
  7347. return 0;
  7348. bool bBuddha = ( m_debugOverlays & OVERLAY_BUDDHA_MODE ) ? true : false;
  7349. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  7350. if ( mp_developer.GetInt() > 1 && !IsBot() )
  7351. bBuddha = true;
  7352. #endif // _DEBUG || STAGING_ONLY
  7353. if ( bBuddha )
  7354. {
  7355. if ( ( m_iHealth - info.GetDamage() ) <= 0 )
  7356. {
  7357. m_iHealth = 1;
  7358. return 0;
  7359. }
  7360. }
  7361. if ( !IsAlive() )
  7362. return 0;
  7363. // Early out if there's no damage
  7364. if ( !info.GetDamage() )
  7365. return 0;
  7366. // Ghosts dont take damage
  7367. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  7368. {
  7369. return 0;
  7370. }
  7371. CBaseEntity *pAttacker = info.GetAttacker();
  7372. CTFPlayer *pTFAttacker = ToTFPlayer( pAttacker );
  7373. bool bDebug = tf_debug_damage.GetBool();
  7374. // If attacker has Strength Powerup Rune, apply damage multiplier, but not if you're a building
  7375. if ( !bIsObject && pTFAttacker && pTFAttacker->m_Shared.GetCarryingRuneType() == RUNE_STRENGTH )
  7376. {
  7377. info.ScaleDamage( 2.f );
  7378. }
  7379. // Make sure the player can take damage from the attacking entity
  7380. if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker, info ) )
  7381. {
  7382. if ( bDebug )
  7383. {
  7384. Warning( " ABORTED: Player can't take damage from that attacker.\n" );
  7385. }
  7386. return 0;
  7387. }
  7388. if ( IsBot() )
  7389. {
  7390. // Don't let Sentry Busters die until they've done their spin-up
  7391. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  7392. {
  7393. CTFBot *bot = ToTFBot( this );
  7394. if ( bot )
  7395. {
  7396. if ( bot->HasMission( CTFBot::MISSION_DESTROY_SENTRIES ) )
  7397. {
  7398. if ( ( m_iHealth - info.GetDamage() ) <= 0 )
  7399. {
  7400. m_iHealth = 1;
  7401. return 0;
  7402. }
  7403. }
  7404. // Sentry Busters hurt teammates when they explode.
  7405. // Force damage value when the victim is a giant.
  7406. if ( pTFAttacker && pTFAttacker->IsBot() )
  7407. {
  7408. CTFBot *pTFAttackerBot = ToTFBot( pTFAttacker );
  7409. if ( pTFAttackerBot &&
  7410. ( pTFAttackerBot != this ) &&
  7411. pTFAttackerBot->GetPrevMission() == CTFBot::MISSION_DESTROY_SENTRIES &&
  7412. info.IsForceFriendlyFire() &&
  7413. InSameTeam( pTFAttackerBot ) &&
  7414. IsMiniBoss() )
  7415. {
  7416. info.SetDamage( 600.f );
  7417. }
  7418. }
  7419. }
  7420. }
  7421. }
  7422. // Halloween 2011
  7423. if ( IsInPurgatory() )
  7424. {
  7425. info.SetDamage( m_purgatoryPainMultiplier * info.GetDamage() );
  7426. }
  7427. m_iHealthBefore = GetHealth();
  7428. bool bIsSoldierRocketJumping = ( IsPlayerClass( TF_CLASS_SOLDIER ) && (pAttacker == this) && !(GetFlags() & FL_ONGROUND) && !(GetFlags() & FL_INWATER)) && (inputInfo.GetDamageType() & DMG_BLAST);
  7429. bool bIsDemomanPipeJumping = ( IsPlayerClass( TF_CLASS_DEMOMAN) && (pAttacker == this) && !(GetFlags() & FL_ONGROUND) && !(GetFlags() & FL_INWATER)) && (inputInfo.GetDamageType() & DMG_BLAST);
  7430. if ( bDebug )
  7431. {
  7432. Warning( "%s taking damage from %s, via %s. Damage: %.2f\n", GetDebugName(), info.GetInflictor() ? info.GetInflictor()->GetDebugName() : "Unknown Inflictor", pAttacker ? pAttacker->GetDebugName() : "Unknown Attacker", info.GetDamage() );
  7433. }
  7434. if ( pTFAttacker )
  7435. {
  7436. pTFAttacker->SetLastEntityDamagedTime( gpGlobals->curtime );
  7437. pTFAttacker->SetLastEntityDamaged( this );
  7438. CTFWeaponBase *myWeapon = GetActiveTFWeapon();
  7439. CTFWeaponBase *attackerWeapon = pTFAttacker->GetActiveTFWeapon();
  7440. if ( myWeapon && attackerWeapon )
  7441. {
  7442. int iStunEnemyWithSameWeapon = 0;
  7443. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( attackerWeapon, iStunEnemyWithSameWeapon, stun_enemies_wielding_same_weapon );
  7444. if ( iStunEnemyWithSameWeapon )
  7445. {
  7446. CEconItemView *myItem = myWeapon->GetAttributeContainer()->GetItem();
  7447. CEconItemView *attackerItem = attackerWeapon->GetAttributeContainer()->GetItem();
  7448. if ( myItem && attackerItem && myItem->GetItemDefIndex() == attackerItem->GetItemDefIndex() )
  7449. {
  7450. // we're both wielding the same weapon - stun!
  7451. m_Shared.StunPlayer( 1.0f, 1.0, TF_STUN_BOTH | TF_STUN_NO_EFFECTS );
  7452. }
  7453. }
  7454. }
  7455. }
  7456. if ( ( info.GetDamageType() & DMG_FALL ) )
  7457. {
  7458. bool bHitEnemy = false;
  7459. // Are we transferring falling damage to someone else?
  7460. // Space Gravity gives everyone manntreads effect. Mantreads just makes it higher
  7461. int iHeadStomp = 0;
  7462. CALL_ATTRIB_HOOK_INT( iHeadStomp, boots_falling_stomp );
  7463. //#ifdef STAGING_ONLY
  7464. // if ( ( iHeadStomp || m_Shared.InCond( TF_COND_SPACE_GRAVITY ) ) &&
  7465. //#else
  7466. if ( iHeadStomp &&
  7467. //#endif // STAGING_ONLY
  7468. GetGroundEntity() &&
  7469. GetGroundEntity()->IsPlayer() )
  7470. {
  7471. // Did we land on a guy from the enemy team?
  7472. CTFPlayer *pOther = ToTFPlayer( GetGroundEntity() );
  7473. if ( pOther && pOther->GetTeamNumber() != GetTeamNumber() )
  7474. {
  7475. float flStompDamage = info.GetDamage();
  7476. if ( iHeadStomp )
  7477. {
  7478. flStompDamage = 10.0f + flStompDamage * 3.0f;
  7479. }
  7480. CTakeDamageInfo infoInner( this, this, GetEquippedWearableForLoadoutSlot( LOADOUT_POSITION_SECONDARY ), flStompDamage, DMG_FALL, TF_DMG_CUSTOM_BOOTS_STOMP );
  7481. pOther->TakeDamage( infoInner );
  7482. m_Local.m_flFallVelocity = 0;
  7483. info.SetDamage( 0.0f );
  7484. EmitSound( "Weapon_Mantreads.Impact" );
  7485. UTIL_ScreenShake( pOther->WorldSpaceCenter(), 15.0, 150.0, 1.0, 500, SHAKE_START );
  7486. bHitEnemy = true;
  7487. }
  7488. }
  7489. #ifdef STAGING_ONLY
  7490. /*
  7491. // no fall damage in space
  7492. if ( m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  7493. {
  7494. info.SetDamage( 0.0f );
  7495. }
  7496. */
  7497. // Apply an impact stun (intensity determined by fall damage for now)
  7498. if ( TFGameRules()->GameModeUsesUpgrades() && m_Shared.InCond( TF_COND_ROCKETPACK ) )
  7499. {
  7500. float flStunTime = RemapValClamped( info.GetDamage(), 0.1f, 50.f, 1.f, 3.f );
  7501. m_Shared.ApplyRocketPackStun( bHitEnemy ? 5.f : flStunTime );
  7502. info.SetDamage( 0.f );
  7503. m_Local.m_flFallVelocity = 0.f;
  7504. }
  7505. #endif // STAGING_ONLY
  7506. }
  7507. // Ignore damagers on our team, to prevent capturing rocket jumping, etc.
  7508. if ( pAttacker && pAttacker->GetTeam() != GetTeam() )
  7509. {
  7510. m_AchievementData.AddDamagerToHistory( pAttacker );
  7511. if ( pAttacker->IsPlayer() )
  7512. {
  7513. ToTFPlayer( pAttacker )->m_AchievementData.AddTargetToHistory( this );
  7514. // add to list of damagers via sentry so that later we can check for achievement: ACHIEVEMENT_TF_ENGINEER_SHOTGUN_KILL_PREV_SENTRY_TARGET
  7515. CBaseEntity *pInflictor = info.GetInflictor();
  7516. CObjectSentrygun *pSentry = dynamic_cast< CObjectSentrygun * >( pInflictor );
  7517. if ( pSentry )
  7518. {
  7519. m_AchievementData.AddSentryDamager( pAttacker, pInflictor );
  7520. }
  7521. }
  7522. }
  7523. // keep track of amount of damage last sustained
  7524. m_lastDamageAmount = info.GetDamage();
  7525. m_LastDamageType = info.GetDamageType();
  7526. if ( m_LastDamageType & DMG_FALL )
  7527. {
  7528. if ( ( m_lastDamageAmount > m_iLeftGroundHealth ) && ( m_lastDamageAmount < GetHealth() ) )
  7529. {
  7530. // we gained health in the air, and it saved us from death.
  7531. // if any medics are healing us, they get an achievement
  7532. int iNumHealers = m_Shared.GetNumHealers();
  7533. for ( int i=0;i<iNumHealers;i++ )
  7534. {
  7535. CTFPlayer *pMedic = ToTFPlayer( m_Shared.GetHealerByIndex(i) );
  7536. // if its a medic healing us
  7537. if ( pMedic && pMedic->IsPlayerClass( TF_CLASS_MEDIC ) )
  7538. {
  7539. pMedic->AwardAchievement( ACHIEVEMENT_TF_MEDIC_SAVE_FALLING_TEAMMATE );
  7540. }
  7541. }
  7542. }
  7543. }
  7544. // Check for Demo Achievement:
  7545. // Kill a Heavy from full health with one detonation
  7546. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  7547. {
  7548. if ( pTFAttacker && pTFAttacker->IsPlayerClass( TF_CLASS_DEMOMAN ) )
  7549. {
  7550. if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_PIPEBOMBLAUNCHER )
  7551. {
  7552. // We're at full health
  7553. if ( m_iHealthBefore >= GetMaxHealth() )
  7554. {
  7555. // Record the time
  7556. m_fMaxHealthTime = gpGlobals->curtime;
  7557. }
  7558. // If we're still being hit in the same time window
  7559. if ( m_fMaxHealthTime == gpGlobals->curtime )
  7560. {
  7561. // Check if the damage is fatal
  7562. int iDamage = info.GetDamage();
  7563. if ( m_iHealth - iDamage <= 0 )
  7564. {
  7565. pTFAttacker->AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL_X_HEAVIES_FULLHP_ONEDET );
  7566. }
  7567. }
  7568. }
  7569. }
  7570. }
  7571. if ( bIsSoldierRocketJumping || bIsDemomanPipeJumping )
  7572. {
  7573. int nJumpType = 0;
  7574. // If this is our own rocket, scale down the damage if we're rocket jumping
  7575. if ( bIsSoldierRocketJumping )
  7576. {
  7577. float flDamage = info.GetDamage() * tf_damagescale_self_soldier.GetFloat();
  7578. info.SetDamage( flDamage );
  7579. if ( m_iHealthBefore - flDamage > 0 )
  7580. {
  7581. nJumpType = TF_PLAYER_ROCKET_JUMPED;
  7582. }
  7583. }
  7584. else if ( bIsDemomanPipeJumping )
  7585. {
  7586. nJumpType = TF_PLAYER_STICKY_JUMPED;
  7587. }
  7588. if ( nJumpType )
  7589. {
  7590. bool bPlaySound = false;
  7591. if ( pWeapon )
  7592. {
  7593. int iNoBlastDamage = 0;
  7594. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iNoBlastDamage, no_self_blast_dmg )
  7595. bPlaySound = iNoBlastDamage ? true : false;
  7596. }
  7597. SetBlastJumpState( nJumpType, bPlaySound );
  7598. }
  7599. }
  7600. if ( TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  7601. {
  7602. // can only bounce invaders when they are on the ground
  7603. if ( GetGroundEntity() == NULL )
  7604. {
  7605. info.SetDamageForce( vec3_origin );
  7606. }
  7607. }
  7608. // Save damage force for ragdolls.
  7609. m_vecTotalBulletForce = info.GetDamageForce();
  7610. m_vecTotalBulletForce.x = clamp( m_vecTotalBulletForce.x, -15000.0f, 15000.0f );
  7611. m_vecTotalBulletForce.y = clamp( m_vecTotalBulletForce.y, -15000.0f, 15000.0f );
  7612. m_vecTotalBulletForce.z = clamp( m_vecTotalBulletForce.z, -15000.0f, 15000.0f );
  7613. int bTookDamage = 0;
  7614. int bitsDamage = inputInfo.GetDamageType();
  7615. bool bAllowDamage = false;
  7616. // check to see if our attacker is a trigger_hurt entity (and allow it to kill us even if we're invuln)
  7617. if ( pAttacker && pAttacker->IsSolidFlagSet( FSOLID_TRIGGER ) )
  7618. {
  7619. CTriggerHurt *pTrigger = dynamic_cast<CTriggerHurt *>( pAttacker );
  7620. if ( pTrigger )
  7621. {
  7622. bAllowDamage = true;
  7623. info.SetDamageCustom( TF_DMG_CUSTOM_TRIGGER_HURT );
  7624. }
  7625. }
  7626. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_TELEFRAG )
  7627. {
  7628. bAllowDamage = true;
  7629. }
  7630. if ( !TFGameRules()->ApplyOnDamageModifyRules( info, this, bAllowDamage ) )
  7631. {
  7632. return 0;
  7633. }
  7634. // If player has Reflect Powerup, reflect damage to attacker.
  7635. // We do this here, after damage modify rules to ensure distance falloff calculations have already been made before we pass that damage back to the attacker
  7636. if ( pTFAttacker && m_Shared.GetCarryingRuneType() == RUNE_REFLECT && pTFAttacker != this && !pTFAttacker->m_Shared.IsInvulnerable() && pTFAttacker->IsAlive() )
  7637. {
  7638. CTakeDamageInfo dmg = info;
  7639. CTFProjectile_SentryRocket *sentryRocket = dynamic_cast<CTFProjectile_SentryRocket *>( info.GetInflictor() );
  7640. if ( gpGlobals->curtime > m_flNextReflectZap ) // don't spam the effect for fast weapons like flamethrower and minigun
  7641. {
  7642. m_flNextReflectZap = gpGlobals->curtime + 0.5f;
  7643. CPVSFilter filter( WorldSpaceCenter() );
  7644. Vector vEnd = pTFAttacker->WorldSpaceCenter();
  7645. Vector vStart = WorldSpaceCenter();
  7646. if ( bIsObject || sentryRocket )
  7647. {
  7648. CBaseEntity *pInflictor = info.GetInflictor();
  7649. vEnd = pInflictor->WorldSpaceCenter();
  7650. }
  7651. else
  7652. {
  7653. // Push the attacker away from the Reflect powerup holder
  7654. Vector toPlayer = vEnd - vStart;
  7655. toPlayer.z = 0.0f;
  7656. toPlayer.NormalizeInPlace();
  7657. toPlayer.z = 1.0f;
  7658. float flDamage = dmg.GetDamage();
  7659. if ( dmg.GetDamageCustom() != TF_DMG_CUSTOM_BURNING )
  7660. {
  7661. float flPushForce = RemapValClamped( flDamage, 0.1f, 150.f, 300.f, 500.f ); // Scale the push force according to damage
  7662. Vector vPush = flPushForce * toPlayer;
  7663. pTFAttacker->ApplyAbsVelocityImpulse( vPush );
  7664. }
  7665. // Play a sound and reduce the volume if damage is low
  7666. CSoundParameters params;
  7667. if ( CBaseEntity::GetParametersForSound( "Powerup.Reflect.Reflect", params, NULL ) )
  7668. {
  7669. CPASAttenuationFilter soundFilter( pTFAttacker->GetAbsOrigin(), params.soundlevel );
  7670. EmitSound_t ep( params );
  7671. if ( flDamage < 10.f )
  7672. {
  7673. ep.m_flVolume *= 0.75f;
  7674. }
  7675. pTFAttacker->EmitSound( soundFilter, entindex(), ep );
  7676. pTFAttacker->PainSound( dmg );
  7677. }
  7678. }
  7679. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  7680. TE_TFParticleEffectComplex( filter, 0.f, "dxhr_arm_muzzleflash", vStart, QAngle( 0.f, 0.f, 0.f ), NULL, &controlPoint, pTFAttacker, PATTACH_CUSTOMORIGIN );
  7681. }
  7682. dmg.SetDamageCustom( TF_DMG_CUSTOM_RUNE_REFLECT );
  7683. dmg.SetDamageType( DMG_SHOCK );
  7684. dmg.SetAttacker( this );
  7685. if ( bIsObject )
  7686. {
  7687. CBaseEntity *pInflictor = info.GetInflictor();
  7688. dmg.SetDamage( info.GetDamage() );
  7689. pInflictor->TakeDamage( dmg );
  7690. }
  7691. // Sentry rockets are not included in bIsobject so we deal with them separately
  7692. else
  7693. {
  7694. if ( sentryRocket )
  7695. {
  7696. dmg.SetDamage( info.GetDamage() );
  7697. info.GetInflictor()->GetOwnerEntity()->TakeDamage( dmg );
  7698. }
  7699. else
  7700. {
  7701. // Take damage unless you have Resist or Vampire (they are immune to reflect damage)
  7702. if ( pTFAttacker->m_Shared.GetCarryingRuneType() != RUNE_RESIST && pTFAttacker->m_Shared.GetCarryingRuneType() != RUNE_VAMPIRE )
  7703. {
  7704. dmg.SetDamage(info.GetDamage() * 0.8f);
  7705. pTFAttacker->TakeDamage( dmg );
  7706. }
  7707. }
  7708. }
  7709. }
  7710. //Don't take damage while I'm phasing.
  7711. if ( ( m_Shared.InCond( TF_COND_PHASE ) || m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) ) && bAllowDamage == false )
  7712. {
  7713. SpeakConceptIfAllowed( MP_CONCEPT_DODGE_SHOT );
  7714. if ( pAttacker && pAttacker->IsPlayer() )
  7715. {
  7716. CEffectData data;
  7717. data.m_nHitBox = GetParticleSystemIndex( "miss_text" );
  7718. data.m_vOrigin = WorldSpaceCenter() + Vector(0,0,32);
  7719. data.m_vAngles = vec3_angle;
  7720. data.m_nEntIndex = 0;
  7721. CSingleUserRecipientFilter filter( (CBasePlayer*)pAttacker );
  7722. te->DispatchEffect( filter, 0.0, data.m_vOrigin, "ParticleEffect", data );
  7723. }
  7724. Vector vecDir = vec3_origin;
  7725. if ( info.GetInflictor() )
  7726. {
  7727. vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0.0f, 0.0f, 10.0f ) - WorldSpaceCenter();
  7728. VectorNormalize( vecDir );
  7729. }
  7730. ApplyPushFromDamage( info, vecDir );
  7731. if ( m_Shared.InCond( TF_COND_PHASE ) )
  7732. {
  7733. m_Shared.m_ConditionData[ TF_COND_PHASE ].m_nPreventedDamageFromCondition += info.GetDamage();
  7734. m_Shared.m_iPhaseDamage += info.GetDamage();
  7735. }
  7736. bTookDamage = false;
  7737. }
  7738. else
  7739. {
  7740. bool bFatal = ( m_iHealth - info.GetDamage() ) <= 0;
  7741. bool bTrackEvent = pTFAttacker && pTFAttacker != this && !pTFAttacker->IsBot() && !IsBot();
  7742. if ( bTrackEvent )
  7743. {
  7744. float flHealthRemoved = bFatal ? m_iHealth : info.GetDamage();
  7745. if ( info.GetDamageBonus() && info.GetDamageBonusProvider() )
  7746. {
  7747. // Don't deal with raw damage numbers, only health removed.
  7748. // Example based on a crit rocket to a player with 120 hp:
  7749. // Actual damage is 120, but potential damage is 300, where
  7750. // 100 is the base, and 200 is the bonus. Apply this ratio
  7751. // to actual (so, attacker did 40, and provider added 80).
  7752. float flBonusMult = info.GetDamage() / abs( info.GetDamageBonus() - info.GetDamage() );
  7753. float flBonus = flHealthRemoved - ( flHealthRemoved / flBonusMult );
  7754. m_AchievementData.AddDamageEventToHistory( info.GetDamageBonusProvider(), flBonus );
  7755. flHealthRemoved -= flBonus;
  7756. }
  7757. m_AchievementData.AddDamageEventToHistory( pAttacker, flHealthRemoved );
  7758. }
  7759. // This should kill us
  7760. if ( bFatal )
  7761. {
  7762. // Damage could have been modified since we started
  7763. // Try to prevent death with buddha one more time
  7764. if ( bBuddha )
  7765. {
  7766. m_iHealth = 1;
  7767. return 0;
  7768. }
  7769. // Check to see if we have the cheat death attribute that makes
  7770. // us teleport to base rather than die
  7771. float flCheatDeathChance = 0.f;
  7772. CALL_ATTRIB_HOOK_FLOAT( flCheatDeathChance, teleport_instead_of_die );
  7773. if( RandomFloat() < flCheatDeathChance )
  7774. {
  7775. // Send back to base
  7776. ForceRespawn();
  7777. m_iHealth = 1;
  7778. return 0;
  7779. }
  7780. // Avoid one death
  7781. if ( m_Shared.InCond( TF_COND_PREVENT_DEATH ) )
  7782. {
  7783. m_Shared.RemoveCond( TF_COND_PREVENT_DEATH );
  7784. m_iHealth = 1;
  7785. return 0;
  7786. }
  7787. // Powerup-sourced reflected damage should not kill player
  7788. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_RUNE_REFLECT )
  7789. {
  7790. m_iHealth = 1;
  7791. return 0;
  7792. }
  7793. }
  7794. // NOTE: Deliberately skip base player OnTakeDamage, because we don't want all the stuff it does re: suit voice
  7795. bTookDamage = CBaseCombatCharacter::OnTakeDamage( info );
  7796. // Early out if the base class took no damage
  7797. if ( !bTookDamage )
  7798. {
  7799. if ( bDebug )
  7800. {
  7801. Warning( " ABORTED: Player failed to take the damage.\n" );
  7802. }
  7803. return 0;
  7804. }
  7805. // Check to see if we need to pass along the damage to other players
  7806. if ( pWeapon && ( gs_pRecursivePlayerCheck == NULL ) )
  7807. {
  7808. int iDamageAllConnected = 0;
  7809. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iDamageAllConnected, damage_all_connected );
  7810. if ( iDamageAllConnected > 0 )
  7811. {
  7812. // Am I healing someone or being healed?
  7813. CUtlVector<CTFPlayer*> pTempPlayerQueue;
  7814. AddConnectedPlayers( pTempPlayerQueue, this );
  7815. gs_pRecursivePlayerCheck = this;
  7816. for ( int iCount = 0 ; iCount < pTempPlayerQueue.Count() ; iCount++ )
  7817. {
  7818. CTFPlayer *pTFPlayer = pTempPlayerQueue[iCount];
  7819. if ( pTFPlayer && ( pTFPlayer != this ) )
  7820. {
  7821. pTFPlayer->TakeDamage( inputInfo );
  7822. }
  7823. }
  7824. gs_pRecursivePlayerCheck = NULL;
  7825. }
  7826. }
  7827. }
  7828. if ( bTookDamage == false )
  7829. return 0;
  7830. if ( bDebug )
  7831. {
  7832. Warning( " DEALT: Player took %.2f damage.\n", info.GetDamage() );
  7833. Warning( " HEALTH LEFT: %d\n", GetHealth() );
  7834. }
  7835. // Some weapons have the ability to impart extra moment just because they feel like it. Let their attributes
  7836. // do so if they're in the mood.
  7837. if ( pWeapon != NULL )
  7838. {
  7839. float flZScale = 0.0f;
  7840. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flZScale, apply_z_velocity_on_damage );
  7841. if ( flZScale != 0.0f )
  7842. {
  7843. ApplyAbsVelocityImpulse( Vector( 0.0f, 0.0f, flZScale ) );
  7844. }
  7845. float flDirScale = 0.0f;
  7846. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flDirScale, apply_look_velocity_on_damage );
  7847. if ( flDirScale != 0.0f && pAttacker != NULL )
  7848. {
  7849. Vector vecForward;
  7850. AngleVectors( pAttacker->EyeAngles(), &vecForward );
  7851. Vector vecForwardNoDownward = Vector( vecForward.x, vecForward.y, MIN( 0.0f, vecForward.z ) ).Normalized();
  7852. ApplyAbsVelocityImpulse( vecForwardNoDownward * flDirScale );
  7853. }
  7854. }
  7855. // let weapons react to their owner being injured
  7856. CTFWeaponBase *pMyWeapon = GetActiveTFWeapon();
  7857. if ( pMyWeapon )
  7858. {
  7859. pMyWeapon->ApplyOnInjuredAttributes( this, pTFAttacker, info );
  7860. }
  7861. // Send the damage message to the client for the hud damage indicator
  7862. // Try and figure out where the damage is coming from
  7863. Vector vecDamageOrigin = info.GetReportedPosition();
  7864. // If we didn't get an origin to use, try using the attacker's origin
  7865. if ( vecDamageOrigin == vec3_origin && info.GetInflictor() )
  7866. {
  7867. vecDamageOrigin = info.GetInflictor()->GetAbsOrigin();
  7868. }
  7869. // Tell the player's client that he's been hurt.
  7870. if ( m_iHealthBefore != GetHealth() )
  7871. {
  7872. CSingleUserRecipientFilter user( this );
  7873. UserMessageBegin( user, "Damage" );
  7874. WRITE_SHORT( clamp( (int)info.GetDamage(), 0, 32000 ) );
  7875. WRITE_LONG( info.GetDamageType() );
  7876. // Tell the client whether they should show it in the indicator
  7877. if ( bitsDamage != DMG_GENERIC && !(bitsDamage & (DMG_DROWN | DMG_FALL | DMG_BURN) ) )
  7878. {
  7879. WRITE_BOOL( true );
  7880. WRITE_VEC3COORD( vecDamageOrigin );
  7881. }
  7882. else
  7883. {
  7884. WRITE_BOOL( false );
  7885. }
  7886. MessageEnd();
  7887. }
  7888. // add to the damage total for clients, which will be sent as a single
  7889. // message at the end of the frame
  7890. // todo: remove after combining shotgun blasts?
  7891. if ( info.GetInflictor() && info.GetInflictor()->edict() )
  7892. {
  7893. m_DmgOrigin = info.GetInflictor()->GetAbsOrigin();
  7894. }
  7895. m_DmgTake += (int)info.GetDamage();
  7896. // Reset damage time countdown for each type of time based damage player just sustained
  7897. for (int i = 0; i < CDMG_TIMEBASED; i++)
  7898. {
  7899. // Make sure the damage type is really time-based.
  7900. // This is kind of hacky but necessary until we setup DamageType as an enum.
  7901. int iDamage = ( DMG_PARALYZE << i );
  7902. if ( ( info.GetDamageType() & iDamage ) && g_pGameRules->Damage_IsTimeBased( iDamage ) )
  7903. {
  7904. m_rgbTimeBasedDamage[i] = 0;
  7905. }
  7906. }
  7907. const char* pzsMedigunResistEffect = NULL;
  7908. const char* pzsTeam = GetTeamNumber() == TF_TEAM_RED ? "red" : "blue";
  7909. // If we have one of the medigun resist buffs and get hit with the matching damage type then
  7910. // spawn a particle above our head to let enemies know their damage is being resisted, and tell
  7911. // the medic he's doing the right thing.
  7912. bool bMedicBulletResist = m_Shared.InCond( TF_COND_MEDIGUN_UBER_BULLET_RESIST ) || m_Shared.InCond( TF_COND_MEDIGUN_SMALL_BULLET_RESIST );
  7913. bool bMedicExplosiveResist = m_Shared.InCond( TF_COND_MEDIGUN_UBER_BLAST_RESIST ) || m_Shared.InCond( TF_COND_MEDIGUN_SMALL_BLAST_RESIST );
  7914. bool bMedicFireResist = m_Shared.InCond( TF_COND_MEDIGUN_UBER_FIRE_RESIST ) || m_Shared.InCond( TF_COND_MEDIGUN_SMALL_FIRE_RESIST );
  7915. if( ( bMedicBulletResist && ( bitsDamage & DMG_BULLET ) ) )
  7916. {
  7917. pzsMedigunResistEffect = CFmtStr( "vaccinator_%s_buff1_burst", pzsTeam );
  7918. }
  7919. else if( bMedicExplosiveResist && ( bitsDamage & DMG_BLAST ) )
  7920. {
  7921. pzsMedigunResistEffect = CFmtStr( "vaccinator_%s_buff2_burst", pzsTeam );
  7922. }
  7923. else if( bMedicFireResist && ( bitsDamage & DMG_BURN ) )
  7924. {
  7925. pzsMedigunResistEffect = CFmtStr( "vaccinator_%s_buff3_burst", pzsTeam );
  7926. }
  7927. if( pzsMedigunResistEffect != NULL )
  7928. {
  7929. const Vector& vecOrigin = GetAbsOrigin();
  7930. CPVSFilter filter( vecOrigin );
  7931. TE_TFParticleEffect( filter, 0, pzsMedigunResistEffect, vecOrigin, vec3_angle );
  7932. }
  7933. // Display any effect associate with this damage type
  7934. DamageEffect( info.GetDamage(),bitsDamage );
  7935. m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client
  7936. m_bitsHUDDamage = -1; // make sure the damage bits get reset
  7937. // Flinch
  7938. bool bFlinch = true;
  7939. if ( bitsDamage != DMG_GENERIC )
  7940. {
  7941. if ( IsPlayerClass( TF_CLASS_SNIPER ) && m_Shared.InCond( TF_COND_AIMING ) )
  7942. {
  7943. if ( pTFAttacker && pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_MINIGUN )
  7944. {
  7945. float flDistSqr = ( pTFAttacker->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  7946. if ( flDistSqr > 750 * 750 )
  7947. {
  7948. bFlinch = false;
  7949. }
  7950. }
  7951. }
  7952. if ( bFlinch )
  7953. {
  7954. if ( ApplyPunchImpulseX( -2 ) )
  7955. {
  7956. PlayFlinch( info );
  7957. }
  7958. }
  7959. // PASSTIME intense flinch to make it hard to throw straight while taking damage
  7960. extern ConVar tf_passtime_flinch_boost;
  7961. if( TFGameRules() && TFGameRules()->IsPasstimeMode() && (tf_passtime_flinch_boost.GetInt() > 0) )
  7962. {
  7963. int iFlinch = tf_passtime_flinch_boost.GetInt();
  7964. CTFWeaponBase *pMyWeapon = GetActiveTFWeapon();
  7965. if( pMyWeapon && pMyWeapon->GetWeaponID() == TF_WEAPON_PASSTIME_GUN )
  7966. {
  7967. QAngle punch;
  7968. punch.Random( -iFlinch, iFlinch );
  7969. SetPunchAngle( punch );
  7970. }
  7971. }
  7972. }
  7973. // Do special explosion damage effect
  7974. if ( bitsDamage & DMG_BLAST )
  7975. {
  7976. OnDamagedByExplosion( info );
  7977. }
  7978. if ( m_iHealthBefore != GetHealth() )
  7979. {
  7980. PainSound( info );
  7981. }
  7982. // Detect drops below 25% health and restart expression, so that characters look worried.
  7983. int iHealthBoundary = (GetMaxHealth() * 0.25);
  7984. if ( GetHealth() <= iHealthBoundary && m_iHealthBefore > iHealthBoundary )
  7985. {
  7986. ClearExpression();
  7987. }
  7988. #ifdef _DEBUG
  7989. // Report damage from the info in debug so damage against targetdummies goes
  7990. // through the system, as m_iHealthBefore - GetHealth() will always be 0.
  7991. CTF_GameStats.Event_PlayerDamage( this, info, info.GetDamage() );
  7992. #else
  7993. CTF_GameStats.Event_PlayerDamage( this, info, m_iHealthBefore - GetHealth() );
  7994. #endif // _DEBUG
  7995. // if we take damage after we leave the ground, update the health if its less
  7996. if ( bTookDamage && m_iLeftGroundHealth > 0 )
  7997. {
  7998. if ( GetHealth() < m_iLeftGroundHealth )
  7999. {
  8000. m_iLeftGroundHealth = GetHealth();
  8001. }
  8002. }
  8003. if ( IsPlayerClass( TF_CLASS_SPY ) && ( inputInfo.GetDamageCustom() != TF_DMG_CUSTOM_TELEFRAG ) )
  8004. {
  8005. // Trigger feign death if the player has it prepped...
  8006. if ( m_Shared.IsFeignDeathReady() )
  8007. {
  8008. m_Shared.SetFeignDeathReady( false );
  8009. if ( !m_Shared.InCond( TF_COND_TAUNTING ) )
  8010. {
  8011. SpyDeadRingerDeath( info );
  8012. pTFAttacker->IncrementKillCountSinceLastDeploy( info );
  8013. }
  8014. }
  8015. else if ( !( info.GetDamageType() & DMG_FALL ) )
  8016. {
  8017. m_Shared.NoteLastDamageTime( m_lastDamageAmount );
  8018. }
  8019. }
  8020. if ( pWeapon )
  8021. {
  8022. pWeapon->ApplyPostHitEffects( inputInfo, this );
  8023. }
  8024. if ( IsPlayerClass( TF_CLASS_DEMOMAN ) )
  8025. {
  8026. // Reduce charge if damage is taken
  8027. int iDemoChargeDamagePenalty = 0;
  8028. CALL_ATTRIB_HOOK_INT( iDemoChargeDamagePenalty, lose_demo_charge_on_damage_when_charging );
  8029. // Does not apply to self or fall damage
  8030. if ( iDemoChargeDamagePenalty && m_Shared.InCond( TF_COND_SHIELD_CHARGE ) && !( info.GetDamageType() & DMG_FALL ) && (pAttacker != this) )
  8031. {
  8032. iDemoChargeDamagePenalty *= info.GetDamage();
  8033. m_Shared.SetDemomanChargeMeter( Max( m_Shared.GetDemomanChargeMeter() - (float)iDemoChargeDamagePenalty, 0.0f ) );
  8034. }
  8035. }
  8036. #ifdef STAGING_ONLY
  8037. // Remove Cond if hit
  8038. if ( m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  8039. {
  8040. m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
  8041. }
  8042. #endif
  8043. float flRageScale = 1.0f;
  8044. CALL_ATTRIB_HOOK_FLOAT( flRageScale, rage_giving_scale );
  8045. // Give the soldier/pyro some rage points for dealing/taking damage.
  8046. if ( bTookDamage && pTFAttacker != this )
  8047. {
  8048. // Buff flag 1: we get rage when we deal damage. Here, that means the soldier that attacked
  8049. // gets rage when we take damage.
  8050. HandleRageGain( pTFAttacker, kRageBuffFlag_OnDamageDealt, info.GetDamage() * flRageScale, 6.0f );
  8051. // Buff flag 2: we get rage when we take damage.
  8052. if ( !( info.GetDamageType() & DMG_FALL ) )
  8053. {
  8054. HandleRageGain( this, kRageBuffFlag_OnDamageReceived, info.GetDamage() * flRageScale, 3.5f );
  8055. }
  8056. // Buff 5: our pyro attacker get rage when we're damaged by fire
  8057. if ( ( info.GetDamageType() & DMG_BURN ) != 0 || ( info.GetDamageType() & DMG_PLASMA ) != 0 )
  8058. {
  8059. float flInverseRageGainScale = TFGameRules()->IsMannVsMachineMode() ? 12.f : 3.f;
  8060. HandleRageGain( pTFAttacker, kRageBuffFlag_OnBurnDamageDealt, info.GetDamage() * flRageScale, flInverseRageGainScale );
  8061. }
  8062. }
  8063. if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_BAT_FISH )
  8064. {
  8065. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED ) && (m_Shared.GetDisguiseTeam() == pTFAttacker->GetTeamNumber());
  8066. if ( m_iHealth <= 0 )
  8067. {
  8068. info.SetDamageCustom( TF_DMG_CUSTOM_FISH_KILL );
  8069. }
  8070. if ( m_iHealth <= 0 || !bDisguised )
  8071. {
  8072. // Do you ever find yourself typing "fish damage override" into a million-lines-of-code project and
  8073. // wondering about the world? Because I do.
  8074. int iFishDamageOverride = 0;
  8075. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iFishDamageOverride, fish_damage_override );
  8076. TFGameRules()->DeathNotice( this, info, iFishDamageOverride ? "fish_notice__arm" : "fish_notice" );
  8077. }
  8078. }
  8079. if ( IsPlayerClass( TF_CLASS_SCOUT) )
  8080. {
  8081. // Lose hype on take damage
  8082. int iHypeResetsOnTakeDamage = 0;
  8083. CALL_ATTRIB_HOOK_INT( iHypeResetsOnTakeDamage, lose_hype_on_take_damage );
  8084. if ( iHypeResetsOnTakeDamage != 0 )
  8085. {
  8086. // Loose x hype on jump
  8087. float flHype = m_Shared.GetScoutHypeMeter();
  8088. m_Shared.SetScoutHypeMeter( flHype - iHypeResetsOnTakeDamage * info.GetDamage() );
  8089. TeamFortress_SetSpeed();
  8090. }
  8091. }
  8092. // Add humilation Obituary here for throwable hits
  8093. //if ( info.GetDamageCustom() == TF_DMG_CUSTOM_THROWABLE )
  8094. //{
  8095. // bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED ) && (m_Shared.GetDisguiseTeam() == pTFAttacker->GetTeamNumber());
  8096. // if( m_iHealth <= 0 )
  8097. // {
  8098. // info.SetDamageCustom( TF_DMG_CUSTOM_THROWABLE_KILL );
  8099. // }
  8100. // if ( m_iHealth <= 0 || !bDisguised )
  8101. // {
  8102. // TFGameRules()->DeathNotice( this, info, "throwable_hit" );
  8103. // }
  8104. //}
  8105. // Let attacker react to the damage they dealt
  8106. if ( pTFAttacker )
  8107. {
  8108. pTFAttacker->OnDealtDamage( this, info );
  8109. }
  8110. bool bIsPyroDetonateJumping = ( IsPlayerClass( TF_CLASS_PYRO ) && pAttacker == this && !(GetFlags() & FL_ONGROUND) && !(GetFlags() & FL_INWATER));
  8111. if ( bIsDemomanPipeJumping || bIsSoldierRocketJumping || bIsPyroDetonateJumping )
  8112. {
  8113. // Are we being healed by any QuickFix medics?
  8114. for ( int i = 0; i < m_Shared.m_nNumHealers; i++ )
  8115. {
  8116. CTFPlayer *pMedic = ToTFPlayer( m_Shared.m_aHealers[i].pHealer );
  8117. if ( !pMedic )
  8118. continue;
  8119. // Share blast jump with them
  8120. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pMedic->GetActiveTFWeapon() );
  8121. if ( pMedigun && pMedigun->GetMedigunType() == MEDIGUN_QUICKFIX )
  8122. {
  8123. // Vector vecDir = vec3_origin;
  8124. // if ( info.GetInflictor() )
  8125. // {
  8126. // vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0.0f, 0.0f, 10.0f ) - WorldSpaceCenter();
  8127. // info.GetInflictor()->AdjustDamageDirection( info, vecDir, this );
  8128. // VectorNormalize( vecDir );
  8129. // }
  8130. // pMedic->RemoveFlag( FL_ONGROUND );
  8131. // pMedic->ApplyPushFromDamage( info, vecDir );
  8132. float flForce = GetAbsVelocity().Length();
  8133. flForce = MIN( flForce, 900.f );
  8134. Vector vecNewVelocity = GetAbsVelocity();
  8135. VectorNormalize( vecNewVelocity );
  8136. pMedic->RemoveFlag( FL_ONGROUND );
  8137. pMedic->ApplyAbsVelocityImpulse( vecNewVelocity * flForce );
  8138. }
  8139. }
  8140. }
  8141. if ( pTFAttacker && pTFAttacker->IsPlayerClass( TF_CLASS_SOLDIER ) )
  8142. {
  8143. if ( info.GetDamageType() & DMG_BLAST )
  8144. {
  8145. // Send an event whenever a soldier hits another player directly with a stun rocket
  8146. CTFBaseRocket *pRocket = dynamic_cast< CTFBaseRocket* >( info.GetInflictor() );
  8147. if ( pRocket && pRocket->GetStunLevel() && pRocket->GetEnemy() && pRocket->GetEnemy() == this )
  8148. {
  8149. IGameEvent *event = gameeventmanager->CreateEvent( "player_directhit_stun" );
  8150. if ( event )
  8151. {
  8152. event->SetInt( "attacker", pTFAttacker->entindex() );
  8153. event->SetInt( "victim", entindex() );
  8154. gameeventmanager->FireEvent( event );
  8155. }
  8156. }
  8157. }
  8158. }
  8159. CTFWeaponBase *pTFWeapon = GetKilleaterWeaponFromDamageInfo( &info );
  8160. if ( !pTFWeapon )
  8161. {
  8162. // Check Wearable instead like demoshields or manntreads
  8163. CTFWearable *pWearable = dynamic_cast< CTFWearable* >( info.GetWeapon() );
  8164. if ( pWearable )
  8165. {
  8166. EconEntity_OnOwnerKillEaterEvent_Batched( pWearable, pTFAttacker, this, kKillEaterEvent_DamageDealt, info.GetDamage() );
  8167. EconEntity_OnOwnerKillEaterEvent_Batched( pWearable, pTFAttacker, this, kKillEaterEvent_PlayersHit, 1 );
  8168. }
  8169. }
  8170. else
  8171. {
  8172. EconEntity_OnOwnerKillEaterEvent_Batched( pTFWeapon, pTFAttacker, this, kKillEaterEvent_DamageDealt, info.GetDamage() );
  8173. EconEntity_OnOwnerKillEaterEvent_Batched( pTFWeapon, pTFAttacker, this, kKillEaterEvent_PlayersHit, 1 );
  8174. }
  8175. // bHadBallBeforeDamage will always be false in non-passtime modes
  8176. if ( bTookDamage && bHadBallBeforeDamage )
  8177. {
  8178. g_pPasstimeLogic->OnBallCarrierDamaged( this, pTFAttacker, info );
  8179. }
  8180. return info.GetDamage();
  8181. }
  8182. //-----------------------------------------------------------------------------
  8183. // Purpose: Invoked when we deal damage to another victim
  8184. //-----------------------------------------------------------------------------
  8185. void CTFPlayer::OnDealtDamage( CBaseCombatCharacter *pVictim, const CTakeDamageInfo &info )
  8186. {
  8187. if ( pVictim )
  8188. {
  8189. // which second of the window are we in
  8190. int i = (int)gpGlobals->curtime;
  8191. i %= DPS_Period;
  8192. if ( i != m_lastDamageRateIndex )
  8193. {
  8194. // a second has ticked over, start a new accumulation
  8195. m_damageRateArray[ i ] = info.GetDamage();
  8196. m_lastDamageRateIndex = i;
  8197. // track peak DPS for this player
  8198. m_peakDamagePerSecond = 0;
  8199. for( i=0; i<DPS_Period; ++i )
  8200. {
  8201. if ( m_damageRateArray[i] > m_peakDamagePerSecond )
  8202. {
  8203. m_peakDamagePerSecond = m_damageRateArray[i];
  8204. }
  8205. }
  8206. }
  8207. else
  8208. {
  8209. m_damageRateArray[ i ] += info.GetDamage();
  8210. }
  8211. }
  8212. }
  8213. //-----------------------------------------------------------------------------
  8214. // Purpose:
  8215. //-----------------------------------------------------------------------------
  8216. void CTFPlayer::AddConnectedPlayers( CUtlVector<CTFPlayer*> &vecPlayers, CTFPlayer *pPlayerToConsider )
  8217. {
  8218. if ( !pPlayerToConsider )
  8219. return;
  8220. if ( vecPlayers.Find( pPlayerToConsider ) != vecPlayers.InvalidIndex() )
  8221. return; // already in the list
  8222. vecPlayers.AddToTail( pPlayerToConsider );
  8223. if ( pPlayerToConsider->MedicGetHealTarget() )
  8224. {
  8225. AddConnectedPlayers( vecPlayers, ToTFPlayer( pPlayerToConsider->MedicGetHealTarget() ) );
  8226. }
  8227. for ( int i = 0 ; i < pPlayerToConsider->m_Shared.GetNumHealers() ; i++ )
  8228. {
  8229. CTFPlayer *pMedic = ToTFPlayer( pPlayerToConsider->m_Shared.GetHealerByIndex( i ) );
  8230. if ( pMedic )
  8231. {
  8232. AddConnectedPlayers( vecPlayers, pMedic );
  8233. }
  8234. }
  8235. }
  8236. //-----------------------------------------------------------------------------
  8237. // Purpose: Reduces backstab damage if we have a back shield.
  8238. //-----------------------------------------------------------------------------
  8239. bool CTFPlayer::CheckBlockBackstab( CTFPlayer *pTFAttacker )
  8240. {
  8241. // Check all items for the attribute that blocks a backstab.
  8242. // Destroy the first item that intercepts the backstab.
  8243. CUtlVector<CBaseEntity*> itemList;
  8244. int iBackStabShield = 0;
  8245. CALL_ATTRIB_HOOK( int, iBackStabShield, set_blockbackstab_once, this, &itemList );
  8246. if ( iBackStabShield )
  8247. {
  8248. Assert( itemList.Count() != 0 );
  8249. CBaseEntity *pEntity = itemList.Element( 0 );
  8250. if ( pEntity )
  8251. {
  8252. if ( pEntity->IsBaseCombatWeapon() )
  8253. {
  8254. // Remove.
  8255. }
  8256. if ( pEntity->IsWearable() )
  8257. {
  8258. // Yay stats.
  8259. EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pEntity ), this, pTFAttacker, kKillEaterEvent_BackstabAbsorbed );
  8260. // Unequip.
  8261. CTFWearable *pItem = dynamic_cast<CTFWearable *>( pEntity );
  8262. pItem->Break();
  8263. pItem->RemoveFrom( this );
  8264. }
  8265. UTIL_Remove( pEntity );
  8266. // tell the bot his Razorback just got broken
  8267. CTFBot *me = ToTFBot( this );
  8268. if ( me )
  8269. {
  8270. me->DelayedThreatNotice( pTFAttacker, 0.5f );
  8271. }
  8272. }
  8273. return true;
  8274. }
  8275. return false;
  8276. }
  8277. //-----------------------------------------------------------------------------
  8278. // Purpose:
  8279. //-----------------------------------------------------------------------------
  8280. void CTFPlayer::DamageEffect(float flDamage, int fDamageType)
  8281. {
  8282. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED );
  8283. if (fDamageType & DMG_CRUSH)
  8284. {
  8285. //Red damage indicator
  8286. color32 red = {128,0,0,128};
  8287. UTIL_ScreenFade( this, red, 1.0f, 0.1f, FFADE_IN );
  8288. }
  8289. else if (fDamageType & DMG_DROWN)
  8290. {
  8291. //Red damage indicator
  8292. color32 blue = {0,0,128,128};
  8293. UTIL_ScreenFade( this, blue, 1.0f, 0.1f, FFADE_IN );
  8294. }
  8295. else if (fDamageType & DMG_SLASH)
  8296. {
  8297. if ( !bDisguised )
  8298. {
  8299. // If slash damage shoot some blood
  8300. SpawnBlood(EyePosition(), g_vecAttackDir, BloodColor(), flDamage);
  8301. }
  8302. }
  8303. else if ( fDamageType & DMG_BULLET )
  8304. {
  8305. if ( !bDisguised )
  8306. {
  8307. EmitSound( "Flesh.BulletImpact" );
  8308. }
  8309. }
  8310. }
  8311. //-----------------------------------------------------------------------------
  8312. // Purpose:
  8313. // Input : collisionGroup -
  8314. // Output : Returns true on success, false on failure.
  8315. //-----------------------------------------------------------------------------
  8316. bool CTFPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const
  8317. {
  8318. if ( ( ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) && tf_avoidteammates.GetBool() ) ||
  8319. collisionGroup == TFCOLLISION_GROUP_ROCKETS || collisionGroup == TFCOLLISION_GROUP_ROCKET_BUT_NOT_WITH_OTHER_ROCKETS )
  8320. {
  8321. switch( GetTeamNumber() )
  8322. {
  8323. case TF_TEAM_RED:
  8324. if ( !( contentsMask & CONTENTS_REDTEAM ) )
  8325. return false;
  8326. break;
  8327. case TF_TEAM_BLUE:
  8328. if ( !( contentsMask & CONTENTS_BLUETEAM ) )
  8329. return false;
  8330. break;
  8331. }
  8332. }
  8333. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  8334. }
  8335. //---------------------------------------
  8336. // Is the player the passed player class?
  8337. //---------------------------------------
  8338. bool CTFPlayer::IsPlayerClass( int iClass ) const
  8339. {
  8340. const CTFPlayerClass *pClass = &m_PlayerClass;
  8341. if ( !pClass )
  8342. return false;
  8343. return ( pClass->IsClass( iClass ) );
  8344. }
  8345. //-----------------------------------------------------------------------------
  8346. // Purpose:
  8347. //-----------------------------------------------------------------------------
  8348. void CTFPlayer::CommitSuicide( bool bExplode /* = false */, bool bForce /*= false*/ )
  8349. {
  8350. // Don't suicide if we haven't picked a class for the first time, or we're not in active state
  8351. if ( IsPlayerClass( TF_CLASS_UNDEFINED ) || !m_Shared.InState( TF_STATE_ACTIVE ) )
  8352. return;
  8353. // Don't suicide during the "bonus time" if we're not on the winning team
  8354. if ( !bForce && TFGameRules()->State_Get() == GR_STATE_TEAM_WIN &&
  8355. GetTeamNumber() != TFGameRules()->GetWinningTeam() )
  8356. {
  8357. return;
  8358. }
  8359. if ( TFGameRules()->ShowMatchSummary() )
  8360. return;
  8361. // No suicide while a ghost!
  8362. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  8363. return;
  8364. // No suicide while a kart
  8365. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  8366. return;
  8367. m_bSuicideExplode = bExplode;
  8368. m_iSuicideCustomKillFlags = TF_DMG_CUSTOM_SUICIDE;
  8369. BaseClass::CommitSuicide( bExplode, bForce );
  8370. }
  8371. //-----------------------------------------------------------------------------
  8372. // Purpose:
  8373. // Input : &info -
  8374. // Output : int
  8375. //-----------------------------------------------------------------------------
  8376. ConVar tf_preround_push_from_damage_enable( "tf_preround_push_from_damage_enable", "0", FCVAR_NONE, "If enabled, this will allow players using certain type of damage to move during pre-round freeze time." );
  8377. void CTFPlayer::ApplyPushFromDamage( const CTakeDamageInfo &info, Vector vecDir )
  8378. {
  8379. // check if player can be moved
  8380. if ( !tf_preround_push_from_damage_enable.GetBool() && !CanPlayerMove() )
  8381. return;
  8382. if ( m_bIsTargetDummy )
  8383. return;
  8384. Vector vecForce;
  8385. vecForce.Init();
  8386. if ( info.GetAttacker() == this )
  8387. {
  8388. Vector vecSize = WorldAlignSize();
  8389. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  8390. if ( vecSize == hullSizeCrouch )
  8391. {
  8392. // Use the original hull for damage force calculation to ensure our RJ height doesn't change due to crouch hull increase
  8393. // ^^ Comment above is an ancient lie, Ducking actually increases blast force, this value increases it even more 82 standing, 62 ducking, 55 modified
  8394. vecSize.z = 55;
  8395. }
  8396. float flDamageForForce = info.GetDamageForForceCalc() ? info.GetDamageForForceCalc() : info.GetDamage();
  8397. float flSelfPushMult = 1.0;
  8398. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( info.GetWeapon(), flSelfPushMult, mult_dmgself_push_force );
  8399. if ( IsPlayerClass( TF_CLASS_SOLDIER ) )
  8400. {
  8401. // Rocket Jump
  8402. if ( (info.GetDamageType() & DMG_BLAST) )
  8403. {
  8404. if ( GetFlags() & FL_ONGROUND )
  8405. {
  8406. vecForce = vecDir * -DamageForce( vecSize, flDamageForForce, tf_damageforcescale_self_soldier_badrj.GetFloat() ) * flSelfPushMult;
  8407. }
  8408. else
  8409. {
  8410. vecForce = vecDir * -DamageForce( vecSize, flDamageForForce, tf_damageforcescale_self_soldier_rj.GetFloat() ) * flSelfPushMult;
  8411. }
  8412. SetBlastJumpState( TF_PLAYER_ROCKET_JUMPED );
  8413. // Reset duck in air on self rocket impulse.
  8414. m_Shared.SetAirDucked( 0 );
  8415. }
  8416. else
  8417. {
  8418. // Self Damage no force
  8419. vecForce.Zero();
  8420. }
  8421. }
  8422. else
  8423. {
  8424. // Detonator blast jump modifier
  8425. if ( IsPlayerClass( TF_CLASS_PYRO ) && info.GetDamageCustom() == TF_DMG_CUSTOM_FLARE_EXPLOSION )
  8426. {
  8427. vecForce = vecDir * -DamageForce( vecSize, flDamageForForce, tf_damageforcescale_pyro_jump.GetFloat() ) * flSelfPushMult;
  8428. }
  8429. else
  8430. {
  8431. // Other Jumps (Stickies)
  8432. vecForce = vecDir * -DamageForce( vecSize, flDamageForForce, DAMAGE_FORCE_SCALE_SELF ) * flSelfPushMult;
  8433. }
  8434. // Reset duck in air on self grenade impulse.
  8435. m_Shared.SetAirDucked( 0 );
  8436. }
  8437. // Precision removes self damage so we don't want push force from damage
  8438. if ( m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  8439. {
  8440. vecForce.Zero();
  8441. }
  8442. }
  8443. else
  8444. {
  8445. // Don't let bot get pushed while they're in spawn area
  8446. if ( m_Shared.InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) || m_Shared.GetCarryingRuneType() == RUNE_KNOCKOUT )
  8447. {
  8448. return;
  8449. }
  8450. // Sentryguns push a lot harder
  8451. if ( (info.GetDamageType() & DMG_BULLET) && info.GetInflictor() && info.GetInflictor()->IsBaseObject() )
  8452. {
  8453. float flSentryPushMultiplier = 16.f;
  8454. CObjectSentrygun* pSentry = dynamic_cast<CObjectSentrygun*>( info.GetInflictor() );
  8455. if ( pSentry )
  8456. {
  8457. flSentryPushMultiplier = pSentry->GetPushMultiplier();
  8458. // Scale the force based on Distance, Wrangled Sentries should not push so hard at distance
  8459. // get the distance between sentry and victim and lower push force if outside of attack range (wrangled)
  8460. float flDistSqr = (pSentry->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
  8461. if ( flDistSqr > SENTRY_MAX_RANGE_SQRD )
  8462. {
  8463. flSentryPushMultiplier *= 0.5f;
  8464. }
  8465. }
  8466. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), flSentryPushMultiplier );
  8467. }
  8468. else
  8469. {
  8470. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>(info.GetWeapon());
  8471. if ( pWeapon && (pWeapon->GetWeaponID() == TF_WEAPON_COMPOUND_BOW) )
  8472. {
  8473. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), tf_damageforcescale_other.GetFloat() );
  8474. vecForce.z = 0;
  8475. }
  8476. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_PLASMA_CHARGED )
  8477. {
  8478. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), tf_damageforcescale_other.GetFloat() ) * 1.25f;
  8479. }
  8480. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_FLARE_PELLET)
  8481. {
  8482. float flTimeAlive = 0.0f;
  8483. CTFProjectile_Flare *pFlare = dynamic_cast< CTFProjectile_Flare* >( info.GetInflictor() );
  8484. if ( pFlare )
  8485. {
  8486. flTimeAlive = pFlare->GetTimeAlive();
  8487. }
  8488. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), TF_FLARE_PELLET_FORCE * RemapValClamped( flTimeAlive, 0.1f, 1.0f, 1.0f, TF_FLARE_PELLET_FORCE_DISTANCE_SCALE ) );
  8489. vecForce.z = ( ( GetPlayerClass()->GetClassIndex() == TF_CLASS_HEAVYWEAPONS ) ? ( TF_FLARE_PELLET_FORCE_UPWARD_HEAVY ) : ( TF_FLARE_PELLET_FORCE_UPWARD ) );
  8490. }
  8491. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_KART )
  8492. {
  8493. vecForce = info.GetDamageForce();
  8494. }
  8495. else
  8496. {
  8497. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), tf_damageforcescale_other.GetFloat() );
  8498. }
  8499. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  8500. {
  8501. // Heavies take less push from non sentryguns
  8502. vecForce *= 0.5;
  8503. }
  8504. CBaseEntity* pInflictor = info.GetInflictor();
  8505. if ( pInflictor && CanScatterGunKnockBack(pWeapon, info.GetDamage(), (WorldSpaceCenter() - pInflictor->WorldSpaceCenter()).LengthSqr() ) )
  8506. {
  8507. // Remove all Z force from these shots if they are close enough and doing enough damage
  8508. if ( vecForce.z < 0 )
  8509. {
  8510. vecForce.z = 0;
  8511. }
  8512. }
  8513. int iAirBlast = 0;
  8514. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iAirBlast, damage_causes_airblast );
  8515. if ( iAirBlast )
  8516. {
  8517. float force = -DamageForce( WorldAlignSize(), 100, 6 );
  8518. ApplyAirBlastImpulse( force * vecDir );
  8519. vecForce.Zero();
  8520. }
  8521. }
  8522. bool bBigKnockback = false;
  8523. CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() );
  8524. if ( pAttacker && pAttacker->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) && pAttacker->m_Shared.IsRageDraining() )
  8525. {
  8526. // Generic Rage attribute
  8527. int iRage = 0;
  8528. CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRage, generate_rage_on_dmg );
  8529. if ( iRage )
  8530. {
  8531. // In MvM, Heavies can purchase a knockback+stun effect
  8532. float flPushMultiplier = ( iRage + 1 ) * 24.f;
  8533. vecForce = vecDir * -DamageForce( WorldAlignSize(), info.GetDamage(), flPushMultiplier );
  8534. bBigKnockback = true;
  8535. // Track for achievements
  8536. m_AchievementData.AddPusherToHistory( pAttacker );
  8537. }
  8538. }
  8539. // Airblast effect for general attacks. Scaled by range.
  8540. float flImpactBlastForce = 1.f;
  8541. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), flImpactBlastForce, damage_blast_push );
  8542. if ( flImpactBlastForce != 1.f )
  8543. {
  8544. CBaseEntity *pInflictor = info.GetInflictor();
  8545. if ( pInflictor )
  8546. {
  8547. const float flMaxPushBackDistSqr = 700.f * 700.f;
  8548. float flDistSqr = ( WorldSpaceCenter() - pInflictor->WorldSpaceCenter() ).LengthSqr();
  8549. if ( flDistSqr <= flMaxPushBackDistSqr )
  8550. {
  8551. if ( vecForce.z < 0 )
  8552. {
  8553. vecForce.z = 0;
  8554. }
  8555. m_Shared.StunPlayer( 0.3f, 1.f, TF_STUN_MOVEMENT | TF_STUN_MOVEMENT_FORWARD_ONLY, pAttacker );
  8556. flImpactBlastForce = RemapValClamped( flDistSqr, 1000.f, flMaxPushBackDistSqr, flImpactBlastForce, ( flImpactBlastForce * 0.5f ) );
  8557. float flForce = -DamageForce( WorldAlignSize(), info.GetDamage() * 2, flImpactBlastForce );
  8558. ApplyAirBlastImpulse( flForce * vecDir );
  8559. }
  8560. }
  8561. }
  8562. if ( TFGameRules()->GameModeUsesUpgrades() )
  8563. {
  8564. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  8565. {
  8566. // invading bots can't be pushed by sentry guns
  8567. if ( info.GetInflictor() && info.GetInflictor()->IsBaseObject() )
  8568. {
  8569. return;
  8570. }
  8571. }
  8572. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS && !bBigKnockback )
  8573. {
  8574. if ( IsMiniBoss() )
  8575. {
  8576. // Minibosses can't be pushed by anything except heavy rage and airblast (airblast is suppressed when deploying in deploy ai code)
  8577. return;
  8578. }
  8579. else if ( m_nDeployingBombState != TF_BOMB_DEPLOYING_NONE && ( info.GetDamageType() & DMG_BLAST ) == 0 )
  8580. {
  8581. // Regular robots only get pushed by blast damage when deploying the bomb
  8582. return;
  8583. }
  8584. }
  8585. }
  8586. float flDamageForceReduction = 1.f;
  8587. CALL_ATTRIB_HOOK_FLOAT( flDamageForceReduction, damage_force_reduction );
  8588. vecForce *= flDamageForceReduction;
  8589. }
  8590. ApplyAbsVelocityImpulse( vecForce );
  8591. // If we were pushed by an enemy explosion, we're now marked as being blasted by an enemy.
  8592. // If we stay on the ground, next frame our player think will remove this flag.
  8593. if ( info.GetAttacker() != this && info.GetDamageType() & DMG_BLAST )
  8594. {
  8595. m_bTakenBlastDamageSinceLastMovement = true;
  8596. }
  8597. }
  8598. //-----------------------------------------------------------------------------
  8599. // Purpose:
  8600. //-----------------------------------------------------------------------------
  8601. void CTFPlayer::PlayDamageResistSound( float flStartDamage, float flModifiedDamage )
  8602. {
  8603. if ( flStartDamage <= 0.f )
  8604. return;
  8605. // Spam control
  8606. if ( gpGlobals->curtime - m_flLastDamageResistSoundTime <= 0.1f )
  8607. return;
  8608. // Play an absorb sound based on the percentage the damage has been reduced to
  8609. float flDamagePercent = flModifiedDamage / flStartDamage;
  8610. if ( flDamagePercent > 0.f && flDamagePercent < 1.f )
  8611. {
  8612. const char *pszSoundName = ( flDamagePercent >= 0.75f ) ? "Player.ResistanceLight" :
  8613. ( flDamagePercent <= 0.25f ) ? "Player.ResistanceHeavy" : "Player.ResistanceMedium";
  8614. CSoundParameters params;
  8615. if ( CBaseEntity::GetParametersForSound( pszSoundName, params, NULL ) )
  8616. {
  8617. CPASAttenuationFilter filter( GetAbsOrigin(), params.soundlevel );
  8618. EmitSound_t ep( params );
  8619. ep.m_flVolume *= RemapValClamped( flStartDamage, 1.f, 70.f, 0.7f, 1.f );
  8620. EmitSound( filter, entindex(), ep );
  8621. m_flLastDamageResistSoundTime = gpGlobals->curtime;
  8622. }
  8623. }
  8624. }
  8625. //-----------------------------------------------------------------------------
  8626. // Purpose:
  8627. // Input : &info -
  8628. // Output : int
  8629. //-----------------------------------------------------------------------------
  8630. int CTFPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  8631. {
  8632. if ( TFGameRules()->IsInItemTestingMode() && !IsFakeClient() )
  8633. return 0;
  8634. bool bUsingUpgrades = TFGameRules()->GameModeUsesUpgrades();
  8635. // Always NULL check this below
  8636. CTFPlayer *pTFAttacker = ToTFPlayer( info.GetAttacker() );
  8637. CTFGameRules::DamageModifyExtras_t outParams;
  8638. outParams.bIgniting = false;
  8639. outParams.bSelfBlastDmg = false;
  8640. outParams.bSendPreFeignDamage = false;
  8641. outParams.bPlayDamageReductionSound = false;
  8642. float realDamage = info.GetDamage();
  8643. int iPreFeignDamage = realDamage;
  8644. if ( TFGameRules() )
  8645. {
  8646. realDamage = TFGameRules()->ApplyOnDamageAliveModifyRules( info, this, outParams );
  8647. if ( realDamage == -1 )
  8648. {
  8649. // Hard out requested from ApplyOnDamageModifyRules
  8650. return 0;
  8651. }
  8652. }
  8653. if ( outParams.bPlayDamageReductionSound )
  8654. {
  8655. PlayDamageResistSound( info.GetDamage(), realDamage );
  8656. }
  8657. // Grab the vector of the incoming attack.
  8658. // (Pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit).
  8659. Vector vecDir = vec3_origin;
  8660. if ( info.GetInflictor() )
  8661. {
  8662. vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0.0f, 0.0f, 10.0f ) - WorldSpaceCenter();
  8663. info.GetInflictor()->AdjustDamageDirection( info, vecDir, this );
  8664. VectorNormalize( vecDir );
  8665. }
  8666. g_vecAttackDir = vecDir;
  8667. // Do the damage.
  8668. m_bitsDamageType |= info.GetDamageType();
  8669. // Check to see if the Wheatley sapper item is equipped and should react
  8670. if ( m_bitsDamageType & DMG_BULLET && IsPlayerClass( TF_CLASS_SPY ) )
  8671. {
  8672. CBaseCombatWeapon *pRet = GetActiveWeapon();
  8673. CTFWeaponSapper *pSap = dynamic_cast< CTFWeaponSapper* >( pRet );
  8674. if ( pSap != NULL )
  8675. {
  8676. if (pSap->IsWheatleySapper())
  8677. {
  8678. pSap->WheatleyDamage();
  8679. }
  8680. }
  8681. }
  8682. float flBleedingTime = 0.0f;
  8683. int iPrevHealth = m_iHealth;
  8684. if ( m_takedamage != DAMAGE_EVENTS_ONLY )
  8685. {
  8686. if ( info.GetDamageCustom() != TF_DMG_CUSTOM_BLEEDING && !outParams.bSelfBlastDmg )
  8687. {
  8688. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( info.GetWeapon(), flBleedingTime, bleeding_duration );
  8689. }
  8690. // Take damage - round to the nearest integer.
  8691. int iOldHealth = m_iHealth;
  8692. m_iHealth -= ( realDamage + 0.5f );
  8693. if ( IsHeadshot( info.GetDamageCustom() ) && (m_iHealth <= 0) && (iOldHealth != 1) )
  8694. {
  8695. int iNoDeathFromHeadshots = 0;
  8696. CALL_ATTRIB_HOOK_INT( iNoDeathFromHeadshots, no_death_from_headshots );
  8697. if ( iNoDeathFromHeadshots == 1 )
  8698. {
  8699. m_iHealth = 1;
  8700. }
  8701. }
  8702. // For lifeleech, calculate how much damage we actually inflicted.
  8703. CTFPlayer *pAttackingPlayer = dynamic_cast<CTFPlayer *>( info.GetAttacker() );
  8704. if ( pAttackingPlayer && pAttackingPlayer->GetActiveWeapon() )
  8705. {
  8706. float fLifeleechOnDamage = 0.0f;
  8707. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAttackingPlayer->GetActiveWeapon(), fLifeleechOnDamage, lifeleech_on_damage );
  8708. if ( fLifeleechOnDamage > 0.0f )
  8709. {
  8710. const float fActualDamageDealt = iOldHealth - m_iHealth;
  8711. const float fHealAmount = fActualDamageDealt * fLifeleechOnDamage;
  8712. if ( fHealAmount >= 0.5f )
  8713. {
  8714. const int iHealthToAdd = MIN( (int)(fHealAmount + 0.5f), pAttackingPlayer->m_Shared.GetMaxBuffedHealth() - pAttackingPlayer->GetHealth() );
  8715. pAttackingPlayer->TakeHealth( iHealthToAdd, DMG_GENERIC );
  8716. }
  8717. }
  8718. }
  8719. // track accumulated sentry gun damage dealt by players
  8720. if ( pTFAttacker )
  8721. {
  8722. // track amount of damage dealt by defender's sentry guns
  8723. CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( info.GetInflictor() );
  8724. CTFProjectile_SentryRocket *sentryRocket = dynamic_cast< CTFProjectile_SentryRocket * >( info.GetInflictor() );
  8725. if ( ( sentry && !sentry->IsDisposableBuilding() ) || sentryRocket )
  8726. {
  8727. int flooredHealth = clamp( m_iHealth, 0, m_iHealth );
  8728. pTFAttacker->AccumulateSentryGunDamageDealt( iOldHealth - flooredHealth );
  8729. }
  8730. }
  8731. }
  8732. m_flLastDamageTime = gpGlobals->curtime;
  8733. // Apply a damage force.
  8734. CBaseEntity *pAttacker = info.GetAttacker();
  8735. if ( !pAttacker )
  8736. return 0;
  8737. if ( ( info.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE ) == 0 )
  8738. {
  8739. if ( info.GetInflictor() && ( GetMoveType() == MOVETYPE_WALK ) &&
  8740. ( !pAttacker->IsSolidFlagSet( FSOLID_TRIGGER ) ) &&
  8741. ( !m_Shared.InCond( TF_COND_DISGUISED ) ) )
  8742. {
  8743. if ( !m_Shared.InCond( TF_COND_MEGAHEAL ) || outParams.bSelfBlastDmg )
  8744. {
  8745. ApplyPushFromDamage( info, vecDir );
  8746. }
  8747. }
  8748. }
  8749. if ( outParams.bIgniting && pTFAttacker )
  8750. {
  8751. m_Shared.Burn( pTFAttacker, dynamic_cast< CTFWeaponBase * >( info.GetWeapon() ) );
  8752. }
  8753. if ( flBleedingTime > 0 && pTFAttacker )
  8754. {
  8755. m_Shared.MakeBleed( pTFAttacker, dynamic_cast< CTFWeaponBase * >( info.GetWeapon() ), flBleedingTime );
  8756. }
  8757. // Don't recieve reflected damage if you are carrying Reflect (prevents a loop in a game with two Reflect players)
  8758. if ( ( info.GetDamageType() & TF_DMG_CUSTOM_RUNE_REFLECT ) && m_Shared.GetCarryingRuneType() == RUNE_REFLECT )
  8759. {
  8760. return 0;
  8761. }
  8762. CTFWeaponBase *pTFWeapon = dynamic_cast< CTFWeaponBase * >( info.GetWeapon() );
  8763. if ( pTFWeapon && WeaponID_IsSniperRifle( pTFWeapon->GetWeaponID() ) )
  8764. {
  8765. CTFSniperRifle *pSniper = dynamic_cast<CTFSniperRifle*>( pTFWeapon );
  8766. if ( pSniper && ( pSniper->IsZoomed() || ( pSniper->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC ) ) )
  8767. {
  8768. float flJarateTime = pSniper->GetJarateTime();
  8769. if ( flJarateTime && !m_Shared.IsInvulnerable() && !m_Shared.InCond( TF_COND_PHASE ) && !m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  8770. {
  8771. Vector vecOrigin = info.GetDamagePosition();
  8772. CPVSFilter filter( vecOrigin );
  8773. TE_TFParticleEffect( filter, 0.0, "peejar_impact_small", vecOrigin, vec3_angle );
  8774. m_Shared.AddCond( TF_COND_URINE, flJarateTime );
  8775. if ( pTFAttacker )
  8776. {
  8777. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"%s\" against \"%s<%i><%s><%s>\" with \"%s\" (attacker_position \"%d %d %d\") (victim_position \"%d %d %d\")\n",
  8778. pTFAttacker->GetPlayerName(),
  8779. pTFAttacker->GetUserID(),
  8780. pTFAttacker->GetNetworkIDString(),
  8781. pTFAttacker->GetTeam()->GetName(),
  8782. "jarate_attack",
  8783. GetPlayerName(),
  8784. GetUserID(),
  8785. GetNetworkIDString(),
  8786. GetTeam()->GetName(),
  8787. "sniperrifle",
  8788. (int)pTFAttacker->GetAbsOrigin().x,
  8789. (int)pTFAttacker->GetAbsOrigin().y,
  8790. (int)pTFAttacker->GetAbsOrigin().z,
  8791. (int)GetAbsOrigin().x,
  8792. (int)GetAbsOrigin().y,
  8793. (int)GetAbsOrigin().z );
  8794. // explosive jarate shot for a fully charged shot or headshot
  8795. if ( pSniper->IsFullyCharged() || IsHeadshot( info.GetDamageCustom() ) || LastHitGroup() == HITGROUP_HEAD )
  8796. {
  8797. JarExplode( entindex(), pTFAttacker, pTFWeapon, pTFWeapon, info.GetDamagePosition(), pTFAttacker->GetTeamNumber(), 100.f, TF_COND_URINE, flJarateTime, "peejar_impact" );
  8798. }
  8799. }
  8800. }
  8801. if ( bUsingUpgrades && pTFAttacker )
  8802. {
  8803. int iExplosiveShot = 0;
  8804. CALL_ATTRIB_HOOK_INT_ON_OTHER ( pTFAttacker, iExplosiveShot, explosive_sniper_shot );
  8805. if ( iExplosiveShot )
  8806. {
  8807. if ( IsHeadshot( info.GetDamageCustom() ) || ( flJarateTime && LastHitGroup() == HITGROUP_HEAD ) )
  8808. {
  8809. pSniper->ExplosiveHeadShot( pTFAttacker, this );
  8810. }
  8811. }
  8812. }
  8813. }
  8814. }
  8815. // Prevents a sandwich ignore-ammo-while-taking-damage-and-eating alias exploit
  8816. if ( m_Shared.InCond( TF_COND_TAUNTING ) && m_Shared.GetTauntIndex() == TAUNT_BASE_WEAPON )
  8817. {
  8818. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  8819. {
  8820. CTFLunchBox *pLunchBox = dynamic_cast <CTFLunchBox *> ( m_Shared.GetActiveTFWeapon() );
  8821. if ( pLunchBox )
  8822. {
  8823. if ( pLunchBox->GetLunchboxType() != LUNCHBOX_ADDS_MAXHEALTH )
  8824. {
  8825. pLunchBox->DrainAmmo( true );
  8826. }
  8827. }
  8828. }
  8829. }
  8830. // Fire a global game event - "player_hurt"
  8831. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  8832. if ( event )
  8833. {
  8834. event->SetInt( "userid", GetUserID() );
  8835. event->SetInt( "health", MAX( 0, m_iHealth ) );
  8836. // HLTV event priority, not transmitted
  8837. event->SetInt( "priority", 5 );
  8838. int iDamageAmount = ( iPrevHealth - m_iHealth );
  8839. event->SetInt( "damageamount", outParams.bSendPreFeignDamage ? iPreFeignDamage : iDamageAmount );
  8840. // Hurt by another player.
  8841. if ( pAttacker->IsPlayer() )
  8842. {
  8843. CBasePlayer *pPlayer = ToBasePlayer( pAttacker );
  8844. event->SetInt( "attacker", pPlayer->GetUserID() );
  8845. event->SetInt( "custom", info.GetDamageCustom() );
  8846. event->SetBool( "showdisguisedcrit", m_bShowDisguisedCrit );
  8847. event->SetBool( "crit", (info.GetDamageType() & DMG_CRITICAL) != 0 );
  8848. event->SetBool( "minicrit", m_bMiniCrit );
  8849. event->SetBool( "allseecrit", m_bAllSeeCrit );
  8850. Assert( (int)m_eBonusAttackEffect < 256 );
  8851. event->SetInt( "bonuseffect", (int)m_eBonusAttackEffect );
  8852. if ( pTFAttacker && pTFAttacker->GetActiveTFWeapon() )
  8853. {
  8854. event->SetInt( "weaponid", pTFAttacker->GetActiveTFWeapon()->GetWeaponID() );
  8855. }
  8856. }
  8857. // Hurt by world.
  8858. else
  8859. {
  8860. event->SetInt( "attacker", 0 );
  8861. }
  8862. gameeventmanager->FireEvent( event );
  8863. }
  8864. if ( pTFAttacker && pTFAttacker != this )
  8865. {
  8866. pTFAttacker->RecordDamageEvent( info, (m_iHealth <= 0), iPrevHealth );
  8867. }
  8868. //No bleeding while invul or disguised.
  8869. bool bBleed = ( ( m_Shared.InCond( TF_COND_DISGUISED ) == false || m_Shared.GetDisguiseTeam() != pAttacker->GetTeamNumber() )
  8870. && !m_Shared.IsInvulnerable() );
  8871. // No bleed effects for DMG_GENERIC
  8872. if ( info.GetDamageType() == 0 )
  8873. {
  8874. bBleed = false;
  8875. }
  8876. // Except if we are really bleeding!
  8877. bBleed |= m_Shared.InCond( TF_COND_BLEEDING );
  8878. if ( bBleed && pTFAttacker )
  8879. {
  8880. CTFWeaponBase *pWeapon = pTFAttacker->GetActiveTFWeapon();
  8881. if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_FLAMETHROWER )
  8882. {
  8883. bBleed = false;
  8884. }
  8885. }
  8886. if ( bBleed && ( realDamage > 0.f ) )
  8887. {
  8888. Vector vDamagePos = info.GetDamagePosition();
  8889. if ( vDamagePos == vec3_origin )
  8890. {
  8891. vDamagePos = WorldSpaceCenter();
  8892. }
  8893. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  8894. {
  8895. if ( ( IsMiniBoss() && static_cast< float >( GetHealth() ) / GetMaxHealth() > 0.3f ) || realDamage < 50 )
  8896. {
  8897. DispatchParticleEffect( "bot_impact_light", GetAbsOrigin(), vec3_angle );
  8898. }
  8899. else
  8900. {
  8901. DispatchParticleEffect( "bot_impact_heavy", GetAbsOrigin(), vec3_angle );
  8902. }
  8903. }
  8904. else
  8905. {
  8906. CPVSFilter filter( vDamagePos );
  8907. TE_TFBlood( filter, 0.0, vDamagePos, -vecDir, entindex() );
  8908. }
  8909. }
  8910. if ( m_bIsTargetDummy )
  8911. {
  8912. // In the case of a targetdummy bot, restore any damage so it can never die
  8913. TakeHealth( ( iPrevHealth - m_iHealth ), DMG_GENERIC );
  8914. }
  8915. m_vecFeignDeathVelocity = GetAbsVelocity();
  8916. if ( pTFAttacker )
  8917. {
  8918. // If we're invuln, give whomever provided it rewards/credit
  8919. if ( m_Shared.IsInvulnerable() && realDamage > 0.f )
  8920. {
  8921. // Medigun?
  8922. CBaseEntity *pProvider = m_Shared.GetConditionProvider( TF_COND_INVULNERABLE );
  8923. if ( !pProvider && bUsingUpgrades )
  8924. {
  8925. // Bottle?
  8926. pProvider = m_Shared.GetConditionProvider( TF_COND_INVULNERABLE_USER_BUFF );
  8927. }
  8928. if ( pProvider )
  8929. {
  8930. CTFPlayer *pTFProvider = ToTFPlayer( pProvider );
  8931. if ( pTFProvider )
  8932. {
  8933. if ( pTFProvider != pTFAttacker && bUsingUpgrades )
  8934. {
  8935. HandleRageGain( pTFProvider, kRageBuffFlag_OnHeal, ( realDamage / 2.f ), 1.f );
  8936. }
  8937. CTF_GameStats.Event_PlayerBlockedDamage( pTFProvider, realDamage );
  8938. }
  8939. }
  8940. }
  8941. // Give the attacker's medic Energy based on damage done
  8942. CBaseEntity *pProvider = pTFAttacker->m_Shared.GetConditionProvider( TF_COND_HEALTH_BUFF );
  8943. if ( pProvider )
  8944. {
  8945. CTFPlayer *pTFProvider = ToTFPlayer( pProvider );
  8946. if ( pTFProvider && pTFProvider->IsPlayerClass( TF_CLASS_MEDIC ) )
  8947. {
  8948. // Cap to prevent insane values coming from headshots and backstabs
  8949. float flAmount = Min( realDamage, 250.f ) / 10.f;
  8950. HandleRageGain( ToTFPlayer( pProvider ), kRageBuffFlag_OnHeal, flAmount, 1.f );
  8951. }
  8952. }
  8953. }
  8954. // Done.
  8955. return 1;
  8956. }
  8957. //-----------------------------------------------------------------------------
  8958. // Purpose:
  8959. // Input : &info -
  8960. // Output : Returns true on success, false on failure.
  8961. //-----------------------------------------------------------------------------
  8962. bool CTFPlayer::ShouldGib( const CTakeDamageInfo &info )
  8963. {
  8964. // Check to see if we should allow players to gib.
  8965. if ( tf_playergib.GetInt() != 1 )
  8966. {
  8967. if ( tf_playergib.GetInt() < 1 )
  8968. return false;
  8969. else
  8970. return true;
  8971. }
  8972. // normal players/bots don't gib in MvM
  8973. if ( TFGameRules()->IsMannVsMachineMode() )
  8974. return false;
  8975. // Suicide explode always gibs.
  8976. if ( m_bSuicideExplode )
  8977. {
  8978. m_bSuicideExplode = false;
  8979. return true;
  8980. }
  8981. // Are we set up to gib always on critical hits?
  8982. if ( info.GetDamageType() & DMG_CRITICAL )
  8983. {
  8984. int iAlwaysGibOnCrit = 0;
  8985. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iAlwaysGibOnCrit, crit_kill_will_gib );
  8986. if ( iAlwaysGibOnCrit )
  8987. return true;
  8988. }
  8989. int iCritOnHardHit = 0;
  8990. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iCritOnHardHit, crit_on_hard_hit );
  8991. if ( iCritOnHardHit == 0 )
  8992. {
  8993. // Only blast & half falloff damage can gib.
  8994. if ( ( (info.GetDamageType() & DMG_BLAST) == 0 ) &&
  8995. ( (info.GetDamageType() & DMG_HALF_FALLOFF) == 0 ) )
  8996. return false;
  8997. }
  8998. // Explosive crits always gib.
  8999. if ( info.GetDamageType() & DMG_CRITICAL )
  9000. return true;
  9001. // Hard hits also gib.
  9002. if ( GetHealth() <= -10 )
  9003. return true;
  9004. if ( m_bGoingFeignDeath )
  9005. {
  9006. // The player won't actually have negative health,
  9007. // but spies often gib from explosive damage so we should make that likely here.
  9008. float frand = (float) rand() / VALVE_RAND_MAX;
  9009. return (frand>0.15f) ? true : false;
  9010. }
  9011. return false;
  9012. }
  9013. //-----------------------------------------------------------------------------
  9014. // Purpose:
  9015. //-----------------------------------------------------------------------------
  9016. bool CTFPlayer::HasBombinomiconEffectOnDeath( void )
  9017. {
  9018. int iBombinomicomEffectOnDeath = 0;
  9019. CALL_ATTRIB_HOOK_INT( iBombinomicomEffectOnDeath, bombinomicon_effect_on_death );
  9020. return ( iBombinomicomEffectOnDeath != 0 );
  9021. }
  9022. //-----------------------------------------------------------------------------
  9023. // Purpose: Figures out if there is a special assist responsible for our death.
  9024. // Must be called before conditions are cleared druing death.
  9025. //-----------------------------------------------------------------------------
  9026. void CTFPlayer::DetermineAssistForKill( const CTakeDamageInfo &info )
  9027. {
  9028. CTFPlayer *pPlayerAttacker = ToTFPlayer( info.GetAttacker() );
  9029. if ( !pPlayerAttacker )
  9030. return;
  9031. CTFPlayer *pPlayerAssist = NULL;
  9032. if ( m_Shared.GetConditionAssistFromVictim() )
  9033. {
  9034. // If we are covered in urine, mad milk, etc, then give the provider an assist.
  9035. pPlayerAssist = ToTFPlayer( m_Shared.GetConditionAssistFromVictim() );
  9036. }
  9037. if ( m_Shared.IsControlStunned() )
  9038. {
  9039. // If we've been stunned, the stunner gets credit for the assist.
  9040. pPlayerAssist = m_Shared.GetStunner();
  9041. }
  9042. // Can't assist ourself.
  9043. if ( pPlayerAttacker && (pPlayerAttacker != pPlayerAssist) )
  9044. {
  9045. m_Shared.SetAssist( pPlayerAssist );
  9046. }
  9047. else
  9048. {
  9049. m_Shared.SetAssist( NULL );
  9050. }
  9051. }
  9052. //-----------------------------------------------------------------------------
  9053. // Purpose:
  9054. //-----------------------------------------------------------------------------
  9055. void CTFPlayer::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )
  9056. {
  9057. BaseClass::Event_KilledOther( pVictim, info );
  9058. if ( pVictim->IsPlayer() )
  9059. {
  9060. CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
  9061. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  9062. {
  9063. if ( pTFVictim && pTFVictim->IsBot() && ( pTFVictim->GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  9064. {
  9065. if ( pTFVictim->GetDeployingBombState() > TF_BOMB_DEPLOYING_NONE )
  9066. {
  9067. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_kill_robot_delivering_bomb" );
  9068. if ( event )
  9069. {
  9070. event->SetInt( "player", entindex() );
  9071. gameeventmanager->FireEvent( event );
  9072. }
  9073. }
  9074. }
  9075. }
  9076. // Custom death handlers
  9077. // TODO: Need a system here! This conditional is getting pretty big.
  9078. const char *pszCustomDeath = "customdeath:none";
  9079. if ( info.GetAttacker() && info.GetAttacker()->IsBaseObject() )
  9080. {
  9081. pszCustomDeath = "customdeath:sentrygun";
  9082. }
  9083. else if ( info.GetInflictor() && info.GetInflictor()->IsBaseObject() )
  9084. {
  9085. CBaseObject* pObj = dynamic_cast<CBaseObject*>( info.GetInflictor() );
  9086. if ( pObj->IsMiniBuilding() )
  9087. {
  9088. pszCustomDeath = "customdeath:minisentrygun";
  9089. }
  9090. else
  9091. {
  9092. pszCustomDeath = "customdeath:sentrygun";
  9093. }
  9094. }
  9095. else if ( IsHeadshot( info.GetDamageCustom() ) )
  9096. {
  9097. pszCustomDeath = "customdeath:headshot";
  9098. }
  9099. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BACKSTAB )
  9100. {
  9101. pszCustomDeath = "customdeath:backstab";
  9102. }
  9103. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BURNING )
  9104. {
  9105. pszCustomDeath = "customdeath:burning";
  9106. }
  9107. else if ( IsTauntDmg( info.GetDamageCustom() ) )
  9108. {
  9109. pszCustomDeath = "customdeath:taunt";
  9110. }
  9111. else if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BURNING_FLARE )
  9112. {
  9113. pszCustomDeath = "customdeath:flareburn";
  9114. }
  9115. // Revenge handler
  9116. const char *pszDomination = "domination:none";
  9117. if ( pTFVictim->GetDeathFlags() & (TF_DEATH_REVENGE|TF_DEATH_ASSISTER_REVENGE) )
  9118. {
  9119. pszDomination = "domination:revenge";
  9120. }
  9121. else if ( pTFVictim->GetDeathFlags() & TF_DEATH_DOMINATION )
  9122. {
  9123. pszDomination = "domination:dominated";
  9124. }
  9125. const char *pszVictimStunned = "victimstunned:0";
  9126. if ( pTFVictim->m_Shared.InCond( TF_COND_STUNNED ) )
  9127. {
  9128. pszVictimStunned = "victimstunned:1";
  9129. }
  9130. const char *pszVictimDoubleJumping = "victimdoublejumping:0";
  9131. if ( pTFVictim->m_Shared.GetAirDash() > 0 )
  9132. {
  9133. pszVictimDoubleJumping = "victimdoublejumping:1";
  9134. }
  9135. CFmtStrN<128> modifiers( "%s,%s,%s,%s,victimclass:%s", pszCustomDeath, pszDomination, pszVictimStunned, pszVictimDoubleJumping, g_aPlayerClassNames_NonLocalized[ pTFVictim->GetPlayerClass()->GetClassIndex() ] );
  9136. bool bPlayspeech = true;
  9137. // Don't play speech if this kill disguises the spy
  9138. if ( IsPlayerClass( TF_CLASS_SPY ) )
  9139. {
  9140. if ( !Q_stricmp( "customdeath:backstab", pszCustomDeath ) )
  9141. {
  9142. CTFKnife *pKnife = dynamic_cast<CTFKnife *>( GetActiveTFWeapon() );
  9143. if ( pKnife && pKnife->GetKnifeType() == KNIFE_DISGUISE_ONKILL )
  9144. {
  9145. bPlayspeech = false;
  9146. }
  9147. }
  9148. }
  9149. if ( bPlayspeech )
  9150. {
  9151. SpeakConceptIfAllowed( MP_CONCEPT_KILLED_PLAYER, modifiers );
  9152. }
  9153. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>(info.GetWeapon());
  9154. if ( pWeapon )
  9155. {
  9156. pWeapon->OnPlayerKill( pTFVictim, info );
  9157. int iCritBoost = 0;
  9158. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iCritBoost, add_onkill_critboost_time );
  9159. if ( iCritBoost )
  9160. {
  9161. // Perceptually, people seem to think the effect is shorter than the stated time, so we cheat by adding a tad more for that
  9162. m_Shared.AddCond( TF_COND_CRITBOOSTED_ON_KILL, iCritBoost+1 );
  9163. }
  9164. int iMiniCritBoost = 0;
  9165. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iMiniCritBoost, add_onkill_minicritboost_time );
  9166. if ( iMiniCritBoost )
  9167. {
  9168. // Perceptually, people seem to think the effect is shorter than the stated time, so we cheat by adding a tad more for that
  9169. m_Shared.AddCond( TF_COND_ENERGY_BUFF, iMiniCritBoost + 1 );
  9170. }
  9171. }
  9172. // Check for CP_Foundry achievements
  9173. if ( FStrEq( "cp_foundry", STRING( gpGlobals->mapname ) ) )
  9174. {
  9175. if ( pTFVictim && ( pTFVictim->GetTeamNumber() != GetTeamNumber() ) )
  9176. {
  9177. if ( pTFVictim->IsCapturingPoint() )
  9178. {
  9179. if ( info.GetDamageType() & DMG_CRITICAL )
  9180. {
  9181. AwardAchievement( ACHIEVEMENT_TF_MAPS_FOUNDRY_KILL_CAPPING_ENEMY );
  9182. }
  9183. }
  9184. if ( InAchievementZone( pTFVictim ) )
  9185. {
  9186. IGameEvent *event = gameeventmanager->CreateEvent( "player_killed_achievement_zone" );
  9187. if ( event )
  9188. {
  9189. event->SetInt( "attacker", entindex() );
  9190. event->SetInt( "victim", pTFVictim->entindex() );
  9191. gameeventmanager->FireEvent( event );
  9192. }
  9193. }
  9194. }
  9195. }
  9196. // Check for SD_Doomsday achievements
  9197. if ( FStrEq( "sd_doomsday", STRING( gpGlobals->mapname ) ) )
  9198. {
  9199. if ( pTFVictim && ( pTFVictim->GetTeamNumber() != GetTeamNumber() ) )
  9200. {
  9201. // find the flag in the map
  9202. CCaptureFlag *pFlag = NULL;
  9203. for ( int i=0; i<ICaptureFlagAutoList::AutoList().Count(); ++i )
  9204. {
  9205. pFlag = static_cast< CCaptureFlag* >( ICaptureFlagAutoList::AutoList()[i] );
  9206. if ( !pFlag->IsDisabled() )
  9207. {
  9208. break;
  9209. }
  9210. }
  9211. // was the victim in an achievement zone?
  9212. CAchievementZone *pZone = InAchievementZone( pTFVictim );
  9213. if ( pZone )
  9214. {
  9215. int iZoneID = pZone->GetZoneID();
  9216. if ( iZoneID == 0 )
  9217. {
  9218. if ( pFlag && pFlag->IsHome() )
  9219. {
  9220. AwardAchievement( ACHIEVEMENT_TF_MAPS_DOOMSDAY_DENY_NEUTRAL_PICKUP );
  9221. }
  9222. }
  9223. else
  9224. {
  9225. IGameEvent *event = gameeventmanager->CreateEvent( "player_killed_achievement_zone" );
  9226. if ( event )
  9227. {
  9228. event->SetInt( "attacker", entindex() );
  9229. event->SetInt( "victim", pTFVictim->entindex() );
  9230. event->SetInt( "zone_id", iZoneID );
  9231. gameeventmanager->FireEvent( event );
  9232. }
  9233. }
  9234. }
  9235. // check the flag carrier to see if the victim has recently damaged them
  9236. if ( pFlag && pFlag->IsStolen() )
  9237. {
  9238. CTFPlayer *pFlagCarrier = ToTFPlayer( pFlag->GetOwnerEntity() );
  9239. if ( pFlagCarrier && ( pFlagCarrier->GetTeamNumber() == GetTeamNumber() ) )
  9240. {
  9241. // has the victim damaged the flag carrier in the last 3 seconds?
  9242. if ( pFlagCarrier->m_AchievementData.IsDamagerInHistory( pTFVictim, 3.0 ) )
  9243. {
  9244. AwardAchievement( ACHIEVEMENT_TF_MAPS_DOOMSDAY_DEFEND_CARRIER );
  9245. }
  9246. }
  9247. }
  9248. }
  9249. }
  9250. // Check for CP_Snakewater achievement
  9251. if ( FStrEq( "cp_snakewater_final1", STRING( gpGlobals->mapname ) ) )
  9252. {
  9253. if ( pTFVictim && ( pTFVictim->GetTeamNumber() != GetTeamNumber() ) )
  9254. {
  9255. if ( InAchievementZone( pTFVictim ) )
  9256. {
  9257. IGameEvent *event = gameeventmanager->CreateEvent( "player_killed_achievement_zone" );
  9258. if ( event )
  9259. {
  9260. event->SetInt( "attacker", entindex() );
  9261. event->SetInt( "victim", pTFVictim->entindex() );
  9262. gameeventmanager->FireEvent( event );
  9263. }
  9264. }
  9265. }
  9266. }
  9267. if ( IsPlayerClass( TF_CLASS_DEMOMAN ) )
  9268. {
  9269. if ( pVictim->GetTeamNumber() != GetTeamNumber() )
  9270. {
  9271. // Check if this kill should refill the charge meter
  9272. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>(info.GetWeapon());
  9273. float flRefill = 0.0f;
  9274. CALL_ATTRIB_HOOK_FLOAT( flRefill, kill_refills_meter );
  9275. if ( m_Shared.GetCarryingRuneType() == RUNE_KNOCKOUT ) // Knockout powerup restricts charge
  9276. {
  9277. flRefill *= 0.2;
  9278. }
  9279. if ( flRefill > 0 && ((info.GetDamageType() & DMG_MELEE) || ( info.GetDamageCustom() == TF_DMG_CUSTOM_CHARGE_IMPACT ) ) )
  9280. {
  9281. m_Shared.SetDemomanChargeMeter( m_Shared.GetDemomanChargeMeter() + flRefill * 100.0f );
  9282. }
  9283. if ( ( pWeapon && pWeapon->IsCurrentAttackDuringDemoCharge() ) || ( info.GetDamageCustom() == TF_DMG_CUSTOM_CHARGE_IMPACT ) )
  9284. {
  9285. if ( flRefill > 0 )
  9286. {
  9287. IGameEvent *event = gameeventmanager->CreateEvent( "kill_refills_meter" );
  9288. if ( event )
  9289. {
  9290. event->SetInt( "index", entindex() );
  9291. gameeventmanager->FireEvent( event );
  9292. }
  9293. }
  9294. if ( pTFVictim )
  9295. {
  9296. // could the attacker see this player when the charge started?
  9297. if ( m_Shared.m_hPlayersVisibleAtChargeStart.Find( pTFVictim ) == m_Shared.m_hPlayersVisibleAtChargeStart.InvalidIndex() )
  9298. {
  9299. AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL_PLAYER_YOU_DIDNT_SEE );
  9300. }
  9301. }
  9302. }
  9303. // Demoman achievement: Kill at least 3 players capping or pushing the cart with the same detonation
  9304. CTriggerAreaCapture *pAreaTrigger = pTFVictim->GetControlPointStandingOn();
  9305. if ( pAreaTrigger )
  9306. {
  9307. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  9308. if ( pCP )
  9309. {
  9310. if ( pCP->GetOwner() == GetTeamNumber() )
  9311. {
  9312. if ( GetActiveTFWeapon() && ( GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_PIPEBOMBLAUNCHER ) )
  9313. {
  9314. // Add victim to our list
  9315. int iIndex = m_Cappers.Find( pTFVictim->GetUserID() );
  9316. if ( iIndex != m_Cappers.InvalidIndex() )
  9317. {
  9318. // they're already in our list
  9319. m_Cappers[iIndex] = gpGlobals->curtime;
  9320. }
  9321. else
  9322. {
  9323. // we need to add them
  9324. m_Cappers.Insert( pTFVictim->GetUserID(), gpGlobals->curtime );
  9325. }
  9326. // Did we get three?
  9327. if ( m_Cappers.Count() >= 3 )
  9328. {
  9329. // Traverse the list, comparing the recorded time to curtime
  9330. int iHitCount = 0;
  9331. FOR_EACH_MAP_FAST ( m_Cappers, cIndex )
  9332. {
  9333. // For each match, increment counter
  9334. if ( gpGlobals->curtime <= m_Cappers[cIndex] + 0.1f )
  9335. {
  9336. iHitCount++;
  9337. }
  9338. else
  9339. {
  9340. m_Cappers.Remove( cIndex );
  9341. }
  9342. // If we hit 3, award and purge the group
  9343. if ( iHitCount >= 3 )
  9344. {
  9345. AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL_X_CAPPING_ONEDET );
  9346. m_Cappers.RemoveAll();
  9347. }
  9348. }
  9349. }
  9350. }
  9351. }
  9352. // Kill players defending "x" times
  9353. else
  9354. {
  9355. // If we're able to cap the point...
  9356. if ( TeamplayGameRules()->TeamMayCapturePoint( GetTeamNumber(), pCP->GetPointIndex() ) &&
  9357. TeamplayGameRules()->PlayerMayCapturePoint( this, pCP->GetPointIndex() ) )
  9358. {
  9359. AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL_X_DEFENDING );
  9360. }
  9361. }
  9362. }
  9363. }
  9364. }
  9365. }
  9366. // Sniper Kill Rage
  9367. if ( IsPlayerClass( TF_CLASS_SNIPER ) )
  9368. {
  9369. // Item attribute
  9370. // Add Sniper Rage On Kills
  9371. float flRageGain = 0;
  9372. CALL_ATTRIB_HOOK_FLOAT( flRageGain, rage_on_kill );
  9373. if (flRageGain != 0)
  9374. {
  9375. m_Shared.ModifyRage(flRageGain);
  9376. }
  9377. }
  9378. for ( int i=0; i<m_Shared.m_nNumHealers; i++ )
  9379. {
  9380. m_Shared.m_aHealers[i].iKillsWhileBeingHealed++;
  9381. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  9382. {
  9383. if ( m_Shared.m_aHealers[i].iKillsWhileBeingHealed >= 5 && m_Shared.m_aHealers[i].bDispenserHeal )
  9384. {
  9385. // We got five kills while being healed by this dispenser. Reward the engineer with an achievement!
  9386. CTFPlayer *pHealScorer = ToTFPlayer( m_Shared.m_aHealers[i].pHealScorer );
  9387. if ( pHealScorer && pHealScorer->IsPlayerClass( TF_CLASS_ENGINEER ) )
  9388. {
  9389. pHealScorer->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_HEAVY_ASSIST );
  9390. }
  9391. }
  9392. }
  9393. }
  9394. OnKilledOther_Effects( pVictim, info );
  9395. // track accumulated sentry gun kills on owning player for Sentry Busters in MvM (so they can't clear this by rebuilding their sentry)
  9396. CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( info.GetInflictor() );
  9397. CTFProjectile_SentryRocket *sentryRocket = dynamic_cast< CTFProjectile_SentryRocket * >( info.GetInflictor() );
  9398. if ( ( sentry && !sentry->IsDisposableBuilding() ) || sentryRocket )
  9399. {
  9400. IncrementSentryGunKillCount();
  9401. }
  9402. // Halloween Death Ghosts
  9403. // Check the weapon I used to kill with this player and if it has my desired attribute
  9404. if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  9405. {
  9406. int iHalloweenDeathGhosts = 0;
  9407. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iHalloweenDeathGhosts, halloween_death_ghosts );
  9408. if ( iHalloweenDeathGhosts > 0 )
  9409. {
  9410. if ( pTFVictim->GetTeam()->GetTeamNumber() == TF_TEAM_BLUE )
  9411. {
  9412. DispatchParticleEffect( "halloween_player_death_blue", pTFVictim->GetAbsOrigin() + Vector( 0, 0, 32 ), vec3_angle );
  9413. }
  9414. else if ( pTFVictim->GetTeam()->GetTeamNumber() == TF_TEAM_RED )
  9415. {
  9416. DispatchParticleEffect( "halloween_player_death", pTFVictim->GetAbsOrigin() + Vector( 0, 0, 32 ), vec3_angle );
  9417. }
  9418. }
  9419. }
  9420. DropDeathCallingCard( this, pTFVictim );
  9421. if ( pTFVictim != this )
  9422. {
  9423. for ( int i=0; i<GetNumWearables(); ++i )
  9424. {
  9425. CTFWearableLevelableItem *pItem = dynamic_cast< CTFWearableLevelableItem* >( GetWearable(i) );
  9426. if ( pItem )
  9427. {
  9428. pItem->IncrementLevel();
  9429. }
  9430. }
  9431. }
  9432. if ( pTFVictim )
  9433. {
  9434. // was the victim on a control point (includes payload carts)
  9435. CTriggerAreaCapture *pAreaTrigger = pTFVictim->GetControlPointStandingOn();
  9436. if ( pAreaTrigger )
  9437. {
  9438. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  9439. if ( pCP && ( pCP->GetOwner() != pTFVictim->GetTeamNumber() ) )
  9440. {
  9441. if ( TeamplayGameRules()->TeamMayCapturePoint( pTFVictim->GetTeamNumber(), pCP->GetPointIndex() ) &&
  9442. TeamplayGameRules()->PlayerMayCapturePoint( pTFVictim, pCP->GetPointIndex() ) )
  9443. {
  9444. CTFPlayer *pTFAssister = NULL;
  9445. if ( TFGameRules() )
  9446. {
  9447. pTFAssister = ToTFPlayer( TFGameRules()->GetAssister( pTFVictim, this, info.GetInflictor() ) );
  9448. }
  9449. IGameEvent *event = gameeventmanager->CreateEvent( "killed_capping_player" );
  9450. if ( event )
  9451. {
  9452. event->SetInt( "cp", pCP->GetPointIndex() );
  9453. event->SetInt( "killer", entindex() );
  9454. event->SetInt( "victim", pTFVictim->entindex() );
  9455. event->SetInt( "assister", pTFAssister ? pTFAssister->entindex() : -1 );
  9456. event->SetInt( "priority", 9 );
  9457. gameeventmanager->FireEvent( event );
  9458. }
  9459. }
  9460. }
  9461. }
  9462. }
  9463. }
  9464. else
  9465. {
  9466. if ( pVictim->IsBaseObject() )
  9467. {
  9468. CBaseObject *pObject = dynamic_cast<CBaseObject *>( pVictim );
  9469. SpeakConceptIfAllowed( MP_CONCEPT_KILLED_OBJECT, pObject->GetResponseRulesModifier() );
  9470. }
  9471. }
  9472. }
  9473. //-----------------------------------------------------------------------------
  9474. // Purpose: Called on kill for primary and second-highest damage dealer
  9475. //-----------------------------------------------------------------------------
  9476. void CTFPlayer::OnKilledOther_Effects( CBaseEntity *pVictim, const CTakeDamageInfo &info )
  9477. {
  9478. int iHealOnKill = 0;
  9479. if ( IsPlayerClass( TF_CLASS_SPY ) )
  9480. {
  9481. int iCloakOnKill = 0;
  9482. CALL_ATTRIB_HOOK_INT_ON_OTHER( GetActiveWeapon(), iCloakOnKill, add_cloak_on_kill );
  9483. if ( iCloakOnKill > 0 )
  9484. {
  9485. m_Shared.AddToSpyCloakMeter( iCloakOnKill, true );
  9486. }
  9487. }
  9488. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>( info.GetWeapon() );
  9489. if ( !pWeapon )
  9490. return;
  9491. int iRestoreHealthToPercentageOnKill = 0;
  9492. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iRestoreHealthToPercentageOnKill, restore_health_on_kill );
  9493. if ( iRestoreHealthToPercentageOnKill > 0 )
  9494. {
  9495. // This attribute should ignore runes
  9496. int iRestoreMax = GetMaxHealth() - GetRuneHealthBonus();
  9497. // We add one here to deal with a bizarre problem that comes up leaving you one health short sometimes
  9498. // due to bizarre floating point rounding or something equally silly.
  9499. int iTargetHealth = ( int )( ( ( float )iRestoreHealthToPercentageOnKill / 100.0f ) * ( float )iRestoreMax ) + 1;
  9500. int iBaseMaxHealth = GetMaxHealth() * 1.5,
  9501. iNewHealth = Min( GetHealth() + iTargetHealth, iBaseMaxHealth ),
  9502. iDeltaHealth = Max(iNewHealth - GetHealth(), 0);
  9503. TakeHealth( iDeltaHealth, DMG_IGNORE_MAXHEALTH );
  9504. }
  9505. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iHealOnKill, heal_on_kill );
  9506. if ( iHealOnKill != 0 )
  9507. {
  9508. int iHealthToAdd = MIN( iHealOnKill, m_Shared.GetMaxBuffedHealth() - m_iHealth );
  9509. TakeHealth( iHealthToAdd, DMG_GENERIC );
  9510. //m_iHealth += iHealthToAdd;
  9511. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  9512. if ( event )
  9513. {
  9514. event->SetInt( "amount", iHealthToAdd );
  9515. event->SetInt( "entindex", entindex() );
  9516. item_definition_index_t healingItemDef = INVALID_ITEM_DEF_INDEX;
  9517. if ( pWeapon->GetAttributeContainer() && pWeapon->GetAttributeContainer()->GetItem() )
  9518. {
  9519. healingItemDef = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex();
  9520. }
  9521. event->SetInt( "weapon_def_index", healingItemDef );
  9522. gameeventmanager->FireEvent( event );
  9523. }
  9524. }
  9525. int iSpeedBoostOnKill = 0;
  9526. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iSpeedBoostOnKill, speed_boost_on_kill );
  9527. if ( iSpeedBoostOnKill )
  9528. {
  9529. m_Shared.AddCond( TF_COND_SPEED_BOOST, iSpeedBoostOnKill );
  9530. }
  9531. }
  9532. //-----------------------------------------------------------------------------
  9533. // Purpose:
  9534. //-----------------------------------------------------------------------------
  9535. void CTFPlayer::Event_Killed( const CTakeDamageInfo &info )
  9536. {
  9537. CTFPlayer *pPlayerAttacker = NULL;
  9538. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
  9539. {
  9540. pPlayerAttacker = ToTFPlayer( info.GetAttacker() );
  9541. }
  9542. CTFWeaponBase *pKillerWeapon = NULL;
  9543. if ( pPlayerAttacker )
  9544. {
  9545. pKillerWeapon = dynamic_cast < CTFWeaponBase * > ( info.GetWeapon() );
  9546. }
  9547. if ( m_Shared.InCond( TF_COND_TAUNTING ) )
  9548. {
  9549. static CSchemaItemDefHandle dosidoTaunt( "Square Dance Taunt" );
  9550. static CSchemaItemDefHandle congaTaunt( "Conga Taunt" );
  9551. if ( GetTauntEconItemView() )
  9552. {
  9553. if ( GetTauntEconItemView()->GetItemDefinition() == dosidoTaunt )
  9554. {
  9555. if ( pKillerWeapon && ( pKillerWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_MELEE ) )
  9556. {
  9557. if ( pPlayerAttacker )
  9558. {
  9559. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_TAUNT_DOSIDO_MELLE_KILL );
  9560. }
  9561. }
  9562. }
  9563. else if ( GetTauntEconItemView()->GetItemDefinition() == congaTaunt )
  9564. {
  9565. if ( pPlayerAttacker )
  9566. {
  9567. IGameEvent *event = gameeventmanager->CreateEvent( "conga_kill" );
  9568. if ( event )
  9569. {
  9570. event->SetInt( "index", pPlayerAttacker->entindex() );
  9571. gameeventmanager->FireEvent( event );
  9572. }
  9573. }
  9574. }
  9575. }
  9576. StopTaunt();
  9577. }
  9578. // Cheat this death!
  9579. if ( m_Shared.InCond( TF_COND_HALLOWEEN_IN_HELL ) )
  9580. {
  9581. // Turn into a ghost
  9582. m_Shared.RemoveAllCond();
  9583. m_Shared.AddCond( TF_COND_HALLOWEEN_GHOST_MODE );
  9584. // Create a puff right where we died to mask the ghost spawning in
  9585. DispatchParticleEffect( "ghost_appearation", PATTACH_ABSORIGIN, this );
  9586. // Check for achievement
  9587. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_TRIGGER_HURT )
  9588. {
  9589. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  9590. {
  9591. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 5.0 );
  9592. if ( pRecentDamager )
  9593. {
  9594. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_ENVIRONMENTAL_KILLS );
  9595. }
  9596. }
  9597. }
  9598. CTakeDamageInfo ghostinfo = info;
  9599. // If we were killed by "the world", then give credit to the next damager in the list
  9600. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 10.0 );
  9601. // If killed by trigger hurt, get last attacker
  9602. if ( info.GetAttacker() == info.GetInflictor() && info.GetAttacker() && info.GetAttacker()->IsBSPModel() )
  9603. {
  9604. if ( pRecentDamager )
  9605. {
  9606. ghostinfo.SetAttacker( pRecentDamager );
  9607. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  9608. {
  9609. ghostinfo.SetDamageCustom( TF_DMG_CUSTOM_KART );
  9610. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_KILL_KARTS );
  9611. HatAndMiscEconEntities_OnOwnerKillEaterEvent( pRecentDamager, this, kKillEaterEvent_Halloween_UnderworldKills );
  9612. }
  9613. }
  9614. // if no recent damager, check for HHH
  9615. else if ( m_flHHHKartAttackTime > gpGlobals->curtime - 15.0f )
  9616. {
  9617. ghostinfo.SetDamageCustom( TF_DMG_CUSTOM_DECAPITATION_BOSS );
  9618. }
  9619. }
  9620. if ( pRecentDamager )
  9621. {
  9622. // Score the "kill". We don't want any of the other logic, so short circuit here.
  9623. pRecentDamager->Event_KilledOther( this, ghostinfo );
  9624. IGameEvent *pEvent = gameeventmanager->CreateEvent( "kill_in_hell" );
  9625. if ( pEvent )
  9626. {
  9627. pEvent->SetInt( "killer", pRecentDamager->GetUserID() );
  9628. pEvent->SetInt( "victim", GetUserID() );
  9629. gameeventmanager->FireEvent( pEvent, true );
  9630. }
  9631. }
  9632. FeignDeath( ghostinfo );
  9633. // Have 1 HP
  9634. m_iHealth = 1;
  9635. return;
  9636. }
  9637. SpeakConceptIfAllowed( MP_CONCEPT_DIED );
  9638. StateTransition( TF_STATE_DYING ); // Transition into the dying state.
  9639. if ( pPlayerAttacker )
  9640. {
  9641. if ( TFGameRules()->IsIT( this ) )
  9642. {
  9643. // I was IT - transfer to my killer
  9644. TFGameRules()->SetIT( pPlayerAttacker );
  9645. }
  9646. if ( pPlayerAttacker != this )
  9647. {
  9648. if ( CTFPlayerDestructionLogic::GetRobotDestructionLogic() && ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetType() == CTFPlayerDestructionLogic::TYPE_PLAYER_DESTRUCTION ) )
  9649. {
  9650. // was this the team leader?
  9651. if ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetTeamLeader( GetTeamNumber() ) == this )
  9652. {
  9653. IGameEvent * event = gameeventmanager->CreateEvent( "team_leader_killed" );
  9654. if ( event )
  9655. {
  9656. event->SetInt( "killer", pPlayerAttacker->entindex() );
  9657. event->SetInt( "victim", entindex() );
  9658. gameeventmanager->FireEvent( event );
  9659. }
  9660. }
  9661. }
  9662. }
  9663. }
  9664. m_bIsTeleportingUsingEurekaEffect = false;
  9665. for ( int i=0; i<GetNumWearables(); ++i )
  9666. {
  9667. CTFWearableLevelableItem *pItem = dynamic_cast< CTFWearableLevelableItem* >( GetWearable(i) );
  9668. if ( pItem )
  9669. {
  9670. pItem->ResetLevel();
  9671. }
  9672. }
  9673. /*
  9674. // We're going to save this for a future date
  9675. if ( pPlayerAttacker )
  9676. {
  9677. if ( pPlayerAttacker != this )
  9678. {
  9679. // Killed by another player
  9680. if ( ( TFGameRules()->GetBirthdayPlayer() == this ) || ( TFGameRules()->GetBirthdayPlayer() == NULL ) )
  9681. {
  9682. // I was the birthday player (or we don't have one) - transfer to my killer
  9683. TFGameRules()->SetBirthdayPlayer( pPlayerAttacker );
  9684. }
  9685. }
  9686. else
  9687. {
  9688. // Suicide
  9689. if ( TFGameRules()->GetBirthdayPlayer() == this )
  9690. {
  9691. // I was the birthday player - reset for suicide
  9692. TFGameRules()->SetBirthdayPlayer( NULL );
  9693. }
  9694. }
  9695. }
  9696. */
  9697. bool bOnGround = GetFlags() & FL_ONGROUND;
  9698. bool bElectrocuted = false;
  9699. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED );
  9700. // we want the rag doll to burn if the player was burning and was not a pyro (who only burns momentarily)
  9701. bool bBurning = m_Shared.InCond( TF_COND_BURNING ) && ( TF_CLASS_PYRO != GetPlayerClass()->GetClassIndex() );
  9702. CTFPlayer *pOriginalBurner = m_Shared.GetOriginalBurnAttacker();
  9703. CTFPlayer *pLastBurner = m_Shared.GetBurnAttacker();
  9704. if ( m_aBurnFromBackAttackers.Count() > 0 )
  9705. {
  9706. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>(info.GetWeapon());
  9707. if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_FLAMETHROWER )
  9708. {
  9709. for ( int i = 0; i < m_aBurnFromBackAttackers.Count(); i++ )
  9710. {
  9711. CTFPlayer *pBurner = ToTFPlayer( m_aBurnFromBackAttackers[i].Get() );
  9712. if ( pBurner )
  9713. {
  9714. pBurner->AwardAchievement( ACHIEVEMENT_TF_PYRO_KILL_FROM_BEHIND );
  9715. }
  9716. }
  9717. }
  9718. ClearBurnFromBehindAttackers();
  9719. }
  9720. if ( IsPlayerClass( TF_CLASS_MEDIC ) )
  9721. {
  9722. CWeaponMedigun* pMedigun = assert_cast<CWeaponMedigun*>( Weapon_OwnsThisID( TF_WEAPON_MEDIGUN ) );
  9723. float flChargeLevel = pMedigun ? pMedigun->GetChargeLevel() : 0.f;
  9724. float flMinChargeLevel = pMedigun ? pMedigun->GetMinChargeAmount() : 1.f;
  9725. bool bCharged = flChargeLevel >= flMinChargeLevel;
  9726. if ( bCharged )
  9727. {
  9728. // Had an ubercharge ready at death?
  9729. CEconEntity *pVictimEconWeapon = dynamic_cast<CEconEntity *>( GetActiveTFWeapon() );
  9730. EconEntity_OnOwnerKillEaterEventNoPartner( pVictimEconWeapon, this, kKillEaterEvent_NEGATIVE_UbersDropped );
  9731. bElectrocuted = true;
  9732. if ( pPlayerAttacker )
  9733. {
  9734. if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SCOUT ) )
  9735. {
  9736. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SCOUT_KILL_CHARGED_MEDICS );
  9737. }
  9738. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SNIPER ) )
  9739. {
  9740. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_KILL_CHARGED_MEDIC );
  9741. }
  9742. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SPY ) )
  9743. {
  9744. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BACKSTAB )
  9745. {
  9746. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SPY_BACKSTAB_MEDIC_CHARGED );
  9747. }
  9748. }
  9749. CTF_GameStats.Event_PlayerAwardBonusPoints( pPlayerAttacker, this, 20 );
  9750. }
  9751. }
  9752. // Disable radius healing
  9753. m_Shared.Heal_Radius( false );
  9754. IGameEvent * event = gameeventmanager->CreateEvent( "medic_death" );
  9755. if ( event )
  9756. {
  9757. int iHealing = 0;
  9758. PlayerStats_t *pPlayerStats = CTF_GameStats.FindPlayerStats( this );
  9759. if ( pPlayerStats )
  9760. {
  9761. iHealing = pPlayerStats->statsCurrentLife.m_iStat[TFSTAT_HEALING];
  9762. // defensive fix for the moment for bug where healing value becomes bogus sometimes: if bogus, slam it to 0
  9763. // ...copied from CTFGameRules::CalcPlayerScore()
  9764. if ( iHealing < 0 || iHealing > 10000000 )
  9765. {
  9766. iHealing = 0;
  9767. }
  9768. }
  9769. event->SetInt( "userid", GetUserID() );
  9770. event->SetInt( "attacker", pPlayerAttacker ? pPlayerAttacker->GetUserID() : 0 );
  9771. event->SetInt( "healing", iHealing );
  9772. event->SetBool( "charged", bCharged );
  9773. gameeventmanager->FireEvent( event );
  9774. }
  9775. }
  9776. else if ( IsPlayerClass( TF_CLASS_SOLDIER ) || IsPlayerClass( TF_CLASS_DEMOMAN ) )
  9777. {
  9778. if ( pPlayerAttacker && pPlayerAttacker->IsPlayerClass( TF_CLASS_SNIPER ) && RocketJumped() && !GetGroundEntity() )
  9779. {
  9780. if ( pKillerWeapon )
  9781. {
  9782. if ( WeaponID_IsSniperRifleOrBow( pKillerWeapon->GetWeaponID() ) )
  9783. {
  9784. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_KILL_RJER );
  9785. }
  9786. if ( pKillerWeapon->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC )
  9787. {
  9788. if ( ( info.GetDamageCustom() == TF_DMG_CUSTOM_HEADSHOT ) && ( info.GetDamageType() & DMG_CRITICAL ) )
  9789. {
  9790. if ( pPlayerAttacker->m_Shared.IsAiming() == false )
  9791. {
  9792. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_CLASSIC_RIFLE_HEADSHOT_JUMPER );
  9793. }
  9794. }
  9795. }
  9796. }
  9797. }
  9798. }
  9799. else if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  9800. {
  9801. if ( pPlayerAttacker && pPlayerAttacker->IsPlayerClass( TF_CLASS_SOLDIER ) )
  9802. {
  9803. // Has Engineer worked on his sentrygun recently?
  9804. CBaseObject *pSentry = GetObjectOfType( OBJ_SENTRYGUN );
  9805. if ( pSentry && m_AchievementData.IsTargetInHistory( pSentry, 4.0 ) )
  9806. {
  9807. if ( pSentry->m_AchievementData.CountDamagersWithinTime( 3.0 ) > 0 )
  9808. {
  9809. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_KILL_ENGY );
  9810. }
  9811. }
  9812. }
  9813. if ( m_Shared.IsCarryingObject() )
  9814. {
  9815. CTakeDamageInfo info( pPlayerAttacker, pPlayerAttacker, NULL, vec3_origin, GetAbsOrigin(), 0, DMG_GENERIC );
  9816. info.SetDamageCustom( TF_DMG_CUSTOM_CARRIED_BUILDING );
  9817. if ( m_Shared.GetCarriedObject() != NULL )
  9818. {
  9819. m_Shared.GetCarriedObject()->Killed( info );
  9820. // Killeater event for being killed while carrying a building
  9821. CEconEntity *pVictimEconWeapon = dynamic_cast<CEconEntity *>( Weapon_OwnsThisID( TF_WEAPON_WRENCH ) );
  9822. EconEntity_OnOwnerKillEaterEventNoPartner( pVictimEconWeapon, this, kKillEaterEvent_NEGATIVE_DeathsWhileCarryingBuilding );
  9823. }
  9824. }
  9825. }
  9826. else if ( IsPlayerClass( TF_CLASS_SNIPER ) )
  9827. {
  9828. if ( pPlayerAttacker )
  9829. {
  9830. if ( GetActiveTFWeapon() && ( GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC ) )
  9831. {
  9832. if ( pKillerWeapon && ( pKillerWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_MELEE ) )
  9833. {
  9834. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_MELEE_KILL_CLASSIC_RIFLE_SNIPER );
  9835. }
  9836. }
  9837. }
  9838. }
  9839. if ( pPlayerAttacker )
  9840. {
  9841. if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SOLDIER ) )
  9842. {
  9843. if ( pPlayerAttacker->RocketJumped() || (gpGlobals->curtime - pPlayerAttacker->m_flBlastJumpLandTime) < 1 )
  9844. {
  9845. if ( pKillerWeapon && pKillerWeapon->GetWeaponID() == TF_WEAPON_SHOVEL )
  9846. {
  9847. CTFShovel *pShovel = static_cast< CTFShovel* >( pKillerWeapon );
  9848. if ( pShovel && pShovel->GetShovelType() == SHOVEL_DAMAGE_BOOST )
  9849. {
  9850. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_RJ_EQUALIZER_KILL );
  9851. }
  9852. }
  9853. }
  9854. }
  9855. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SNIPER ) )
  9856. {
  9857. if ( pKillerWeapon && WeaponID_IsSniperRifle( pKillerWeapon->GetWeaponID() ) && pPlayerAttacker->m_Shared.IsAiming() == false )
  9858. {
  9859. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_KILL_UNSCOPED );
  9860. }
  9861. if ( pKillerWeapon && ( pKillerWeapon->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC ) )
  9862. {
  9863. if ( ( info.GetDamageCustom() == TF_DMG_CUSTOM_HEADSHOT ) && ( info.GetDamageType() & DMG_CRITICAL ) )
  9864. {
  9865. if ( pPlayerAttacker->m_Shared.IsAiming() == false )
  9866. {
  9867. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_CLASSIC_RIFLE_NOSCOPE_HEADSHOT );
  9868. }
  9869. }
  9870. }
  9871. }
  9872. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_SPY ) )
  9873. {
  9874. #ifdef STAGING_ONLY
  9875. // Move to Killed Other
  9876. // Spy Tranq Buff
  9877. if ( m_Shared.InCond( TF_COND_TRANQ_MARKED ) && info.GetDamageCustom() == TF_DMG_CUSTOM_BACKSTAB )
  9878. {
  9879. int iTranq = 0;
  9880. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayerAttacker, iTranq, override_projectile_type );
  9881. if ( iTranq == TF_PROJECTILE_TRANQ )
  9882. {
  9883. // BIGGEST HACK EVER
  9884. int iDesiredClass = GetPlayerClass()->GetClassIndex();
  9885. if ( iDesiredClass != TF_CLASS_SPY )
  9886. {
  9887. pPlayerAttacker->GetPlayerClass()->Init( iDesiredClass );
  9888. for ( int i = 0; i < MAX_WEAPONS; i++ )
  9889. {
  9890. CTFWeaponBase *pWeapon = (CTFWeaponBase *)pPlayerAttacker->GetWeapon( i );
  9891. if ( pWeapon )
  9892. {
  9893. pWeapon->OnOwnerClassChange();
  9894. }
  9895. }
  9896. pPlayerAttacker->RemoveAllItems( true );
  9897. // TODO: move this into conditions
  9898. pPlayerAttacker->RemoveTeleportEffect();
  9899. // remove invisibility very quickly
  9900. pPlayerAttacker->m_Shared.FadeInvis( 0.1f );
  9901. // Stop any firing that was taking place before respawn.
  9902. pPlayerAttacker->m_nButtons = 0;
  9903. // Possibly Save and set their health percentage here
  9904. Vector vAttackerPos = pPlayerAttacker->GetAbsOrigin();
  9905. QAngle qAttackerAngle = pPlayerAttacker->GetAbsAngles();
  9906. pPlayerAttacker->StateTransition( TF_STATE_ACTIVE );
  9907. pPlayerAttacker->Spawn();
  9908. pPlayerAttacker->Teleport( &vAttackerPos, &qAttackerAngle, &vec3_origin );
  9909. pPlayerAttacker->m_Shared.AddCond( TF_COND_SPY_CLASS_STEAL );
  9910. // Overheal
  9911. pPlayerAttacker->SetHealth( pPlayerAttacker->GetMaxHealth() * 1.5f );
  9912. // Steal their uber
  9913. if ( IsPlayerClass( TF_CLASS_MEDIC ) )
  9914. {
  9915. // Steal Enemy Uber
  9916. CWeaponMedigun *pMedigun = (CWeaponMedigun *)Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  9917. if ( pMedigun )
  9918. {
  9919. float flCharge = pMedigun->GetChargeLevel();
  9920. CWeaponMedigun *pAttackerMedigun = (CWeaponMedigun *)pPlayerAttacker->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  9921. if ( pAttackerMedigun )
  9922. {
  9923. pAttackerMedigun->AddCharge( flCharge );
  9924. }
  9925. }
  9926. }
  9927. // Steal Rage
  9928. pPlayerAttacker->m_Shared.SetRageMeter( m_Shared.GetRageMeter() );
  9929. // Steal heads
  9930. pPlayerAttacker->m_Shared.SetDecapitations( m_Shared.GetDecapitations() );
  9931. // Effects
  9932. //pPlayerAttacker->EmitSound( "Player.Spy_Disguise" );
  9933. pPlayerAttacker->EmitSound( "WeaponDNAGun.Transform" );
  9934. Vector vOrigin = pPlayerAttacker->GetAbsOrigin();
  9935. CPVSFilter filter( vOrigin );
  9936. switch ( pPlayerAttacker->GetTeamNumber() )
  9937. {
  9938. case TF_TEAM_RED:
  9939. TE_TFParticleEffect( filter, 0.0, "teleported_red", vOrigin, vec3_angle );
  9940. TE_TFParticleEffect( filter, 0.0, "player_sparkles_red", vOrigin, vec3_angle, pPlayerAttacker, PATTACH_POINT );
  9941. break;
  9942. case TF_TEAM_BLUE:
  9943. TE_TFParticleEffect( filter, 0.0, "teleported_blue", vOrigin, vec3_angle );
  9944. TE_TFParticleEffect( filter, 0.0, "player_sparkles_blue", vOrigin, vec3_angle, pPlayerAttacker, PATTACH_POINT );
  9945. break;
  9946. default:
  9947. break;
  9948. }
  9949. }
  9950. }
  9951. }
  9952. #endif // STAGING_ONLY
  9953. CTriggerAreaCapture *pAreaTrigger = GetControlPointStandingOn();
  9954. if ( pAreaTrigger )
  9955. {
  9956. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  9957. if ( pCP )
  9958. {
  9959. if ( pCP->GetOwner() == GetTeamNumber() )
  9960. {
  9961. // killed on a control point owned by my team
  9962. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SPY_KILL_CP_DEFENDERS );
  9963. }
  9964. else
  9965. {
  9966. // killed on a control point NOT owned by my team, was it a backstab?
  9967. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BACKSTAB )
  9968. {
  9969. // was i able to capture the control point?
  9970. if ( TeamplayGameRules()->TeamMayCapturePoint( GetTeamNumber(), pCP->GetPointIndex() ) &&
  9971. TeamplayGameRules()->PlayerMayCapturePoint( this, pCP->GetPointIndex() ) )
  9972. {
  9973. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SPY_BACKSTAB_CAPPING_ENEMIES );
  9974. }
  9975. }
  9976. }
  9977. }
  9978. }
  9979. if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  9980. {
  9981. //m_AchievementData.CountTargetsWithinTime
  9982. int iHistory = 0;
  9983. EntityHistory_t *pHistory = m_AchievementData.GetTargetHistory( iHistory );
  9984. while ( pHistory )
  9985. {
  9986. if ( pHistory->hEntity && pHistory->hEntity->IsBaseObject() && m_AchievementData.IsTargetInHistory( pHistory->hEntity, 1.0f ) )
  9987. {
  9988. CBaseObject *pObject = dynamic_cast<CBaseObject *>( pHistory->hEntity.Get() );
  9989. if ( pObject->ObjectType() == OBJ_SENTRYGUN )
  9990. {
  9991. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SPY_KILL_WORKING_ENGY );
  9992. break;
  9993. }
  9994. }
  9995. iHistory++;
  9996. pHistory = m_AchievementData.GetTargetHistory( iHistory );
  9997. }
  9998. }
  9999. }
  10000. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_DEMOMAN ) )
  10001. {
  10002. // Kill "x" players with a direct pipebomb hit
  10003. if ( pPlayerAttacker->GetActiveTFWeapon() && ( pPlayerAttacker->GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_GRENADELAUNCHER ) )
  10004. {
  10005. CBaseEntity *pInflictor = info.GetInflictor();
  10006. if ( pInflictor && pInflictor->IsPlayer() == false )
  10007. {
  10008. CTFGrenadePipebombProjectile *pBaseGrenade = dynamic_cast< CTFGrenadePipebombProjectile* >( pInflictor );
  10009. if ( pBaseGrenade && pBaseGrenade->m_bTouched != true )
  10010. {
  10011. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL_X_WITH_DIRECTPIPE );
  10012. }
  10013. }
  10014. }
  10015. }
  10016. else if ( pPlayerAttacker->IsPlayerClass( TF_CLASS_ENGINEER ) )
  10017. {
  10018. // give achievement for killing someone who was recently damaged by our sentry
  10019. // note that we don't check to see if the sentry is still alive
  10020. if ( pKillerWeapon &&
  10021. ( pKillerWeapon->GetWeaponID() == TF_WEAPON_SENTRY_REVENGE ||
  10022. pKillerWeapon->GetWeaponID() == TF_WEAPON_SHOTGUN_PRIMARY ) )
  10023. {
  10024. if ( m_AchievementData.IsSentryDamagerInHistory( pPlayerAttacker, 5.0 ) )
  10025. {
  10026. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_SHOTGUN_KILL_PREV_SENTRY_TARGET );
  10027. }
  10028. }
  10029. }
  10030. // Revenge Crits for Diamondback
  10031. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BACKSTAB )
  10032. {
  10033. pPlayerAttacker->m_Shared.IncrementRevengeCrits();
  10034. }
  10035. }
  10036. // Check for CP_Foundry achievement
  10037. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_TRIGGER_HURT )
  10038. {
  10039. if ( FStrEq( "cp_foundry", STRING( gpGlobals->mapname ) ) )
  10040. {
  10041. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 5.0 );
  10042. if ( pRecentDamager )
  10043. {
  10044. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_MAPS_FOUNDRY_PUSH_INTO_CAULDRON );
  10045. }
  10046. }
  10047. }
  10048. // Record if we were stunned for achievement tracking.
  10049. m_iOldStunFlags = m_Shared.GetStunFlags();
  10050. // Determine the optional assist for the kill.
  10051. DetermineAssistForKill( info );
  10052. // put here to stop looping kritz sound from playing til respawn.
  10053. if ( m_Shared.InCond( TF_COND_CRITBOOSTED ) )
  10054. {
  10055. StopSound( "TFPlayer.CritBoostOn" );
  10056. }
  10057. if ( m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  10058. {
  10059. SetPendingMerasmusPlayerBombExplode();
  10060. }
  10061. // check for MvM achievements
  10062. if ( TFGameRules()->IsMannVsMachineMode() && IsBot() )
  10063. {
  10064. if ( pPlayerAttacker && ( pPlayerAttacker->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) )
  10065. {
  10066. if ( FStrEq( "mvm_mannhattan", STRING( gpGlobals->mapname ) ) )
  10067. {
  10068. CTFBot *pBot = dynamic_cast< CTFBot* >( this );
  10069. if ( pBot )
  10070. {
  10071. // kill gate bots
  10072. if ( pBot->HasTag( "bot_gatebot" ) )
  10073. {
  10074. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_MVM_MAPS_MANNHATTAN_BOMB_BOT_GRIND );
  10075. }
  10076. }
  10077. // kill stunned bots
  10078. if ( m_Shared.InCond( TF_COND_MVM_BOT_STUN_RADIOWAVE ) )
  10079. {
  10080. if ( g_pPopulationManager->IsAdvancedPopFile() )
  10081. {
  10082. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_adv_wave_killed_stun_radio" );
  10083. if ( event )
  10084. {
  10085. gameeventmanager->FireEvent( event );
  10086. }
  10087. }
  10088. }
  10089. }
  10090. }
  10091. }
  10092. // Reset our model if we were disguised
  10093. if ( bDisguised )
  10094. {
  10095. UpdateModel();
  10096. }
  10097. RemoveTeleportEffect();
  10098. // Drop a pack with their leftover ammo
  10099. // Arena: Only do this if the match hasn't started yet.
  10100. if ( ShouldDropAmmoPack() )
  10101. {
  10102. DropAmmoPack( info, false, false );
  10103. }
  10104. if ( TFGameRules()->IsInMedievalMode() )
  10105. {
  10106. DropHealthPack( info, true );
  10107. }
  10108. #ifdef TF_RAID_MODE
  10109. // Bots sometimes drop health kits in Raid Mode
  10110. if ( TFGameRules()->IsRaidMode() && GetTeamNumber() == TF_TEAM_RED )
  10111. {
  10112. if ( RandomInt( 1, 100 ) <= tf_raid_drop_healthkit_chance.GetInt() )
  10113. {
  10114. DropHealthPack( info, true );
  10115. }
  10116. }
  10117. #endif // TF_RAID_MODE
  10118. // PvE mode credits/currency
  10119. if ( TFGameRules()->IsMannVsMachineMode() )
  10120. {
  10121. MannVsMachineStats_PlayerEvent_Died( this );
  10122. if ( IsBot() )
  10123. {
  10124. m_nCurrency = 0;
  10125. if ( !IsMissionEnemy() && m_pWaveSpawnPopulator )
  10126. {
  10127. m_nCurrency = m_pWaveSpawnPopulator->GetCurrencyAmountPerDeath();
  10128. }
  10129. // only drop currency if the map designer has specified it
  10130. if ( m_nCurrency > 0 )
  10131. {
  10132. // We only drop a pack when the game's accumulated enough to make it worth it
  10133. int nDropAmount = TFGameRules()->CalculateCurrencyAmount_CustomPack( m_nCurrency );
  10134. if ( nDropAmount )
  10135. {
  10136. bool bDropPack = true;
  10137. // Give money directly to the team if a trigger killed us
  10138. if ( info.GetDamageType() )
  10139. {
  10140. CBaseTrigger *pTrigger = dynamic_cast< CBaseTrigger *>( info.GetInflictor() );
  10141. if ( pTrigger )
  10142. {
  10143. bDropPack = false;
  10144. TFGameRules()->DistributeCurrencyAmount( nDropAmount, NULL, true, true );
  10145. }
  10146. }
  10147. if ( bDropPack )
  10148. {
  10149. CTFPlayer* pMoneyMaker = NULL;
  10150. if ( pPlayerAttacker && pPlayerAttacker->IsPlayerClass( TF_CLASS_SNIPER ) )
  10151. {
  10152. if ( info.GetDamageCustom() == TF_DMG_CUSTOM_BLEEDING || ( pKillerWeapon && WeaponID_IsSniperRifleOrBow( pKillerWeapon->GetWeaponID() ) ) )
  10153. {
  10154. pMoneyMaker = pPlayerAttacker;
  10155. if ( IsHeadshot( info.GetDamageCustom() ) || ( LastHitGroup() == HITGROUP_HEAD && pKillerWeapon && pKillerWeapon->GetJarateTime() ) )
  10156. {
  10157. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_sniper_headshot_currency" );
  10158. if ( event )
  10159. {
  10160. event->SetInt( "userid", pPlayerAttacker->GetUserID() );
  10161. event->SetInt( "currency", nDropAmount );
  10162. gameeventmanager->FireEvent( event );
  10163. }
  10164. }
  10165. }
  10166. }
  10167. int iForceDistributeCurrency = 0;
  10168. CALL_ATTRIB_HOOK_INT( iForceDistributeCurrency, force_distribute_currency_on_death );
  10169. bool bForceDistribute = iForceDistributeCurrency != 0;
  10170. // if I'm force to distribute currency, just give the credit to the attacker
  10171. if ( !pMoneyMaker && bForceDistribute )
  10172. {
  10173. pMoneyMaker = pPlayerAttacker;
  10174. }
  10175. DropCurrencyPack( TF_CURRENCY_PACK_CUSTOM, nDropAmount, bForceDistribute, pMoneyMaker );
  10176. }
  10177. }
  10178. }
  10179. if ( !m_bIsSupportEnemy )
  10180. {
  10181. unsigned int iFlags = m_bIsMissionEnemy ? MVM_CLASS_FLAG_MISSION : MVM_CLASS_FLAG_NORMAL;
  10182. if ( IsMiniBoss() )
  10183. {
  10184. iFlags |= MVM_CLASS_FLAG_MINIBOSS;
  10185. }
  10186. TFObjectiveResource()->DecrementMannVsMachineWaveClassCount( GetPlayerClass()->GetClassIconName(), iFlags );
  10187. }
  10188. if ( m_bIsLimitedSupportEnemy )
  10189. {
  10190. TFObjectiveResource()->DecrementMannVsMachineWaveClassCount( GetPlayerClass()->GetClassIconName(), MVM_CLASS_FLAG_SUPPORT_LIMITED );
  10191. }
  10192. // Electrical effect whenever a bot dies
  10193. CPVSFilter filter( WorldSpaceCenter() );
  10194. TE_TFParticleEffect( filter, 0.f, "bot_death", GetAbsOrigin(), vec3_angle );
  10195. }
  10196. else
  10197. {
  10198. // Players lose money for dying
  10199. RemoveCurrency( tf_mvm_death_penalty.GetInt() );
  10200. }
  10201. // tell the population manager a player died
  10202. // THIS MUST HAPPEN AFTER THE CURRENCY CALCULATION (ABOVE)
  10203. // NOW THAT WE'RE CALCULATING CURRENCY ON-DEATH INSTEAD OF ON-SPAWN
  10204. if ( g_pPopulationManager )
  10205. {
  10206. g_pPopulationManager->OnPlayerKilled( this );
  10207. }
  10208. if ( IsBot() && HasTheFlag() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  10209. {
  10210. int nLevel = TFObjectiveResource()->GetFlagCarrierUpgradeLevel();
  10211. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_bomb_carrier_killed" );
  10212. if ( event )
  10213. {
  10214. event->SetInt( "level", nLevel );
  10215. gameeventmanager->FireEvent( event );
  10216. }
  10217. }
  10218. if ( !IsBot() && !m_hReviveMarker )
  10219. {
  10220. m_hReviveMarker = CTFReviveMarker::Create( this );
  10221. }
  10222. }
  10223. // This system is designed to coarsely measure a player's skill in public pvp games.
  10224. // UpdateSkillRatingData();
  10225. #ifdef STAGING_ONLY
  10226. if ( TFGameRules()->IsBountyMode() )
  10227. {
  10228. // Lose unspent currency on death?
  10229. float flPenalty = tf_bountymode_currency_penalty_ondeath.GetFloat();
  10230. if ( flPenalty )
  10231. {
  10232. int nAmount = GetCurrency();
  10233. if ( nAmount )
  10234. {
  10235. nAmount *= ( 1.f - flPenalty );
  10236. SetCurrency( Max( nAmount, 0 ) );
  10237. }
  10238. }
  10239. if ( tf_bountymode_upgrades_wipeondeath.GetInt() )
  10240. {
  10241. // Remove upgrade attributes from the player and their items
  10242. if ( g_hUpgradeEntity )
  10243. {
  10244. g_hUpgradeEntity->GrantOrRemoveAllUpgrades( this, true, false );
  10245. }
  10246. // Remove the appropriate upgrade info from upgrade histories
  10247. if ( g_pPopulationManager )
  10248. {
  10249. g_pPopulationManager->RemovePlayerAndItemUpgradesFromHistory( this );
  10250. }
  10251. }
  10252. }
  10253. #endif // STAGING_ONLY
  10254. if ( pPlayerAttacker )
  10255. {
  10256. int iDropHealthOnKill = 0;
  10257. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayerAttacker, iDropHealthOnKill, drop_health_pack_on_kill );
  10258. if ( iDropHealthOnKill == 1 )
  10259. {
  10260. DropHealthPack( info, true );
  10261. }
  10262. int iKillForcesAttackerToLaugh = 0;
  10263. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayerAttacker, iKillForcesAttackerToLaugh, kill_forces_attacker_to_laugh );
  10264. if ( iKillForcesAttackerToLaugh == 1 )
  10265. {
  10266. // force yourself to laugh!
  10267. pPlayerAttacker->Taunt( TAUNT_MISC_ITEM, MP_CONCEPT_TAUNT_LAUGH );
  10268. }
  10269. }
  10270. // If the player has a capture flag and was killed by another player, award that player a defense
  10271. if ( HasItem() && pPlayerAttacker && ( pPlayerAttacker != this ) )
  10272. {
  10273. CCaptureFlag *pCaptureFlag = dynamic_cast<CCaptureFlag *>( GetItem() );
  10274. if ( pCaptureFlag )
  10275. {
  10276. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  10277. if ( event )
  10278. {
  10279. event->SetInt( "player", pPlayerAttacker->entindex() );
  10280. event->SetInt( "eventtype", TF_FLAGEVENT_DEFEND );
  10281. event->SetInt( "carrier", entindex() );
  10282. event->SetInt( "priority", 8 );
  10283. event->SetInt( "team", pCaptureFlag->GetTeamNumber() );
  10284. gameeventmanager->FireEvent( event );
  10285. }
  10286. CTF_GameStats.Event_PlayerDefendedPoint( pPlayerAttacker );
  10287. if ( !CTFPlayerDestructionLogic::GetRobotDestructionLogic() || ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetType() != CTFPlayerDestructionLogic::TYPE_PLAYER_DESTRUCTION ) )
  10288. {
  10289. if ( pPlayerAttacker && pPlayerAttacker->IsPlayerClass( TF_CLASS_SNIPER ) )
  10290. {
  10291. CTFWeaponBase *pKillerWeapon = dynamic_cast < CTFWeaponBase * > ( info.GetWeapon() );
  10292. if ( pKillerWeapon && pKillerWeapon->GetWeaponID() == TF_WEAPON_COMPOUND_BOW )
  10293. {
  10294. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_BOW_KILL_FLAGCARRIER );
  10295. }
  10296. }
  10297. // Handle the "you killed someone with the flag" event. We can't handle this with the usual block
  10298. // in PlayerKilled() because by that point we've forgotten that we had the flag.
  10299. EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pKillerWeapon ), pPlayerAttacker, this, kKillEaterEvent_DefenderKill );
  10300. }
  10301. }
  10302. }
  10303. CTFWeaponBase* pActiveWeapon = GetActiveTFWeapon();
  10304. if( pActiveWeapon )
  10305. {
  10306. CEconEntity *pVictimEconWeapon = dynamic_cast<CEconEntity *>( pActiveWeapon );
  10307. EconEntity_OnOwnerKillEaterEventNoPartner( pVictimEconWeapon, this, kKillEaterEvent_NEGATIVE_Deaths );
  10308. // Check if we died from environmental damage
  10309. CBaseTrigger *pTrigger = dynamic_cast< CBaseTrigger *>( info.GetInflictor() );
  10310. if ( pTrigger )
  10311. {
  10312. EconEntity_OnOwnerKillEaterEventNoPartner( pVictimEconWeapon, this, kKillEaterEvent_NEGATIVE_DeathsFromEnvironment );
  10313. }
  10314. // Check if we died from fall damage
  10315. if( info.GetDamageType() == DMG_FALL )
  10316. {
  10317. EconEntity_OnOwnerKillEaterEventNoPartner( pVictimEconWeapon, this, kKillEaterEvent_NEGATIVE_DeathsFromCratering );
  10318. }
  10319. }
  10320. ClearZoomOwner();
  10321. m_vecLastDeathPosition = GetAbsOrigin();
  10322. CTakeDamageInfo info_modified = info;
  10323. // Ragdoll, gib, or death animation.
  10324. bool bRagdoll = true;
  10325. bool bGib = false;
  10326. // See if we should gib.
  10327. if ( ShouldGib( info ) )
  10328. {
  10329. bGib = true;
  10330. bRagdoll = false;
  10331. }
  10332. else
  10333. // See if we should play a custom death animation.
  10334. {
  10335. // If this was a rocket/grenade kill that didn't gib, exaggerated the blast force
  10336. if ( ( info.GetDamageType() & DMG_BLAST ) != 0 )
  10337. {
  10338. Vector vForceModifier = info.GetDamageForce();
  10339. vForceModifier.x *= 2.5;
  10340. vForceModifier.y *= 2.5;
  10341. vForceModifier.z *= 2;
  10342. info_modified.SetDamageForce( vForceModifier );
  10343. }
  10344. }
  10345. if ( bElectrocuted && bGib )
  10346. {
  10347. const char *pEffectName = ( GetTeamNumber() == TF_TEAM_RED ) ? "electrocuted_gibbed_red" : "electrocuted_gibbed_blue";
  10348. DispatchParticleEffect( pEffectName, GetAbsOrigin(), vec3_angle );
  10349. EmitSound( "TFPlayer.MedicChargedDeath" );
  10350. }
  10351. SetGibbedOnLastDeath( bGib );
  10352. bool bIsMvMRobot = TFGameRules()->IsMannVsMachineMode() && IsBot();
  10353. if ( bGib && !bIsMvMRobot && IsPlayerClass( TF_CLASS_SCOUT ) && RandomInt( 1, 100 ) <= SCOUT_ADD_BIRD_ON_GIB_CHANCE )
  10354. {
  10355. Vector vecPos = WorldSpaceCenter();
  10356. SpawnClientsideFlyingBird( vecPos );
  10357. }
  10358. // show killer in death cam mode
  10359. // chopped down version of SetObserverTarget without the team check
  10360. if( pPlayerAttacker )
  10361. {
  10362. // See if we were killed by a sentrygun. If so, look at that instead of the player
  10363. if ( info.GetInflictor() && info.GetInflictor()->IsBaseObject() )
  10364. {
  10365. // Catches the case where we're killed directly by the sentrygun (i.e. bullets)
  10366. // Look at the sentrygun
  10367. m_hObserverTarget.Set( info.GetInflictor() );
  10368. }
  10369. // See if we were killed by a projectile emitted from a base object. The attacker
  10370. // will still be the owner of that object, but we want the deathcam to point to the
  10371. // object itself.
  10372. else if ( info.GetInflictor() && info.GetInflictor()->GetOwnerEntity() &&
  10373. info.GetInflictor()->GetOwnerEntity()->IsBaseObject() )
  10374. {
  10375. m_hObserverTarget.Set( info.GetInflictor()->GetOwnerEntity() );
  10376. }
  10377. else
  10378. {
  10379. // Look at the player
  10380. if ( m_Shared.InCond( TF_COND_HALLOWEEN_IN_HELL ) )
  10381. {
  10382. m_hObserverTarget.Set( pPlayerAttacker );
  10383. }
  10384. else
  10385. {
  10386. m_hObserverTarget.Set( info.GetAttacker() );
  10387. }
  10388. }
  10389. // reset fov to default
  10390. SetFOV( this, 0 );
  10391. }
  10392. else if ( info.GetAttacker() && info.GetAttacker()->IsBaseObject() )
  10393. {
  10394. // Catches the case where we're killed by entities spawned by the sentrygun (i.e. rockets)
  10395. // Look at the sentrygun.
  10396. m_hObserverTarget.Set( info.GetAttacker() );
  10397. }
  10398. else if ( info.GetAttacker() && TFGameRules()->GetActiveBoss() && info.GetAttacker()->entindex() == TFGameRules()->GetActiveBoss()->entindex() )
  10399. {
  10400. // killed by the boss - look at him
  10401. m_hObserverTarget.Set( info.GetAttacker() );
  10402. }
  10403. else
  10404. {
  10405. m_hObserverTarget.Set( NULL );
  10406. }
  10407. bool bSuicide = false;
  10408. if ( info_modified.GetDamageCustom() == TF_DMG_CUSTOM_SUICIDE )
  10409. {
  10410. bSuicide = true;
  10411. // if this was suicide, recalculate attacker to see if we want to award the kill to a recent damager
  10412. info_modified.SetAttacker( TFGameRules()->GetDeathScorer( info.GetAttacker(), info.GetInflictor(), this ) );
  10413. }
  10414. else if ( info.GetAttacker() == this )
  10415. {
  10416. bSuicide = true;
  10417. // If we killed ourselves in non-suicide fashion, and we've been hurt lately, give that guy the kill.
  10418. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 0, 5.0 );
  10419. if ( pRecentDamager )
  10420. {
  10421. info_modified.SetDamageCustom( TF_DMG_CUSTOM_SUICIDE );
  10422. info_modified.SetDamageType( DMG_GENERIC );
  10423. info_modified.SetAttacker( pRecentDamager );
  10424. info_modified.SetWeapon( NULL );
  10425. info_modified.SetInflictor( NULL );
  10426. }
  10427. }
  10428. else if ( info.GetAttacker() == info.GetInflictor() && info.GetAttacker() && info.GetAttacker()->IsBSPModel() )
  10429. {
  10430. bSuicide = true;
  10431. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  10432. {
  10433. // If we were killed by "the world", then give credit to the next damager in the list
  10434. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 10.0 );
  10435. if ( pRecentDamager )
  10436. {
  10437. //info_modified.SetDamageCustom( TF_DMG_CUSTOM_SUICIDE );
  10438. info_modified.SetDamageType( DMG_GENERIC );
  10439. info_modified.SetAttacker( pRecentDamager );
  10440. info_modified.SetWeapon( NULL );
  10441. info_modified.SetInflictor( NULL );
  10442. }
  10443. }
  10444. else
  10445. {
  10446. // If we were killed by "the world", then give credit to the next damager in the list
  10447. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 5.0 );
  10448. if ( pRecentDamager )
  10449. {
  10450. info_modified.SetDamageCustom( TF_DMG_CUSTOM_SUICIDE );
  10451. info_modified.SetDamageType( DMG_GENERIC );
  10452. info_modified.SetAttacker( pRecentDamager );
  10453. info_modified.SetWeapon( NULL );
  10454. info_modified.SetInflictor( NULL );
  10455. }
  10456. else if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) && ( info_modified.GetDamageType() & DMG_CLUB ) )
  10457. {
  10458. info_modified.SetDamageCustom( TF_DMG_CUSTOM_GIANT_HAMMER );
  10459. info_modified.SetDamageType( info_modified.GetDamageType() | DMG_CRITICAL );
  10460. }
  10461. }
  10462. }
  10463. if ( pPlayerAttacker && pPlayerAttacker->m_Shared.InCond( TF_COND_HALLOWEEN_TINY ) && !pPlayerAttacker->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  10464. {
  10465. info_modified.SetDamageCustom( TF_DMG_CUSTOM_SPELL_TINY );
  10466. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  10467. {
  10468. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_TINY_SMASHER );
  10469. }
  10470. }
  10471. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  10472. {
  10473. // Report Kill
  10474. CTF_GameStats.Event_PowerUpModeDeath( pPlayerAttacker, this );
  10475. }
  10476. // Drop your powerup rune when you die
  10477. if ( m_Shared.IsCarryingRune() )
  10478. {
  10479. int iTeam = GetEnemyTeam( GetTeamNumber() ); // Dead players drop opposing team colored powerups
  10480. CTFRune::CreateRune( GetAbsOrigin(), m_Shared.GetCarryingRuneType(), iTeam, true, false );
  10481. }
  10482. // in PD, player death adds points to the flag drop
  10483. if ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()
  10484. && CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetType() == CTFPlayerDestructionLogic::TYPE_PLAYER_DESTRUCTION )
  10485. {
  10486. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 0, 5.0 );
  10487. int pointsOnDeath = ( !bSuicide || pRecentDamager ) ? CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetPointsOnPlayerDeath() : 0;
  10488. CCaptureFlag *pFlag = NULL;
  10489. if ( HasItem() )
  10490. {
  10491. pFlag = dynamic_cast<CCaptureFlag*>( GetItem() );
  10492. }
  10493. else
  10494. {
  10495. if ( pointsOnDeath && !PointInRespawnRoom( this, WorldSpaceCenter() ) )
  10496. {
  10497. pFlag = CCaptureFlag::Create( GetAbsOrigin(), CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetPropModelName(), TF_FLAGTYPE_PLAYER_DESTRUCTION );
  10498. }
  10499. }
  10500. if ( pFlag )
  10501. {
  10502. // don't add more point to the dropping flag if the player suicided
  10503. if ( pointsOnDeath )
  10504. {
  10505. pFlag->AddPointValue( pointsOnDeath );
  10506. }
  10507. pFlag->Drop( this, true, true, true );
  10508. }
  10509. }
  10510. CTFPlayerResource *pResource = dynamic_cast<CTFPlayerResource *>( g_pPlayerResource );
  10511. if ( pResource )
  10512. {
  10513. pResource->SetPlayerClassWhenKilled( entindex(), GetPlayerClass()->GetClassIndex() );
  10514. }
  10515. BaseClass::Event_Killed( info_modified );
  10516. if ( !m_bSwitchedClass )
  10517. {
  10518. SaveLastWeaponSlot();
  10519. }
  10520. // Remove all items...
  10521. RemoveAllItems( true );
  10522. for ( int iWeapon = 0; iWeapon < TF_PLAYER_WEAPON_COUNT; ++iWeapon )
  10523. {
  10524. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon( iWeapon );
  10525. if ( pWeapon )
  10526. {
  10527. pWeapon->WeaponReset();
  10528. }
  10529. }
  10530. if ( GetActiveWeapon() )
  10531. {
  10532. m_iActiveWeaponTypePriorToDeath = GetActiveTFWeapon()->GetWeaponID();
  10533. if ( m_iActiveWeaponTypePriorToDeath == TF_WEAPON_BUILDER )
  10534. m_iActiveWeaponTypePriorToDeath = 0;
  10535. GetActiveWeapon()->SendViewModelAnim( ACT_IDLE );
  10536. GetActiveWeapon()->Holster();
  10537. SetActiveWeapon( NULL );
  10538. }
  10539. else
  10540. {
  10541. m_iActiveWeaponTypePriorToDeath = 0;
  10542. }
  10543. int iIceRagdoll = 0;
  10544. CTFPlayer *pInflictor = ToTFPlayer( info.GetInflictor() );
  10545. if ( ( IsHeadshot( info.GetDamageCustom() ) ) && pPlayerAttacker )
  10546. {
  10547. CTFWeaponBase *pWpn = ( CTFWeaponBase *) info.GetWeapon();
  10548. bool bBowShot = false;
  10549. if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_COMPOUND_BOW )
  10550. {
  10551. bBowShot = true;
  10552. }
  10553. CTF_GameStats.Event_Headshot( pPlayerAttacker, bBowShot );
  10554. }
  10555. else if ( ( TF_DMG_CUSTOM_BACKSTAB == info.GetDamageCustom() ) && pInflictor )
  10556. {
  10557. CTF_GameStats.Event_Backstab( pInflictor );
  10558. if ( pKillerWeapon )
  10559. {
  10560. CALL_ATTRIB_HOOK_INT_ON_OTHER( pKillerWeapon, iIceRagdoll, freeze_backstab_victim );
  10561. }
  10562. }
  10563. bool bCloakedCorpse = false;
  10564. if ( pKillerWeapon && pKillerWeapon->GetWeaponID() == TF_WEAPON_KNIFE )
  10565. {
  10566. CTFKnife *pKnife = dynamic_cast<CTFKnife*>( pKillerWeapon );
  10567. if ( pKnife && pKnife->ShouldDisguiseOnBackstab() )
  10568. {
  10569. bCloakedCorpse = true;
  10570. }
  10571. }
  10572. int iGoldRagdoll = 0;
  10573. if ( pKillerWeapon )
  10574. {
  10575. CALL_ATTRIB_HOOK_INT_ON_OTHER( pKillerWeapon, iGoldRagdoll, set_turn_to_gold );
  10576. }
  10577. int iRagdollsBecomeAsh = 0;
  10578. if ( info.GetWeapon() )
  10579. {
  10580. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iRagdollsBecomeAsh, ragdolls_become_ash );
  10581. }
  10582. int iRagdollsPlasmaEffect = 0;
  10583. if ( info.GetWeapon() )
  10584. {
  10585. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iRagdollsPlasmaEffect, ragdolls_plasma_effect );
  10586. }
  10587. int iCustomDamage = info.GetDamageCustom();
  10588. if ( iRagdollsPlasmaEffect )
  10589. {
  10590. iCustomDamage = TF_DMG_CUSTOM_PLASMA;
  10591. }
  10592. int iCritOnHardHit = 0;
  10593. if ( info.GetWeapon() )
  10594. {
  10595. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iCritOnHardHit, crit_on_hard_hit );
  10596. }
  10597. // Create the ragdoll entity.
  10598. if ( bGib || bRagdoll )
  10599. {
  10600. CreateRagdollEntity( bGib, bBurning, bElectrocuted, bOnGround, bCloakedCorpse, iGoldRagdoll != 0, iIceRagdoll != 0, iRagdollsBecomeAsh != 0, iCustomDamage, ( iCritOnHardHit != 0 ) );
  10601. }
  10602. #ifdef STAGING_ONLY
  10603. // Spy Mark removal on others when killed
  10604. // Only check if I have the spy marking gun
  10605. // STAGING_SPY
  10606. int iTranq = 0;
  10607. CALL_ATTRIB_HOOK_INT( iTranq, override_projectile_type );
  10608. if ( iTranq == TF_PROJECTILE_TRANQ )
  10609. {
  10610. CUtlVector< CTFPlayer * > playerVector;
  10611. CollectPlayers( &playerVector, GetTeamNumber() == TF_TEAM_BLUE ? TF_TEAM_RED : TF_TEAM_BLUE, true );
  10612. FOR_EACH_VEC ( playerVector, i )
  10613. {
  10614. if ( playerVector[i]->m_Shared.GetConditionProvider( TF_COND_TRANQ_MARKED ) == this )
  10615. {
  10616. playerVector[i]->m_Shared.RemoveCond( TF_COND_TRANQ_MARKED );
  10617. }
  10618. }
  10619. }
  10620. // If I was a spy cloned, give me instant respawn
  10621. if ( m_Shared.InCond( TF_COND_SPY_CLASS_STEAL ) )
  10622. {
  10623. m_flRespawnTimeOverride = 2.0f;
  10624. }
  10625. #endif
  10626. // Remove all conditions...
  10627. m_Shared.RemoveAllCond();
  10628. // Don't overflow the value for this.
  10629. m_iHealth = 0;
  10630. // If we died in sudden death and we're an engineer, explode our buildings
  10631. if ( IsPlayerClass( TF_CLASS_ENGINEER ) && TFGameRules()->InStalemate() && TFGameRules()->IsInArenaMode() == false )
  10632. {
  10633. for (int i = GetObjectCount()-1; i >= 0; i--)
  10634. {
  10635. CBaseObject *obj = GetObject(i);
  10636. Assert( obj );
  10637. if ( obj )
  10638. {
  10639. obj->DetonateObject();
  10640. }
  10641. }
  10642. }
  10643. // Achievement checks
  10644. if ( pPlayerAttacker )
  10645. {
  10646. // ACHIEVEMENT_TF_MEDIC_KILL_HEALED_SPY - medic kills a spy he has been healing
  10647. if ( IsPlayerClass( TF_CLASS_SPY ) && pPlayerAttacker->IsPlayerClass( TF_CLASS_MEDIC ) )
  10648. {
  10649. // if we were killed by a medic, see if he healed us most recently
  10650. for ( int i=0;i<pPlayerAttacker->WeaponCount();i++ )
  10651. {
  10652. CTFWeaponBase *pWpn = ( CTFWeaponBase *)pPlayerAttacker->GetWeapon( i );
  10653. if ( pWpn == NULL )
  10654. continue;
  10655. if ( pWpn->GetWeaponID() == TF_WEAPON_MEDIGUN )
  10656. {
  10657. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun * >( pWpn );
  10658. if ( pMedigun )
  10659. {
  10660. if ( pMedigun->GetMostRecentHealTarget() == this )
  10661. {
  10662. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_MEDIC_KILL_HEALED_SPY );
  10663. }
  10664. }
  10665. }
  10666. }
  10667. }
  10668. if ( bBurning && pPlayerAttacker->IsPlayerClass( TF_CLASS_PYRO ) )
  10669. {
  10670. // ACHIEVEMENT_TF_PYRO_KILL_MULTIWEAPONS - Pyro kills previously ignited target with other weapon
  10671. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>(info.GetWeapon());
  10672. if ( ( pOriginalBurner == pPlayerAttacker || pLastBurner == pPlayerAttacker ) && pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_SHOTGUN_PYRO )
  10673. {
  10674. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_PYRO_KILL_MULTIWEAPONS );
  10675. }
  10676. // ACHIEVEMENT_TF_PYRO_KILL_TEAMWORK - Pyro kills an enemy previously ignited by another Pyro
  10677. if ( pOriginalBurner != pPlayerAttacker )
  10678. {
  10679. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_PYRO_KILL_TEAMWORK );
  10680. }
  10681. }
  10682. }
  10683. if ( TFGameRules()->IsMannVsMachineMode() )
  10684. {
  10685. // Have teammates announce my death
  10686. if ( GetTeamNumber() == TF_TEAM_PVE_DEFENDERS )
  10687. {
  10688. // have the last player on the defenders speak the last_man_standing line
  10689. CUtlVector< CTFPlayer * > playerVector;
  10690. CollectPlayers( &playerVector, TF_TEAM_PVE_DEFENDERS, true );
  10691. if ( playerVector.Count() == 1 )
  10692. {
  10693. CTFPlayer *pAlivePlayer = playerVector[0];
  10694. if ( pAlivePlayer )
  10695. {
  10696. pAlivePlayer->SpeakConceptIfAllowed( MP_CONCEPT_MVM_LAST_MAN_STANDING );
  10697. }
  10698. }
  10699. else
  10700. {
  10701. if ( pPlayerAttacker && pPlayerAttacker->IsMiniBoss() )
  10702. {
  10703. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_KILLED_TEAMMATE, TF_TEAM_PVE_DEFENDERS );
  10704. }
  10705. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_DEFENDER_DIED, TF_TEAM_PVE_DEFENDERS, CFmtStr( "victimclass:%s", g_aPlayerClassNames_NonLocalized[ GetPlayerClass()->GetClassIndex() ] ).Access() );
  10706. }
  10707. }
  10708. else
  10709. {
  10710. if ( IsMiniBoss() )
  10711. {
  10712. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_KILLED, TF_TEAM_PVE_DEFENDERS );
  10713. }
  10714. }
  10715. }
  10716. // Reset Streaks to zero
  10717. m_Shared.ResetStreaks();
  10718. for ( int i = 0; i < WeaponCount(); i++)
  10719. {
  10720. CTFWeaponBase *pWpn = ( CTFWeaponBase *)GetWeapon(i);
  10721. if ( !pWpn )
  10722. continue;
  10723. pWpn->SetKillStreak( 0 );
  10724. }
  10725. for ( int i = 0; i < GetNumWearables(); ++i )
  10726. {
  10727. CTFWearable* pWearable = dynamic_cast<CTFWearable*>( GetWearable(i) );
  10728. if ( !pWearable )
  10729. continue;
  10730. pWearable->SetKillStreak( 0 );
  10731. }
  10732. // Is the player inside a respawn time override volume?
  10733. // don't do this for MvM bots
  10734. if ( !TFGameRules()->IsMannVsMachineMode() || !IsBot() )
  10735. {
  10736. FOR_EACH_VEC( ITriggerPlayerRespawnOverride::AutoList(), i )
  10737. {
  10738. CTriggerPlayerRespawnOverride *pTriggerRespawn = static_cast< CTriggerPlayerRespawnOverride* >( ITriggerPlayerRespawnOverride::AutoList()[i] );
  10739. if ( !pTriggerRespawn->m_bDisabled && pTriggerRespawn->IsTouching( this ) )
  10740. {
  10741. SetRespawnOverride( pTriggerRespawn->GetRespawnTime(), pTriggerRespawn->GetRespawnName() );
  10742. break;
  10743. }
  10744. else
  10745. {
  10746. SetRespawnOverride( -1.f, NULL_STRING );
  10747. }
  10748. }
  10749. }
  10750. // Is this an environmental death?
  10751. if ( ( info.GetAttacker() == info.GetInflictor() && info.GetAttacker() && info.GetAttacker()->IsBSPModel() ) ||
  10752. ( info.GetDamageCustom() == TF_DMG_CUSTOM_TRIGGER_HURT ) ||
  10753. ( info.GetDamageType() & DMG_VEHICLE ) )
  10754. {
  10755. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 1, 5.0 );
  10756. if ( pRecentDamager )
  10757. {
  10758. IGameEvent *event = gameeventmanager->CreateEvent( "environmental_death" );
  10759. if ( event )
  10760. {
  10761. event->SetInt( "killer", pRecentDamager->entindex() );
  10762. event->SetInt( "victim", entindex() );
  10763. event->SetInt( "priority", 9 );
  10764. gameeventmanager->FireEvent( event );
  10765. }
  10766. }
  10767. }
  10768. // make sure to remove custom attributes
  10769. RemoveAllCustomAttributes();
  10770. }
  10771. struct SkillRatingAttackRecord_t
  10772. {
  10773. CHandle< CTFPlayer > hAttacker;
  10774. float flDamagePercent;
  10775. };
  10776. //-----------------------------------------------------------------------------
  10777. // Purpose:
  10778. // Input : *pWeapon -
  10779. // &vecOrigin -
  10780. // &vecAngles -
  10781. //-----------------------------------------------------------------------------
  10782. bool CTFPlayer::CalculateAmmoPackPositionAndAngles( CTFWeaponBase *pWeapon, Vector &vecOrigin, QAngle &vecAngles )
  10783. {
  10784. // Look up the hand and weapon bones.
  10785. int iHandBone = LookupBone( "weapon_bone" );
  10786. if ( iHandBone == -1 )
  10787. return false;
  10788. GetBonePosition( iHandBone, vecOrigin, vecAngles );
  10789. // need to fix up the z because the weapon bone position can be under the player
  10790. if ( IsTaunting() )
  10791. {
  10792. // put the pack at the middle of the dying player
  10793. vecOrigin = WorldSpaceCenter();
  10794. }
  10795. // Draw the position and angles.
  10796. Vector vecDebugForward2, vecDebugRight2, vecDebugUp2;
  10797. AngleVectors( vecAngles, &vecDebugForward2, &vecDebugRight2, &vecDebugUp2 );
  10798. /*
  10799. NDebugOverlay::Line( vecOrigin, ( vecOrigin + vecDebugForward2 * 25.0f ), 255, 0, 0, false, 30.0f );
  10800. NDebugOverlay::Line( vecOrigin, ( vecOrigin + vecDebugRight2 * 25.0f ), 0, 255, 0, false, 30.0f );
  10801. NDebugOverlay::Line( vecOrigin, ( vecOrigin + vecDebugUp2 * 25.0f ), 0, 0, 255, false, 30.0f );
  10802. */
  10803. VectorAngles( vecDebugUp2, vecAngles );
  10804. return true;
  10805. }
  10806. //-----------------------------------------------------------------------------
  10807. // Purpose:
  10808. // NOTE: If we don't let players drop ammo boxes, we don't need this code..
  10809. //-----------------------------------------------------------------------------
  10810. void CTFPlayer::AmmoPackCleanUp( void )
  10811. {
  10812. // If we have more than 3 ammo packs out now, destroy the oldest one.
  10813. int iNumPacks = 0;
  10814. CTFAmmoPack *pOldestBox = NULL;
  10815. // Cycle through all ammobox in the world and remove them
  10816. CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "tf_ammo_pack" );
  10817. while ( pEnt )
  10818. {
  10819. CBaseEntity *pOwner = pEnt->GetOwnerEntity();
  10820. if (pOwner == this)
  10821. {
  10822. CTFAmmoPack *pThisBox = dynamic_cast<CTFAmmoPack *>( pEnt );
  10823. Assert( pThisBox );
  10824. if ( pThisBox )
  10825. {
  10826. iNumPacks++;
  10827. // Find the oldest one
  10828. if ( pOldestBox == NULL || pOldestBox->GetCreationTime() > pThisBox->GetCreationTime() )
  10829. {
  10830. pOldestBox = pThisBox;
  10831. }
  10832. }
  10833. }
  10834. pEnt = gEntList.FindEntityByClassname( pEnt, "tf_ammo_pack" );
  10835. }
  10836. // If they have more than 3 packs active, remove the oldest one
  10837. if ( iNumPacks > 3 && pOldestBox )
  10838. {
  10839. UTIL_Remove( pOldestBox );
  10840. }
  10841. }
  10842. //-----------------------------------------------------------------------------
  10843. // Purpose:
  10844. //-----------------------------------------------------------------------------
  10845. bool CTFPlayer::ShouldDropAmmoPack()
  10846. {
  10847. if ( TFGameRules()->IsMannVsMachineMode() && IsBot() )
  10848. return false;
  10849. if ( TFGameRules()->IsInArenaMode() && TFGameRules()->InStalemate() == false )
  10850. return false;
  10851. return true;
  10852. }
  10853. //-----------------------------------------------------------------------------
  10854. // Purpose:
  10855. //-----------------------------------------------------------------------------
  10856. void CTFPlayer::DropAmmoPack( const CTakeDamageInfo &info, bool bEmpty, bool bDisguisedWeapon )
  10857. {
  10858. // We want the ammo packs to look like the player's weapon model they were carrying.
  10859. // except if they are melee or building weapons
  10860. CTFWeaponBase *pWeapon = NULL;
  10861. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  10862. if ( !pActiveWeapon || pActiveWeapon->GetTFWpnData().m_bDontDrop )
  10863. {
  10864. // Don't drop this one, find another one to drop
  10865. int iWeight = -1;
  10866. // find the highest weighted weapon
  10867. for (int i = 0;i < WeaponCount(); i++)
  10868. {
  10869. CTFWeaponBase *pWpn = ( CTFWeaponBase *)GetWeapon(i);
  10870. if ( !pWpn )
  10871. continue;
  10872. if ( pWpn->GetTFWpnData().m_bDontDrop )
  10873. continue;
  10874. int iThisWeight = pWpn->GetTFWpnData().iWeight;
  10875. if ( iThisWeight > iWeight )
  10876. {
  10877. iWeight = iThisWeight;
  10878. pWeapon = pWpn;
  10879. }
  10880. }
  10881. }
  10882. else
  10883. {
  10884. pWeapon = pActiveWeapon;
  10885. }
  10886. // If we didn't find one, bail
  10887. if ( !pWeapon )
  10888. return;
  10889. // Figure out which model/skin to use for the drop. We may pull from our real weapon or
  10890. // from the weapon we're disguised as.
  10891. CTFWeaponBase *pDropWeaponProps = (bDisguisedWeapon && m_Shared.InCond( TF_COND_DISGUISED ) && m_Shared.GetDisguiseWeapon())
  10892. ? m_Shared.GetDisguiseWeapon()
  10893. : pWeapon;
  10894. const char *pszWorldModel = pDropWeaponProps->GetWorldModel();
  10895. int nSkin = pDropWeaponProps->GetDropSkinOverride();
  10896. if ( nSkin < 0 )
  10897. {
  10898. nSkin = pDropWeaponProps->GetSkin();
  10899. }
  10900. if ( pszWorldModel == NULL )
  10901. return;
  10902. // Find the position and angle of the weapons so the "ammo box" matches.
  10903. Vector vecPackOrigin;
  10904. QAngle vecPackAngles;
  10905. if( !CalculateAmmoPackPositionAndAngles( pWeapon, vecPackOrigin, vecPackAngles ) )
  10906. return;
  10907. CEconItemView *pItem = pDropWeaponProps->GetAttributeContainer()->GetItem();
  10908. bool bIsSuicide = info.GetAttacker() ? info.GetAttacker()->GetTeamNumber() == GetTeamNumber() : false;
  10909. CTFDroppedWeapon *pDroppedWeapon = CTFDroppedWeapon::Create( this, vecPackOrigin, vecPackAngles, pszWorldModel, pItem );
  10910. if ( pDroppedWeapon )
  10911. {
  10912. pDroppedWeapon->InitDroppedWeapon( this, pDropWeaponProps, false, bIsSuicide );
  10913. }
  10914. // Create the ammo pack.
  10915. CTFAmmoPack *pAmmoPack = CTFAmmoPack::Create( vecPackOrigin, vecPackAngles, this, "models/items/ammopack_medium.mdl" );
  10916. Assert( pAmmoPack );
  10917. if ( pAmmoPack )
  10918. {
  10919. pAmmoPack->InitWeaponDrop( this, pWeapon, nSkin, bEmpty, bIsSuicide );
  10920. // Clean up old ammo packs if they exist in the world
  10921. AmmoPackCleanUp();
  10922. }
  10923. }
  10924. //-----------------------------------------------------------------------------
  10925. // Purpose:
  10926. //-----------------------------------------------------------------------------
  10927. void CTFPlayer::DropHealthPack( const CTakeDamageInfo &info, bool bEmpty )
  10928. {
  10929. Vector vecSrc = this->WorldSpaceCenter();
  10930. CHealthKitSmall *pMedKit = assert_cast<CHealthKitSmall*>( CBaseEntity::Create( "item_healthkit_small", vecSrc, vec3_angle, this ) );
  10931. if ( pMedKit )
  10932. {
  10933. Vector vecImpulse = RandomVector( -1,1 );
  10934. vecImpulse.z = 1;
  10935. VectorNormalize( vecImpulse );
  10936. Vector vecVelocity = vecImpulse * 250.0;
  10937. pMedKit->DropSingleInstance( vecVelocity, this, 0 );
  10938. }
  10939. }
  10940. //-----------------------------------------------------------------------------
  10941. // Purpose:
  10942. //-----------------------------------------------------------------------------
  10943. void CTFPlayer::DropCurrencyPack( CurrencyRewards_t nSize /* = TF_CURRENCY_PACK_SMALL */, int nAmount /*= 0*/, bool bForceDistribute /*= false*/, CBasePlayer* pMoneyMaker /*= NULL*/ )
  10944. {
  10945. // SMALL, MEDIUM, LARGE packs generate a default value on spawn
  10946. // Only pass in an amount when dropping TF_CURRENCY_PACK_CUSTOM
  10947. Vector vecSrc = this->WorldSpaceCenter();
  10948. CCurrencyPack *pCurrencyPack = NULL;
  10949. switch ( nSize )
  10950. {
  10951. case TF_CURRENCY_PACK_SMALL:
  10952. pCurrencyPack = assert_cast<CCurrencyPackSmall*>( CBaseEntity::Create( "item_currencypack_small", vecSrc, vec3_angle, this ) );
  10953. break;
  10954. case TF_CURRENCY_PACK_MEDIUM:
  10955. pCurrencyPack = assert_cast<CCurrencyPackMedium*>( CBaseEntity::Create( "item_currencypack_medium", vecSrc, vec3_angle, this ) );
  10956. break;
  10957. case TF_CURRENCY_PACK_LARGE:
  10958. pCurrencyPack = assert_cast<CCurrencyPack*>( CBaseEntity::Create( "item_currencypack_large", vecSrc, vec3_angle, this ) );
  10959. break;
  10960. case TF_CURRENCY_PACK_CUSTOM:
  10961. // Pop file may have said to not drop anything
  10962. Assert( nAmount > 0 );
  10963. if ( nAmount == 0 )
  10964. return;
  10965. // Create no spawn first so we can set the multiplier before it spawns & picks it model
  10966. pCurrencyPack = assert_cast<CCurrencyPack*>( CBaseEntity::CreateNoSpawn( "item_currencypack_custom", vecSrc, vec3_angle, this ) );
  10967. pCurrencyPack->SetAmount( nAmount );
  10968. break;
  10969. };
  10970. if ( pCurrencyPack )
  10971. {
  10972. Vector vecImpulse = RandomVector( -1,1 );
  10973. vecImpulse.z = 1;
  10974. VectorNormalize( vecImpulse );
  10975. Vector vecVelocity = vecImpulse * 250.0;
  10976. if ( pMoneyMaker || bForceDistribute )
  10977. {
  10978. pCurrencyPack->DistributedBy( pMoneyMaker );
  10979. }
  10980. DispatchSpawn( pCurrencyPack );
  10981. pCurrencyPack->DropSingleInstance( vecVelocity, this, 0, 0 );
  10982. }
  10983. }
  10984. //-----------------------------------------------------------------------------
  10985. // Purpose:
  10986. //-----------------------------------------------------------------------------
  10987. void CTFPlayer::PlayerDeathThink( void )
  10988. {
  10989. // We're doing this here to avoid getting stuck
  10990. // in a recursive loop if we do it in Event_Killed
  10991. if ( m_bPendingMerasmusPlayerBombExplode )
  10992. {
  10993. m_bPendingMerasmusPlayerBombExplode = false;
  10994. MerasmusPlayerBombExplode();
  10995. }
  10996. // don't need to think again...
  10997. }
  10998. //-----------------------------------------------------------------------------
  10999. // Purpose: Remove the tf items from the player then call into the base class
  11000. // removal of items.
  11001. //-----------------------------------------------------------------------------
  11002. void CTFPlayer::RemoveAllItems( bool removeSuit )
  11003. {
  11004. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() && m_Shared.HasPasstimeBall() )
  11005. {
  11006. g_pPasstimeLogic->EjectBall( this, this );
  11007. }
  11008. // If the player has a capture flag, drop it.
  11009. if ( HasItem() )
  11010. {
  11011. int nFlagTeamNumber = GetItem()->GetTeamNumber();
  11012. GetItem()->Drop( this, true );
  11013. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  11014. if ( event )
  11015. {
  11016. event->SetInt( "player", entindex() );
  11017. event->SetInt( "eventtype", TF_FLAGEVENT_DROPPED );
  11018. event->SetInt( "priority", 8 );
  11019. event->SetInt( "team", nFlagTeamNumber );
  11020. gameeventmanager->FireEvent( event );
  11021. }
  11022. }
  11023. m_Shared.Heal_Radius( false );
  11024. if ( m_hOffHandWeapon.Get() )
  11025. {
  11026. HolsterOffHandWeapon();
  11027. // hide the weapon model
  11028. // don't normally have to do this, unless we have a holster animation
  11029. CBaseViewModel *vm = GetViewModel( 1 );
  11030. if ( vm )
  11031. {
  11032. vm->SetWeaponModel( NULL, NULL );
  11033. }
  11034. m_hOffHandWeapon = NULL;
  11035. }
  11036. Weapon_SetLast( NULL );
  11037. UpdateClientData();
  11038. }
  11039. //-----------------------------------------------------------------------------
  11040. // Purpose:
  11041. //-----------------------------------------------------------------------------
  11042. void CTFPlayer::ClientHearVox( const char *pSentence )
  11043. {
  11044. //TFTODO: implement this.
  11045. }
  11046. //-----------------------------------------------------------------------------
  11047. // Purpose:
  11048. //-----------------------------------------------------------------------------
  11049. void CTFPlayer::UpdateModel( void )
  11050. {
  11051. SetModel( GetPlayerClass()->GetModelName() );
  11052. // Immediately reset our collision bounds - our collision bounds will be set to the model's bounds.
  11053. SetCollisionBounds( GetPlayerMins(), GetPlayerMaxs() );
  11054. m_PlayerAnimState->OnNewModel();
  11055. }
  11056. //-----------------------------------------------------------------------------
  11057. // Purpose:
  11058. // Input : iSkin -
  11059. //-----------------------------------------------------------------------------
  11060. void CTFPlayer::UpdateSkin( int iTeam )
  11061. {
  11062. // The player's skin is team - 2.
  11063. int iSkin = iTeam - 2;
  11064. // Check to see if the skin actually changed.
  11065. if ( iSkin != m_iLastSkin )
  11066. {
  11067. m_nSkin = iSkin;
  11068. m_iLastSkin = iSkin;
  11069. }
  11070. }
  11071. //=========================================================================
  11072. // Displays the state of the items specified by the Goal passed in
  11073. void CTFPlayer::DisplayLocalItemStatus( CTFGoal *pGoal )
  11074. {
  11075. #if 0
  11076. for (int i = 0; i < 4; i++)
  11077. {
  11078. if (pGoal->display_item_status[i] != 0)
  11079. {
  11080. CTFGoalItem *pItem = Finditem(pGoal->display_item_status[i]);
  11081. if (pItem)
  11082. DisplayItemStatus(pGoal, this, pItem);
  11083. else
  11084. ClientPrint( this, HUD_PRINTTALK, "#Item_missing" );
  11085. }
  11086. }
  11087. #endif
  11088. }
  11089. void CTFPlayer::SetIsCoaching( bool bIsCoaching )
  11090. {
  11091. m_bIsCoaching = bIsCoaching;
  11092. if ( !bIsCoaching )
  11093. {
  11094. // reset our last action time so we don't get kicked for being idle while we were coaching
  11095. m_flLastAction = gpGlobals->curtime;
  11096. }
  11097. }
  11098. //=========================================================================
  11099. // Called when the player disconnects from the server.
  11100. void CTFPlayer::TeamFortress_ClientDisconnected( void )
  11101. {
  11102. RemoveAllOwnedEntitiesFromWorld( true );
  11103. RemoveNemesisRelationships();
  11104. StopTaunt();
  11105. RemoveAllWeapons();
  11106. RemoveAllItems( true );
  11107. TFGameRules()->RemovePlayerFromQueue( this );
  11108. TFGameRules()->PlayerHistory_AddPlayer( this );
  11109. DuelMiniGame_NotifyPlayerDisconnect( this );
  11110. // cleanup coaching
  11111. if ( GetCoach() )
  11112. {
  11113. GetCoach()->SetIsCoaching( false );
  11114. GetCoach()->SetStudent( NULL );
  11115. }
  11116. else if ( GetStudent() )
  11117. {
  11118. SetIsCoaching( false );
  11119. GetStudent()->SetCoach( NULL );
  11120. }
  11121. // Drop your powerup when you disconnect
  11122. if ( m_Shared.IsCarryingRune() )
  11123. {
  11124. CTFRune::CreateRune( GetAbsOrigin(), m_Shared.GetCarryingRuneType(), TEAM_ANY, true, false );
  11125. }
  11126. }
  11127. //=========================================================================
  11128. // Removes everything this player has (buildings, grenades, etc.) from the world
  11129. void CTFPlayer::RemoveAllOwnedEntitiesFromWorld( bool bExplodeBuildings /* = false */ )
  11130. {
  11131. RemoveOwnedProjectiles();
  11132. if ( TFGameRules()->IsMannVsMachineMode() && ( GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  11133. {
  11134. // MvM engineer bots leave their sentries behind when they die
  11135. return;
  11136. }
  11137. #ifdef TF_RAID_MODE
  11138. if ( TFGameRules()->IsRaidMode() && ( GetTeamNumber() == TF_TEAM_RED ) )
  11139. {
  11140. // for now, leave Engineer's sentrygun alive after he dies
  11141. return;
  11142. }
  11143. #endif // TF_RAID_MODE
  11144. if ( IsBotOfType( TF_BOT_TYPE ) && ToTFBot( this )->HasAttribute( CTFBot::RETAIN_BUILDINGS ) )
  11145. {
  11146. // keep this bot's buildings
  11147. return;
  11148. }
  11149. // Destroy any buildables - this should replace TeamFortress_RemoveBuildings
  11150. RemoveAllObjects( bExplodeBuildings );
  11151. }
  11152. //=========================================================================
  11153. // Removes all rockets the player has fired into the world
  11154. // (this prevents a team kill cheat where players would fire rockets
  11155. // then change teams to kill their own team)
  11156. void CTFPlayer::RemoveOwnedProjectiles( void )
  11157. {
  11158. FOR_EACH_VEC( IBaseProjectileAutoList::AutoList(), i )
  11159. {
  11160. CBaseProjectile *pProjectile = static_cast< CBaseProjectile* >( IBaseProjectileAutoList::AutoList()[i] );
  11161. // if the player owns this entity, remove it
  11162. bool bOwner = ( pProjectile->GetOwnerEntity() == this );
  11163. if ( !bOwner )
  11164. {
  11165. if ( pProjectile->GetBaseProjectileType() == TF_BASE_PROJECTILE_GRENADE )
  11166. {
  11167. CTFWeaponBaseGrenadeProj *pGrenade = assert_cast<CTFWeaponBaseGrenadeProj*>( pProjectile );
  11168. if ( pGrenade )
  11169. {
  11170. bOwner = ( pGrenade->GetThrower() == this );
  11171. }
  11172. }
  11173. else if ( pProjectile->GetProjectileType() == TF_PROJECTILE_SENTRY_ROCKET )
  11174. {
  11175. CTFProjectile_SentryRocket *pRocket = assert_cast<CTFProjectile_SentryRocket*>( pProjectile );
  11176. if ( pRocket )
  11177. {
  11178. bOwner = ( pRocket->GetScorer() == this );
  11179. }
  11180. }
  11181. }
  11182. if ( bOwner )
  11183. {
  11184. pProjectile->SetTouch( NULL );
  11185. pProjectile->AddEffects( EF_NODRAW );
  11186. UTIL_Remove( pProjectile );
  11187. }
  11188. }
  11189. FOR_EACH_VEC( ITFFlameEntityAutoList::AutoList(), i )
  11190. {
  11191. CTFFlameEntity *pFlameEnt = static_cast< CTFFlameEntity* >( ITFFlameEntityAutoList::AutoList()[i] );
  11192. if ( pFlameEnt->IsEntityAttacker( this ) )
  11193. {
  11194. pFlameEnt->SetTouch( NULL );
  11195. pFlameEnt->AddEffects( EF_NODRAW );
  11196. UTIL_Remove( pFlameEnt );
  11197. }
  11198. }
  11199. }
  11200. //-----------------------------------------------------------------------------
  11201. // Purpose:
  11202. //-----------------------------------------------------------------------------
  11203. void CTFPlayer::NoteWeaponFired()
  11204. {
  11205. Assert( m_pCurrentCommand );
  11206. if ( m_pCurrentCommand )
  11207. {
  11208. m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
  11209. }
  11210. // Remember the tickcount when the weapon was fired and lock viewangles here!
  11211. if ( m_iLockViewanglesTickNumber != gpGlobals->tickcount )
  11212. {
  11213. m_iLockViewanglesTickNumber = gpGlobals->tickcount;
  11214. m_qangLockViewangles = pl.v_angle;
  11215. }
  11216. }
  11217. //=============================================================================
  11218. //
  11219. // Player state functions.
  11220. //
  11221. //-----------------------------------------------------------------------------
  11222. // Purpose:
  11223. //-----------------------------------------------------------------------------
  11224. CPlayerStateInfo *CTFPlayer::StateLookupInfo( int nState )
  11225. {
  11226. // This table MUST match the
  11227. static CPlayerStateInfo playerStateInfos[] =
  11228. {
  11229. { TF_STATE_ACTIVE, "TF_STATE_ACTIVE", &CTFPlayer::StateEnterACTIVE, NULL, NULL },
  11230. { TF_STATE_WELCOME, "TF_STATE_WELCOME", &CTFPlayer::StateEnterWELCOME, NULL, &CTFPlayer::StateThinkWELCOME },
  11231. { TF_STATE_OBSERVER, "TF_STATE_OBSERVER", &CTFPlayer::StateEnterOBSERVER, NULL, &CTFPlayer::StateThinkOBSERVER },
  11232. { TF_STATE_DYING, "TF_STATE_DYING", &CTFPlayer::StateEnterDYING, NULL, &CTFPlayer::StateThinkDYING },
  11233. };
  11234. for ( int iState = 0; iState < ARRAYSIZE( playerStateInfos ); ++iState )
  11235. {
  11236. if ( playerStateInfos[iState].m_nPlayerState == nState )
  11237. return &playerStateInfos[iState];
  11238. }
  11239. return NULL;
  11240. }
  11241. //-----------------------------------------------------------------------------
  11242. // Purpose:
  11243. //-----------------------------------------------------------------------------
  11244. void CTFPlayer::StateEnter( int nState )
  11245. {
  11246. m_Shared.m_nPlayerState = nState;
  11247. m_pStateInfo = StateLookupInfo( nState );
  11248. if ( tf_playerstatetransitions.GetInt() == -1 || tf_playerstatetransitions.GetInt() == entindex() )
  11249. {
  11250. if ( m_pStateInfo )
  11251. Msg( "ShowStateTransitions: entering '%s'\n", m_pStateInfo->m_pStateName );
  11252. else
  11253. Msg( "ShowStateTransitions: entering #%d\n", nState );
  11254. }
  11255. // Initialize the new state.
  11256. if ( m_pStateInfo && m_pStateInfo->pfnEnterState )
  11257. {
  11258. (this->*m_pStateInfo->pfnEnterState)();
  11259. }
  11260. }
  11261. //-----------------------------------------------------------------------------
  11262. // Purpose:
  11263. //-----------------------------------------------------------------------------
  11264. void CTFPlayer::StateLeave( void )
  11265. {
  11266. if ( m_pStateInfo && m_pStateInfo->pfnLeaveState )
  11267. {
  11268. (this->*m_pStateInfo->pfnLeaveState)();
  11269. }
  11270. }
  11271. //-----------------------------------------------------------------------------
  11272. // Purpose:
  11273. //-----------------------------------------------------------------------------
  11274. void CTFPlayer::StateTransition( int nState )
  11275. {
  11276. StateLeave();
  11277. StateEnter( nState );
  11278. }
  11279. //-----------------------------------------------------------------------------
  11280. // Purpose:
  11281. //-----------------------------------------------------------------------------
  11282. void CTFPlayer::StateEnterWELCOME( void )
  11283. {
  11284. PickWelcomeObserverPoint();
  11285. StartObserverMode( OBS_MODE_FIXED );
  11286. // Important to set MOVETYPE_NONE or our physics object will fall while we're sitting at one of the intro cameras.
  11287. SetMoveType( MOVETYPE_NONE );
  11288. AddSolidFlags( FSOLID_NOT_SOLID );
  11289. AddEffects( EF_NODRAW | EF_NOSHADOW );
  11290. PhysObjectSleep();
  11291. if ( g_pServerBenchmark->IsLocalBenchmarkPlayer( this ) )
  11292. {
  11293. m_bSeenRoundInfo = true;
  11294. ChangeTeam( TEAM_SPECTATOR );
  11295. }
  11296. else if ( gpGlobals->eLoadType == MapLoad_Background )
  11297. {
  11298. m_bSeenRoundInfo = true;
  11299. ChangeTeam( TEAM_SPECTATOR );
  11300. }
  11301. else if ( (TFGameRules() && TFGameRules()->IsLoadingBugBaitReport()) )
  11302. {
  11303. m_bSeenRoundInfo = true;
  11304. ChangeTeam( TF_TEAM_BLUE );
  11305. SetDesiredPlayerClassIndex( TF_CLASS_SCOUT );
  11306. ForceRespawn();
  11307. }
  11308. else if ( IsInCommentaryMode() )
  11309. {
  11310. m_bSeenRoundInfo = true;
  11311. }
  11312. //=============================================================================
  11313. // HPE_BEGIN:
  11314. // [msmith] When in training, we want the option to show an intro movie.
  11315. //=============================================================================
  11316. else if ( TFGameRules()->IsInTraining() && IsFakeClient() == false )
  11317. {
  11318. ShowViewPortPanel( PANEL_INTRO, true );
  11319. m_bSeenRoundInfo = true;
  11320. }
  11321. //=============================================================================
  11322. // HPE_END
  11323. //=============================================================================
  11324. #ifdef STAGING_ONLY
  11325. else if ( tf_skip_intro_and_spectate.GetBool() )
  11326. {
  11327. m_bSeenRoundInfo = true;
  11328. ChangeTeam( TEAM_SPECTATOR );
  11329. SetObserverMode( OBS_MODE_CHASE );
  11330. }
  11331. #endif
  11332. else
  11333. {
  11334. if ( !IsX360() )
  11335. {
  11336. char pszWelcome[128];
  11337. Q_snprintf( pszWelcome, sizeof(pszWelcome), "#TF_Welcome" );
  11338. if ( UTIL_GetActiveHolidayString() )
  11339. {
  11340. Q_snprintf( pszWelcome, sizeof(pszWelcome), "#TF_Welcome_%s", UTIL_GetActiveHolidayString() );
  11341. }
  11342. KeyValues *data = new KeyValues( "data" );
  11343. data->SetString( "title", pszWelcome ); // info panel title
  11344. data->SetString( "type", "1" ); // show userdata from stringtable entry
  11345. data->SetString( "msg", "motd" ); // use this stringtable entry
  11346. data->SetString( "msg_fallback", "motd_text" ); // use this stringtable entry if the base is HTML, and client has disabled HTML motds
  11347. data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );
  11348. ShowViewPortPanel( PANEL_INFO, true, data );
  11349. data->deleteThis();
  11350. }
  11351. else
  11352. {
  11353. ShowViewPortPanel( PANEL_MAPINFO, true );
  11354. }
  11355. m_bSeenRoundInfo = false;
  11356. }
  11357. #ifdef STAGING_ONLY
  11358. if ( TFGameRules() && TFGameRules()->IsBountyMode() )
  11359. {
  11360. // See if we should give starting money
  11361. int nCurrency = tf_bountymode_currency_starting.GetInt();
  11362. if ( nCurrency > 0 )
  11363. {
  11364. SetCurrency( nCurrency );
  11365. }
  11366. }
  11367. #endif // STAGING_ONLY
  11368. }
  11369. //-----------------------------------------------------------------------------
  11370. // Purpose:
  11371. //-----------------------------------------------------------------------------
  11372. void CTFPlayer::StateThinkWELCOME( void )
  11373. {
  11374. if ( !IsFakeClient() )
  11375. {
  11376. if ( IsInCommentaryMode() )
  11377. {
  11378. ChangeTeam( TF_TEAM_BLUE );
  11379. SetDesiredPlayerClassIndex( TF_CLASS_SCOUT );
  11380. ForceRespawn();
  11381. }
  11382. else if ( TFGameRules()->IsInTraining() )
  11383. {
  11384. int iTeam = TFGameRules()->GetAssignedHumanTeam();
  11385. int iClass = TFGameRules()->GetTrainingModeLogic() ? TFGameRules()->GetTrainingModeLogic()->GetDesiredClass() : TF_CLASS_SOLDIER;
  11386. ChangeTeam( iTeam != TEAM_ANY ? iTeam : TF_TEAM_BLUE );
  11387. SetDesiredPlayerClassIndex( iClass );
  11388. ForceRespawn();
  11389. }
  11390. }
  11391. }
  11392. //-----------------------------------------------------------------------------
  11393. // Purpose:
  11394. //-----------------------------------------------------------------------------
  11395. void CTFPlayer::StateEnterACTIVE()
  11396. {
  11397. SetMoveType( MOVETYPE_WALK );
  11398. RemoveEffects( EF_NODRAW | EF_NOSHADOW );
  11399. RemoveSolidFlags( FSOLID_NOT_SOLID );
  11400. m_Local.m_iHideHUD = 0;
  11401. PhysObjectWake();
  11402. m_flLastAction = gpGlobals->curtime;
  11403. m_flLastHealthRegenAt = gpGlobals->curtime;
  11404. SetContextThink( &CTFPlayer::RegenThink, gpGlobals->curtime + TF_REGEN_TIME, "RegenThink" );
  11405. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  11406. {
  11407. SetContextThink( &CTFPlayer::RuneRegenThink, gpGlobals->curtime + TF_REGEN_TIME_RUNE, "RuneRegenThink" );
  11408. }
  11409. }
  11410. //-----------------------------------------------------------------------------
  11411. // Purpose:
  11412. //-----------------------------------------------------------------------------
  11413. bool CTFPlayer::SetObserverMode(int mode)
  11414. {
  11415. if ( !TFGameRules() )
  11416. return false;
  11417. if ( mode < OBS_MODE_NONE || mode >= NUM_OBSERVER_MODES )
  11418. return false;
  11419. if ( TFGameRules()->ShowMatchSummary() )
  11420. return false;
  11421. // Skip over OBS_MODE_POI if we're not in Passtime mode
  11422. if ( mode == OBS_MODE_POI )
  11423. {
  11424. if ( !TFGameRules()->IsPasstimeMode() )
  11425. {
  11426. mode = OBS_MODE_ROAMING;
  11427. }
  11428. }
  11429. // Skip over OBS_MODE_ROAMING for dead players
  11430. if( GetTeamNumber() > TEAM_SPECTATOR )
  11431. {
  11432. if ( IsDead() && ( mode > OBS_MODE_FIXED ) && mp_fadetoblack.GetBool() )
  11433. {
  11434. mode = OBS_MODE_CHASE;
  11435. }
  11436. else if ( mode == OBS_MODE_ROAMING )
  11437. {
  11438. mode = OBS_MODE_IN_EYE;
  11439. }
  11440. }
  11441. if ( m_iObserverMode > OBS_MODE_DEATHCAM )
  11442. {
  11443. // remember mode if we were really spectating before
  11444. m_iObserverLastMode = m_iObserverMode;
  11445. }
  11446. m_iObserverMode = mode;
  11447. if ( !m_bArenaIsAFK )
  11448. {
  11449. m_flLastAction = gpGlobals->curtime;
  11450. }
  11451. // this is the old behavior, still supported for community servers
  11452. bool bAllowSpecModeChange = TFGameRules()->IsInTournamentMode() ? TFGameRules()->IsMannVsMachineMode() : true;
  11453. // new behavior for Valve casual, competitive, and mvm matches
  11454. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  11455. if ( pMatchDesc )
  11456. {
  11457. bAllowSpecModeChange = pMatchDesc->m_params.m_bAllowSpecModeChange;
  11458. }
  11459. if ( !bAllowSpecModeChange )
  11460. {
  11461. if ( ( mode != OBS_MODE_DEATHCAM ) && ( mode != OBS_MODE_FREEZECAM ) && ( GetTeamNumber() > TEAM_SPECTATOR ) )
  11462. {
  11463. if ( IsValidObserverTarget( GetObserverTarget() ) )
  11464. {
  11465. m_iObserverMode.Set( OBS_MODE_IN_EYE );
  11466. }
  11467. else
  11468. {
  11469. m_iObserverMode.Set( OBS_MODE_DEATHCAM );
  11470. }
  11471. }
  11472. }
  11473. switch ( m_iObserverMode )
  11474. {
  11475. case OBS_MODE_NONE:
  11476. case OBS_MODE_FIXED :
  11477. case OBS_MODE_DEATHCAM :
  11478. SetFOV( this, 0 ); // Reset FOV
  11479. SetViewOffset( vec3_origin );
  11480. SetMoveType( MOVETYPE_NONE );
  11481. break;
  11482. case OBS_MODE_CHASE :
  11483. case OBS_MODE_IN_EYE :
  11484. // udpate FOV and viewmodels
  11485. SetObserverTarget( m_hObserverTarget );
  11486. SetMoveType( MOVETYPE_OBSERVER );
  11487. break;
  11488. case OBS_MODE_POI : // PASSTIME
  11489. SetObserverTarget( TFGameRules()->GetObjectiveObserverTarget() );
  11490. SetMoveType( MOVETYPE_OBSERVER );
  11491. break;
  11492. case OBS_MODE_ROAMING :
  11493. SetFOV( this, 0 ); // Reset FOV
  11494. SetObserverTarget( m_hObserverTarget );
  11495. SetViewOffset( vec3_origin );
  11496. SetMoveType( MOVETYPE_OBSERVER );
  11497. break;
  11498. case OBS_MODE_FREEZECAM:
  11499. SetFOV( this, 0 ); // Reset FOV
  11500. SetObserverTarget( m_hObserverTarget );
  11501. SetViewOffset( vec3_origin );
  11502. SetMoveType( MOVETYPE_OBSERVER );
  11503. break;
  11504. }
  11505. CheckObserverSettings();
  11506. return true;
  11507. }
  11508. //-----------------------------------------------------------------------------
  11509. // Purpose:
  11510. //-----------------------------------------------------------------------------
  11511. void CTFPlayer::StateEnterOBSERVER( void )
  11512. {
  11513. // Always start a spectator session in chase mode
  11514. m_iObserverLastMode = OBS_MODE_CHASE;
  11515. if( m_hObserverTarget == NULL )
  11516. {
  11517. // find a new observer target
  11518. CheckObserverSettings();
  11519. }
  11520. if ( !m_bAbortFreezeCam )
  11521. {
  11522. FindInitialObserverTarget();
  11523. }
  11524. // If we haven't yet set a valid observer mode, such as when
  11525. // the player aborts the freezecam and sets a mode "by hand"
  11526. // force the initial mode to last mode
  11527. if ( m_iObserverMode <= OBS_MODE_FREEZECAM )
  11528. {
  11529. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  11530. {
  11531. m_iObserverMode = OBS_MODE_POI;
  11532. }
  11533. else
  11534. {
  11535. m_iObserverMode = m_iObserverLastMode;
  11536. }
  11537. }
  11538. // If we're in fixed mode, but we found an observer target, move to non fixed.
  11539. if ( m_hObserverTarget.Get() != NULL && m_iObserverMode == OBS_MODE_FIXED )
  11540. {
  11541. m_iObserverMode.Set( OBS_MODE_IN_EYE );
  11542. }
  11543. StartObserverMode( m_iObserverMode );
  11544. PhysObjectSleep();
  11545. if ( GetTeamNumber() != TEAM_SPECTATOR )
  11546. {
  11547. HandleFadeToBlack();
  11548. }
  11549. }
  11550. //-----------------------------------------------------------------------------
  11551. // Purpose:
  11552. //-----------------------------------------------------------------------------
  11553. void CTFPlayer::StateThinkOBSERVER()
  11554. {
  11555. // Make sure nobody has changed any of our state.
  11556. Assert( m_takedamage == DAMAGE_NO );
  11557. Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
  11558. // Must be dead.
  11559. Assert( m_lifeState == LIFE_DEAD );
  11560. Assert( pl.deadflag );
  11561. #ifdef STAGING_ONLY
  11562. if ( tf_skip_intro_and_spectate.GetInt() > 5 )
  11563. {
  11564. static float s_flLastTime = gpGlobals->curtime;
  11565. float curtime = gpGlobals->curtime;
  11566. if ( ( curtime - s_flLastTime ) > tf_skip_intro_and_spectate.GetInt() )
  11567. {
  11568. s_flLastTime = curtime;
  11569. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  11570. {
  11571. CBasePlayer *pl = UTIL_PlayerByIndex( i );
  11572. if ( pl && ( pl->GetTeamNumber() == TEAM_SPECTATOR ) )
  11573. {
  11574. CBaseEntity * target = pl->FindNextObserverTarget( false );
  11575. if ( target )
  11576. {
  11577. // Could also switch spec_mode: GetObserverMode().
  11578. pl->SetObserverTarget( target );
  11579. }
  11580. }
  11581. }
  11582. }
  11583. }
  11584. #endif
  11585. }
  11586. //-----------------------------------------------------------------------------
  11587. // Purpose:
  11588. //-----------------------------------------------------------------------------
  11589. void CTFPlayer::StateEnterDYING( void )
  11590. {
  11591. SetMoveType( MOVETYPE_NONE );
  11592. AddSolidFlags( FSOLID_NOT_SOLID );
  11593. m_bPlayedFreezeCamSound = false;
  11594. m_bAbortFreezeCam = false;
  11595. if ( TFGameRules() && TFGameRules()->IsInArenaMode() )
  11596. {
  11597. float flLastActionTime = gpGlobals->curtime - m_flLastAction;
  11598. float flAliveThisRoundTime = gpGlobals->curtime - TFGameRules()->GetRoundStart();
  11599. if ( flAliveThisRoundTime - flLastActionTime < 0 )
  11600. {
  11601. m_bArenaIsAFK = true;
  11602. }
  11603. }
  11604. }
  11605. //-----------------------------------------------------------------------------
  11606. // Purpose: Move the player to observer mode once the dying process is over
  11607. //-----------------------------------------------------------------------------
  11608. void CTFPlayer::StateThinkDYING( void )
  11609. {
  11610. // If we have a ragdoll, it's time to go to deathcam
  11611. if ( !m_bAbortFreezeCam && m_hRagdoll &&
  11612. (m_lifeState == LIFE_DYING || m_lifeState == LIFE_DEAD) &&
  11613. GetObserverMode() != OBS_MODE_FREEZECAM )
  11614. {
  11615. if ( GetObserverMode() != OBS_MODE_DEATHCAM )
  11616. {
  11617. StartObserverMode( OBS_MODE_DEATHCAM ); // go to observer mode
  11618. }
  11619. RemoveEffects( EF_NODRAW | EF_NOSHADOW ); // still draw player body
  11620. }
  11621. float flTimeInFreeze = spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();
  11622. float flFreezeEnd = (m_flDeathTime + TF_DEATH_ANIMATION_TIME + flTimeInFreeze );
  11623. if ( !m_bPlayedFreezeCamSound && GetObserverTarget() && GetObserverTarget() != this )
  11624. {
  11625. // Start the sound so that it ends at the freezecam lock on time
  11626. float flFreezeSoundLength = 0.3;
  11627. float flFreezeSoundTime = (m_flDeathTime + TF_DEATH_ANIMATION_TIME ) + spec_freeze_traveltime.GetFloat() - flFreezeSoundLength;
  11628. if ( gpGlobals->curtime >= flFreezeSoundTime )
  11629. {
  11630. CSingleUserRecipientFilter filter( this );
  11631. EmitSound_t params;
  11632. params.m_flSoundTime = 0;
  11633. params.m_pSoundName = "TFPlayer.FreezeCam";
  11634. EmitSound( filter, entindex(), params );
  11635. m_bPlayedFreezeCamSound = true;
  11636. }
  11637. }
  11638. if ( gpGlobals->curtime >= (m_flDeathTime + TF_DEATH_ANIMATION_TIME ) ) // allow x seconds death animation / death cam
  11639. {
  11640. if ( GetObserverTarget() && GetObserverTarget() != this )
  11641. {
  11642. if ( !m_bAbortFreezeCam && gpGlobals->curtime < flFreezeEnd )
  11643. {
  11644. if ( GetObserverMode() != OBS_MODE_FREEZECAM )
  11645. {
  11646. StartObserverMode( OBS_MODE_FREEZECAM );
  11647. PhysObjectSleep();
  11648. }
  11649. return;
  11650. }
  11651. }
  11652. if ( GetObserverMode() == OBS_MODE_FREEZECAM )
  11653. {
  11654. // If we're in freezecam, and we want out, abort. (only if server is not using mp_fadetoblack)
  11655. if ( m_bAbortFreezeCam && !mp_fadetoblack.GetBool() )
  11656. {
  11657. if ( m_hObserverTarget == NULL )
  11658. {
  11659. // find a new observer target
  11660. CheckObserverSettings();
  11661. }
  11662. FindInitialObserverTarget();
  11663. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  11664. {
  11665. SetObserverMode( OBS_MODE_POI );
  11666. }
  11667. else
  11668. {
  11669. SetObserverMode( OBS_MODE_CHASE );
  11670. }
  11671. ShowViewPortPanel( "specgui" , ModeWantsSpectatorGUI(OBS_MODE_CHASE) );
  11672. }
  11673. }
  11674. // Don't allow anyone to respawn until freeze time is over, even if they're not
  11675. // in freezecam. This prevents players skipping freezecam to spawn faster.
  11676. if ( gpGlobals->curtime < flFreezeEnd )
  11677. return;
  11678. m_lifeState = LIFE_RESPAWNABLE;
  11679. StopAnimation();
  11680. IncrementInterpolationFrame();
  11681. if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
  11682. SetMoveType( MOVETYPE_NONE );
  11683. StateTransition( TF_STATE_OBSERVER );
  11684. }
  11685. }
  11686. //-----------------------------------------------------------------------------
  11687. // Purpose:
  11688. //-----------------------------------------------------------------------------
  11689. void CTFPlayer::AttemptToExitFreezeCam( void )
  11690. {
  11691. float flFreezeTravelTime = (m_flDeathTime + TF_DEATH_ANIMATION_TIME ) + spec_freeze_traveltime.GetFloat() + 0.5;
  11692. if ( gpGlobals->curtime < flFreezeTravelTime )
  11693. return;
  11694. m_bAbortFreezeCam = true;
  11695. }
  11696. class CIntroViewpoint : public CPointEntity
  11697. {
  11698. DECLARE_CLASS( CIntroViewpoint, CPointEntity );
  11699. public:
  11700. DECLARE_DATADESC();
  11701. virtual int UpdateTransmitState()
  11702. {
  11703. return SetTransmitState( FL_EDICT_ALWAYS );
  11704. }
  11705. int m_iIntroStep;
  11706. float m_flStepDelay;
  11707. string_t m_iszMessage;
  11708. string_t m_iszGameEvent;
  11709. float m_flEventDelay;
  11710. int m_iGameEventData;
  11711. float m_flFOV;
  11712. };
  11713. BEGIN_DATADESC( CIntroViewpoint )
  11714. DEFINE_KEYFIELD( m_iIntroStep, FIELD_INTEGER, "step_number" ),
  11715. DEFINE_KEYFIELD( m_flStepDelay, FIELD_FLOAT, "time_delay" ),
  11716. DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "hint_message" ),
  11717. DEFINE_KEYFIELD( m_iszGameEvent, FIELD_STRING, "event_to_fire" ),
  11718. DEFINE_KEYFIELD( m_flEventDelay, FIELD_FLOAT, "event_delay" ),
  11719. DEFINE_KEYFIELD( m_iGameEventData, FIELD_INTEGER, "event_data_int" ),
  11720. DEFINE_KEYFIELD( m_flFOV, FIELD_FLOAT, "fov" ),
  11721. END_DATADESC()
  11722. LINK_ENTITY_TO_CLASS( game_intro_viewpoint, CIntroViewpoint );
  11723. //-----------------------------------------------------------------------------
  11724. // Purpose: Give the player some ammo.
  11725. // Input : iCount - Amount of ammo to give.
  11726. // iAmmoIndex - Index of the ammo into the AmmoInfoArray
  11727. // iMax - Max carrying capability of the player
  11728. // Output : Amount of ammo actually given
  11729. //-----------------------------------------------------------------------------
  11730. int CTFPlayer::GiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound )
  11731. {
  11732. return GiveAmmo( iCount, iAmmoIndex, bSuppressSound, kAmmoSource_Pickup );
  11733. }
  11734. //-----------------------------------------------------------------------------
  11735. // Purpose: Give the player some ammo.
  11736. // Input : iCount - Amount of ammo to give.
  11737. // iAmmoIndex - Index of the ammo into the AmmoInfoArray
  11738. // iMax - Max carrying capability of the player
  11739. // Output : Amount of ammo actually given
  11740. //-----------------------------------------------------------------------------
  11741. int CTFPlayer::GiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound, EAmmoSource eAmmoSource )
  11742. {
  11743. if ( iCount <= 0 )
  11744. {
  11745. return 0;
  11746. }
  11747. // Metal always ignores the eAmmoSource settings, which are really used only for determining
  11748. // whether ammo should be converted into health or ignored or, in rare cases, treated as actual
  11749. // ammo.
  11750. if ( iAmmoIndex != TF_AMMO_METAL )
  11751. {
  11752. //int iAmmoBecomesHealth = 0;
  11753. //CALL_ATTRIB_HOOK_INT( iAmmoBecomesHealth, ammo_becomes_health );
  11754. //if ( iAmmoBecomesHealth == 1 )
  11755. //{
  11756. // // Ammo from ground pickups is converted to health.
  11757. // if ( eAmmoSource == kAmmoSource_Pickup )
  11758. // {
  11759. // int iTakenHealth = TakeHealth( iCount, DMG_GENERIC );
  11760. // if ( iTakenHealth > 0 )
  11761. // {
  11762. // if ( !bSuppressSound )
  11763. // {
  11764. // EmitSound( "BaseCombatCharacter.AmmoPickup" );
  11765. // }
  11766. // m_Shared.HealthKitPickupEffects( iCount );
  11767. // }
  11768. // return iTakenHealth;
  11769. // }
  11770. // // Ammo from the cart or engineer dispensers is flatly ignored.
  11771. // if ( eAmmoSource == kAmmoSource_DispenserOrCart )
  11772. // return 0;
  11773. // Assert( eAmmoSource == kAmmoSource_Resupply );
  11774. //}
  11775. }
  11776. else if ( iAmmoIndex == TF_AMMO_METAL )
  11777. {
  11778. if ( eAmmoSource != kAmmoSource_Resupply )
  11779. {
  11780. float flMultMetal = 1.0f;
  11781. CALL_ATTRIB_HOOK_FLOAT( flMultMetal, mult_metal_pickup );
  11782. iCount = (int)(flMultMetal * iCount );
  11783. }
  11784. }
  11785. if ( !g_pGameRules->CanHaveAmmo( this, iAmmoIndex ) )
  11786. {
  11787. // game rules say I can't have any more of this ammo type.
  11788. return 0;
  11789. }
  11790. if ( iAmmoIndex < 0 || iAmmoIndex >= MAX_AMMO_SLOTS )
  11791. {
  11792. return 0;
  11793. }
  11794. int iAdd = MIN( iCount, GetMaxAmmo(iAmmoIndex) - GetAmmoCount(iAmmoIndex) );
  11795. if ( iAdd < 1 )
  11796. {
  11797. return 0;
  11798. }
  11799. // Ammo pickup sound
  11800. if ( !bSuppressSound )
  11801. {
  11802. EmitSound( "BaseCombatCharacter.AmmoPickup" );
  11803. }
  11804. CBaseCombatCharacter::GiveAmmo( iAdd, iAmmoIndex, bSuppressSound );
  11805. return iAdd;
  11806. }
  11807. //-----------------------------------------------------------------------------
  11808. // Purpose:
  11809. //-----------------------------------------------------------------------------
  11810. void CTFPlayer::RemoveAmmo( int iCount, int iAmmoIndex )
  11811. {
  11812. #ifdef STAGING_ONLY
  11813. if ( tf_infinite_ammo.GetBool() )
  11814. {
  11815. return;
  11816. }
  11817. #endif // STAGING_ONLY
  11818. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  11819. if ( mp_developer.GetInt() > 1 && !IsBot() )
  11820. return;
  11821. #endif // _DEBUG || STAGING_ONLY
  11822. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GIANT ) )
  11823. {
  11824. return;
  11825. }
  11826. // Infinite primary, secondary and metal in these game modes
  11827. if ( TFGameRules() && iAmmoIndex < TF_AMMO_GRENADES1 )
  11828. {
  11829. if ( TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  11830. return;
  11831. #ifdef STAGING_ONLY
  11832. if ( TFGameRules()->IsBountyMode() && IsMiniBoss() )
  11833. return;
  11834. #endif // STAGING_ONLY
  11835. }
  11836. CBaseCombatCharacter::RemoveAmmo( iCount, iAmmoIndex );
  11837. }
  11838. //-----------------------------------------------------------------------------
  11839. // Purpose:
  11840. //-----------------------------------------------------------------------------
  11841. void CTFPlayer::RemoveAmmo( int iCount, const char *szName )
  11842. {
  11843. if ( TFGameRules() )
  11844. {
  11845. if ( TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  11846. return;
  11847. if ( TFGameRules()->GameModeUsesMiniBosses() && IsMiniBoss() )
  11848. return;
  11849. }
  11850. CBaseCombatCharacter::RemoveAmmo( iCount, szName );
  11851. }
  11852. //-----------------------------------------------------------------------------
  11853. // Purpose: Returns the amount of ammunition of a particular type owned
  11854. // owned by the character
  11855. // Input : Ammo Index
  11856. // Output : The amount of ammo
  11857. //-----------------------------------------------------------------------------
  11858. int CTFPlayer::GetAmmoCount( int iAmmoIndex ) const
  11859. {
  11860. if ( iAmmoIndex == -1 )
  11861. return 0;
  11862. if ( IsFakeClient() && TFGameRules()->IsInItemTestingMode() )
  11863. return 999;
  11864. return BaseClass::GetAmmoCount( iAmmoIndex );
  11865. }
  11866. //-----------------------------------------------------------------------------
  11867. // Purpose: Has to be const for override, but needs to access non-const member methods.
  11868. //-----------------------------------------------------------------------------
  11869. int CTFPlayer::GetMaxHealth() const
  11870. {
  11871. int iMax = GetMaxHealthForBuffing();
  11872. // Also add the nonbuffed health bonuses
  11873. CALL_ATTRIB_HOOK_INT( iMax, add_maxhealth_nonbuffed );
  11874. return MAX( iMax, 1 );
  11875. }
  11876. //-----------------------------------------------------------------------------
  11877. // Purpose:
  11878. //-----------------------------------------------------------------------------
  11879. int CTFPlayer::GetMaxHealthForBuffing() const
  11880. {
  11881. int iMax = m_PlayerClass.GetMaxHealth();
  11882. CALL_ATTRIB_HOOK_INT( iMax, add_maxhealth );
  11883. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  11884. if ( pWeapon )
  11885. {
  11886. iMax += pWeapon->GetMaxHealthMod();
  11887. }
  11888. if ( const_cast<CTFPlayer*>(this)->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN )
  11889. {
  11890. CTFSword *pSword = dynamic_cast<CTFSword*>(const_cast<CTFPlayer*>(this)->Weapon_OwnsThisID( TF_WEAPON_SWORD ));
  11891. if ( pSword )
  11892. {
  11893. iMax += pSword->GetSwordHealthMod();
  11894. }
  11895. }
  11896. // Some Powerup Runes increase your Max Health
  11897. iMax += GetRuneHealthBonus();
  11898. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GIANT ) )
  11899. {
  11900. return iMax * tf_halloween_giant_health_scale.GetFloat();
  11901. }
  11902. return iMax;
  11903. }
  11904. //-----------------------------------------------------------------------------
  11905. // Purpose:
  11906. //-----------------------------------------------------------------------------
  11907. int CTFPlayer::GetRuneHealthBonus() const
  11908. {
  11909. int nRuneType = m_Shared.GetCarryingRuneType();
  11910. if ( nRuneType == RUNE_NONE )
  11911. {
  11912. return 0;
  11913. }
  11914. if ( nRuneType == RUNE_KNOCKOUT )
  11915. {
  11916. if ( IsPlayerClass( TF_CLASS_DEMOMAN ) )
  11917. {
  11918. // Swords have various extra melee benefits, so we reduce Max Health bonus
  11919. if ( Weapon_GetSlot( TF_WPN_TYPE_MELEE ) )
  11920. {
  11921. int iDecapitateType = 0;
  11922. CALL_ATTRIB_HOOK_INT( iDecapitateType, decapitate_type );
  11923. if ( iDecapitateType )
  11924. {
  11925. return 20;
  11926. }
  11927. }
  11928. // Shields have passive resistance so we reduce Max Health bonus
  11929. if ( m_Shared.IsShieldEquipped() )
  11930. {
  11931. return 30;
  11932. }
  11933. return 150;
  11934. }
  11935. else if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) || IsPlayerClass( TF_CLASS_PYRO ) )
  11936. {
  11937. return 125;
  11938. }
  11939. else if ( IsPlayerClass( TF_CLASS_SOLDIER ) || IsPlayerClass( TF_CLASS_MEDIC ) )
  11940. {
  11941. return 150;
  11942. }
  11943. else
  11944. {
  11945. return 175;
  11946. }
  11947. }
  11948. else if ( nRuneType == RUNE_REFLECT )
  11949. {
  11950. return ( 400 - m_PlayerClass.GetMaxHealth() );
  11951. }
  11952. else if ( nRuneType == RUNE_KING )
  11953. {
  11954. return 100;
  11955. }
  11956. else if ( nRuneType == RUNE_VAMPIRE )
  11957. {
  11958. return 80;
  11959. }
  11960. return 0;
  11961. }
  11962. //-----------------------------------------------------------------------------
  11963. // Purpose:
  11964. //-----------------------------------------------------------------------------
  11965. void CTFPlayer::ForceRegenerateAndRespawn( void )
  11966. {
  11967. m_bRegenerating = true;
  11968. ForceRespawn();
  11969. m_bRegenerating = false;
  11970. }
  11971. //-----------------------------------------------------------------------------
  11972. // Purpose: Reset player's information and force him to spawn
  11973. //-----------------------------------------------------------------------------
  11974. void CTFPlayer::ForceRespawn( void )
  11975. {
  11976. VPROF_BUDGET( "CTFPlayer::ForceRespawn", VPROF_BUDGETGROUP_PLAYER );
  11977. CTF_GameStats.Event_PlayerForceRespawn( this );
  11978. m_flSpawnTime = gpGlobals->curtime;
  11979. bool bRandom = false;
  11980. // force a random class if the server requires it
  11981. if ( TFGameRules() && TFGameRules()->IsInArenaMode() )
  11982. {
  11983. if ( tf_arena_force_class.GetBool() == true )
  11984. {
  11985. bRandom = true;
  11986. if ( GetTeamNumber() > LAST_SHARED_TEAM )
  11987. {
  11988. if ( !IsAlive() || ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_STALEMATE ) )
  11989. {
  11990. HandleCommand_JoinClass( "random", false );
  11991. }
  11992. }
  11993. }
  11994. if ( ( tf_arena_use_queue.GetBool() == false && TFGameRules()->IsInWaitingForPlayers() ) || TFGameRules()->State_Get() == GR_STATE_PREGAME )
  11995. {
  11996. return;
  11997. }
  11998. }
  11999. int iDesiredClass = GetDesiredPlayerClassIndex();
  12000. if ( iDesiredClass == TF_CLASS_UNDEFINED )
  12001. {
  12002. return;
  12003. }
  12004. if ( iDesiredClass == TF_CLASS_RANDOM )
  12005. {
  12006. bRandom = true;
  12007. // Don't let them be the same class twice in a row
  12008. do{
  12009. iDesiredClass = random->RandomInt( TF_FIRST_NORMAL_CLASS, TF_LAST_NORMAL_CLASS );
  12010. } while( iDesiredClass == GetPlayerClass()->GetClassIndex() );
  12011. }
  12012. if ( HasTheFlag() )
  12013. {
  12014. DropFlag();
  12015. }
  12016. if ( GetPlayerClass()->GetClassIndex() != iDesiredClass )
  12017. {
  12018. // clean up any pipebombs/buildings in the world (no explosions)
  12019. m_bSwitchedClass = true;
  12020. RemoveAllOwnedEntitiesFromWorld();
  12021. int iOldClass = GetPlayerClass()->GetClassIndex();
  12022. GetPlayerClass()->Init( iDesiredClass );
  12023. // Don't report class changes if we're random, because it's not a player choice
  12024. if ( !bRandom )
  12025. {
  12026. m_iClassChanges++;
  12027. CTF_GameStats.Event_PlayerChangedClass( this, iOldClass, iDesiredClass );
  12028. }
  12029. }
  12030. else
  12031. {
  12032. m_bSwitchedClass = false;
  12033. }
  12034. m_Shared.RemoveAllCond();
  12035. m_Shared.ResetRageMeter();
  12036. if ( m_bSwitchedClass )
  12037. {
  12038. m_iLastWeaponSlot = 1;
  12039. // Tell all the items we have that we've changed class. Some items need to change model.
  12040. // Also reset KillStreaks
  12041. for ( int i = 0; i < MAX_WEAPONS; i++ )
  12042. {
  12043. CTFWeaponBase *pWeapon = (CTFWeaponBase *)GetWeapon(i);
  12044. if ( pWeapon )
  12045. {
  12046. pWeapon->OnOwnerClassChange();
  12047. }
  12048. }
  12049. }
  12050. else
  12051. {
  12052. if ( IsAlive() )
  12053. {
  12054. if ( GetActiveTFWeapon() )
  12055. {
  12056. m_iActiveWeaponTypePriorToDeath = GetActiveTFWeapon()->GetWeaponID();
  12057. }
  12058. SaveLastWeaponSlot();
  12059. }
  12060. }
  12061. // Any Respawns will reset killstreaks
  12062. m_Shared.ResetStreaks();
  12063. for ( int i = 0; i < WeaponCount(); i++ )
  12064. {
  12065. CTFWeaponBase *pWpn = (CTFWeaponBase *)GetWeapon( i );
  12066. if ( !pWpn )
  12067. continue;
  12068. pWpn->SetKillStreak( 0 );
  12069. }
  12070. for ( int i = 0; i < GetNumWearables(); ++i )
  12071. {
  12072. CTFWearable* pWearable = dynamic_cast<CTFWearable*>( GetWearable( i ) );
  12073. if ( !pWearable )
  12074. continue;
  12075. pWearable->SetKillStreak( 0 );
  12076. }
  12077. RemoveAllItems( true );
  12078. // Reset ground state for airwalk animations
  12079. SetGroundEntity( NULL );
  12080. // TODO: move this into conditions
  12081. RemoveTeleportEffect();
  12082. // remove invisibility very quickly
  12083. m_Shared.FadeInvis( 0.1f );
  12084. // Stop any firing that was taking place before respawn.
  12085. m_nButtons = 0;
  12086. StateTransition( TF_STATE_ACTIVE );
  12087. Spawn();
  12088. m_bSwitchedClass = false;
  12089. }
  12090. //-----------------------------------------------------------------------------
  12091. // Purpose: Do nothing multiplayer_animstate takes care of animation.
  12092. // Input : playerAnim -
  12093. //-----------------------------------------------------------------------------
  12094. void CTFPlayer::SetAnimation( PLAYER_ANIM playerAnim )
  12095. {
  12096. return;
  12097. }
  12098. //-----------------------------------------------------------------------------
  12099. // Purpose: Handle cheat commands
  12100. // Input : iImpulse -
  12101. //-----------------------------------------------------------------------------
  12102. void CTFPlayer::CheatImpulseCommands( int iImpulse )
  12103. {
  12104. switch( iImpulse )
  12105. {
  12106. case 101:
  12107. {
  12108. if( sv_cheats->GetBool() )
  12109. {
  12110. extern int gEvilImpulse101;
  12111. gEvilImpulse101 = true;
  12112. GiveAmmo( 1000, TF_AMMO_PRIMARY );
  12113. GiveAmmo( 1000, TF_AMMO_SECONDARY );
  12114. GiveAmmo( 1000, TF_AMMO_METAL );
  12115. GiveAmmo( 1000, TF_AMMO_GRENADES1 );
  12116. GiveAmmo( 1000, TF_AMMO_GRENADES2 );
  12117. GiveAmmo( 1000, TF_AMMO_GRENADES3 );
  12118. TakeHealth( 999, DMG_GENERIC );
  12119. // Refills weapon clips, too
  12120. for ( int i = 0; i < MAX_WEAPONS; i++ )
  12121. {
  12122. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( GetWeapon( i ) );
  12123. if ( !pWeapon )
  12124. continue;
  12125. pWeapon->GiveDefaultAmmo();
  12126. if ( pWeapon->IsEnergyWeapon() )
  12127. {
  12128. pWeapon->WeaponRegenerate();
  12129. }
  12130. }
  12131. m_Shared.m_flRageMeter = 100.f;
  12132. m_Shared.SetDemomanChargeMeter( 100.f );
  12133. gEvilImpulse101 = false;
  12134. }
  12135. }
  12136. break;
  12137. default:
  12138. {
  12139. BaseClass::CheatImpulseCommands( iImpulse );
  12140. }
  12141. }
  12142. }
  12143. //-----------------------------------------------------------------------------
  12144. // Purpose:
  12145. //-----------------------------------------------------------------------------
  12146. void CTFPlayer::SetWeaponBuilder( CTFWeaponBuilder *pBuilder )
  12147. {
  12148. m_hWeaponBuilder = pBuilder;
  12149. }
  12150. //-----------------------------------------------------------------------------
  12151. // Purpose:
  12152. //-----------------------------------------------------------------------------
  12153. CTFWeaponBuilder *CTFPlayer::GetWeaponBuilder( void )
  12154. {
  12155. Assert( 0 );
  12156. return m_hWeaponBuilder;
  12157. }
  12158. //-----------------------------------------------------------------------------
  12159. // Purpose: Returns true if this player is building something
  12160. //-----------------------------------------------------------------------------
  12161. bool CTFPlayer::IsBuilding( void )
  12162. {
  12163. /*
  12164. CTFWeaponBuilder *pBuilder = GetWeaponBuilder();
  12165. if ( pBuilder )
  12166. return pBuilder->IsBuilding();
  12167. */
  12168. return false;
  12169. }
  12170. //-----------------------------------------------------------------------------
  12171. // Purpose:
  12172. //-----------------------------------------------------------------------------
  12173. void CTFPlayer::RemoveBuildResources( int iAmount )
  12174. {
  12175. RemoveAmmo( iAmount, TF_AMMO_METAL );
  12176. }
  12177. //-----------------------------------------------------------------------------
  12178. // Purpose:
  12179. //-----------------------------------------------------------------------------
  12180. void CTFPlayer::AddBuildResources( int iAmount )
  12181. {
  12182. GiveAmmo( iAmount, TF_AMMO_METAL, false, kAmmoSource_Pickup );
  12183. }
  12184. //-----------------------------------------------------------------------------
  12185. // Purpose:
  12186. //-----------------------------------------------------------------------------
  12187. CBaseObject *CTFPlayer::GetObject( int index ) const
  12188. {
  12189. return (CBaseObject *)( m_aObjects[index].Get() );
  12190. }
  12191. //-----------------------------------------------------------------------------
  12192. // Purpose:
  12193. //-----------------------------------------------------------------------------
  12194. CBaseObject *CTFPlayer::GetObjectOfType( int iObjectType, int iObjectMode ) const
  12195. {
  12196. int iNumObjects = GetObjectCount();
  12197. for ( int i=0; i<iNumObjects; i++ )
  12198. {
  12199. CBaseObject *pObj = GetObject(i);
  12200. if ( !pObj )
  12201. continue;
  12202. if ( pObj->GetType() != iObjectType )
  12203. continue;
  12204. if ( pObj->GetObjectMode() != iObjectMode )
  12205. continue;
  12206. if ( pObj->IsDisposableBuilding() )
  12207. continue;
  12208. return pObj;
  12209. }
  12210. return NULL;
  12211. }
  12212. //-----------------------------------------------------------------------------
  12213. // Purpose:
  12214. //-----------------------------------------------------------------------------
  12215. int CTFPlayer::GetObjectCount( void ) const
  12216. {
  12217. return m_aObjects.Count();
  12218. }
  12219. //-----------------------------------------------------------------------------
  12220. // Purpose: Remove all the player's objects
  12221. // If bExplodeBuildings is not set, remove all of them immediately.
  12222. // Otherwise, make them all explode.
  12223. //-----------------------------------------------------------------------------
  12224. void CTFPlayer::RemoveAllObjects( bool bExplodeBuildings /* = false */ )
  12225. {
  12226. // Remove all the player's objects
  12227. for (int i = GetObjectCount()-1; i >= 0; i--)
  12228. {
  12229. CBaseObject *obj = GetObject(i);
  12230. Assert( obj );
  12231. if ( obj )
  12232. {
  12233. // this is separate from the object_destroyed event, which does
  12234. // not get sent when we remove the objects from the world
  12235. IGameEvent *event = gameeventmanager->CreateEvent( "object_removed" );
  12236. if ( event )
  12237. {
  12238. event->SetInt( "userid", GetUserID() ); // user ID of the object owner
  12239. event->SetInt( "objecttype", obj->GetType() ); // type of object removed
  12240. event->SetInt( "index", obj->entindex() ); // index of the object removed
  12241. gameeventmanager->FireEvent( event );
  12242. }
  12243. if ( bExplodeBuildings )
  12244. {
  12245. obj->DetonateObject();
  12246. }
  12247. else
  12248. {
  12249. // This fixes a bug in Raid mode where we could spawn where our sentry was but
  12250. // we didn't get the weapons because they couldn't trace to us in FVisible
  12251. obj->SetSolid( SOLID_NONE );
  12252. UTIL_Remove( obj );
  12253. }
  12254. }
  12255. }
  12256. }
  12257. //-----------------------------------------------------------------------------
  12258. // Purpose:
  12259. //-----------------------------------------------------------------------------
  12260. void CTFPlayer::StopPlacement( void )
  12261. {
  12262. /*
  12263. // Tell our builder weapon
  12264. CTFWeaponBuilder *pBuilder = GetWeaponBuilder();
  12265. if ( pBuilder )
  12266. {
  12267. pBuilder->StopPlacement();
  12268. }
  12269. */
  12270. }
  12271. //-----------------------------------------------------------------------------
  12272. // Purpose: Player has started building an object
  12273. //-----------------------------------------------------------------------------
  12274. int CTFPlayer::StartedBuildingObject( int iObjectType )
  12275. {
  12276. // Deduct the cost of the object
  12277. int iCost = m_Shared.CalculateObjectCost( this, iObjectType );
  12278. if ( iCost > GetBuildResources() )
  12279. {
  12280. // Player must have lost resources since he started placing
  12281. return 0;
  12282. }
  12283. RemoveBuildResources( iCost );
  12284. // If the object costs 0, we need to return non-0 to mean success
  12285. if ( !iCost )
  12286. return 1;
  12287. return iCost;
  12288. }
  12289. //-----------------------------------------------------------------------------
  12290. // Purpose: Player has aborted building something
  12291. //-----------------------------------------------------------------------------
  12292. void CTFPlayer::StoppedBuilding( int iObjectType )
  12293. {
  12294. /*
  12295. int iCost = CalculateObjectCost( iObjectType );
  12296. AddBuildResources( iCost );
  12297. // Tell our builder weapon
  12298. CTFWeaponBuilder *pBuilder = GetWeaponBuilder();
  12299. if ( pBuilder )
  12300. {
  12301. pBuilder->StoppedBuilding( iObjectType );
  12302. }
  12303. */
  12304. }
  12305. //-----------------------------------------------------------------------------
  12306. // Purpose: Object has been built by this player
  12307. //-----------------------------------------------------------------------------
  12308. void CTFPlayer::FinishedObject( CBaseObject *pObject )
  12309. {
  12310. AddObject( pObject );
  12311. CTF_GameStats.Event_PlayerCreatedBuilding( this, pObject );
  12312. if ( TFGameRules() && TFGameRules()->IsInTraining() && TFGameRules()->GetTrainingModeLogic() && IsFakeClient() == false )
  12313. {
  12314. TFGameRules()->GetTrainingModeLogic()->OnPlayerBuiltBuilding( this, pObject );
  12315. }
  12316. /*
  12317. // Tell our builder weapon
  12318. CTFWeaponBuilder *pBuilder = GetWeaponBuilder();
  12319. if ( pBuilder )
  12320. {
  12321. pBuilder->FinishedObject();
  12322. }
  12323. */
  12324. }
  12325. //-----------------------------------------------------------------------------
  12326. // Purpose: Add the specified object to this player's object list.
  12327. //-----------------------------------------------------------------------------
  12328. void CTFPlayer::AddObject( CBaseObject *pObject )
  12329. {
  12330. TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::AddObject adding object %p:%s to player %s\n", gpGlobals->curtime, pObject, pObject->GetClassname(), GetPlayerName() ) );
  12331. // Make a handle out of it
  12332. CHandle<CBaseObject> hObject;
  12333. hObject = pObject;
  12334. bool alreadyInList = PlayerOwnsObject( pObject );
  12335. // Assert( !alreadyInList );
  12336. if ( !alreadyInList )
  12337. {
  12338. m_aObjects.AddToTail( hObject );
  12339. }
  12340. }
  12341. //-----------------------------------------------------------------------------
  12342. // Purpose: Object built by this player has been destroyed
  12343. //-----------------------------------------------------------------------------
  12344. void CTFPlayer::OwnedObjectDestroyed( CBaseObject *pObject )
  12345. {
  12346. TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::OwnedObjectDestroyed player %s object %p:%s\n", gpGlobals->curtime,
  12347. GetPlayerName(),
  12348. pObject,
  12349. pObject->GetClassname() ) );
  12350. RemoveObject( pObject );
  12351. // Tell our builder weapon so it recalculates the state of the build icons
  12352. /*
  12353. CTFWeaponBuilder *pBuilder = GetWeaponBuilder();
  12354. if ( pBuilder )
  12355. {
  12356. pBuilder->RecalcState();
  12357. }
  12358. */
  12359. }
  12360. //-----------------------------------------------------------------------------
  12361. // Removes an object from the player
  12362. //-----------------------------------------------------------------------------
  12363. void CTFPlayer::RemoveObject( CBaseObject *pObject )
  12364. {
  12365. TRACE_OBJECT( UTIL_VarArgs( "%0.2f CBaseTFPlayer::RemoveObject %p:%s from player %s\n", gpGlobals->curtime,
  12366. pObject,
  12367. pObject->GetClassname(),
  12368. GetPlayerName() ) );
  12369. Assert( pObject );
  12370. int i;
  12371. for ( i = m_aObjects.Count(); --i >= 0; )
  12372. {
  12373. // Also, while we're at it, remove all other bogus ones too...
  12374. if ( (!m_aObjects[i].Get()) || (m_aObjects[i] == pObject))
  12375. {
  12376. m_aObjects.FastRemove(i);
  12377. }
  12378. }
  12379. }
  12380. //-----------------------------------------------------------------------------
  12381. // See if the player owns this object
  12382. //-----------------------------------------------------------------------------
  12383. bool CTFPlayer::PlayerOwnsObject( CBaseObject *pObject )
  12384. {
  12385. return ( m_aObjects.Find( pObject ) != -1 );
  12386. }
  12387. //-----------------------------------------------------------------------------
  12388. // Purpose:
  12389. //-----------------------------------------------------------------------------
  12390. void CTFPlayer::PlayFlinch( const CTakeDamageInfo &info )
  12391. {
  12392. // Don't play flinches if we just died.
  12393. if ( !IsAlive() )
  12394. return;
  12395. // No pain flinches while disguised, our man has supreme discipline
  12396. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  12397. return;
  12398. PlayerAnimEvent_t flinchEvent;
  12399. switch ( LastHitGroup() )
  12400. {
  12401. // pick a region-specific flinch
  12402. case HITGROUP_HEAD:
  12403. flinchEvent = PLAYERANIMEVENT_FLINCH_HEAD;
  12404. break;
  12405. case HITGROUP_LEFTARM:
  12406. flinchEvent = PLAYERANIMEVENT_FLINCH_LEFTARM;
  12407. break;
  12408. case HITGROUP_RIGHTARM:
  12409. flinchEvent = PLAYERANIMEVENT_FLINCH_RIGHTARM;
  12410. break;
  12411. case HITGROUP_LEFTLEG:
  12412. flinchEvent = PLAYERANIMEVENT_FLINCH_LEFTLEG;
  12413. break;
  12414. case HITGROUP_RIGHTLEG:
  12415. flinchEvent = PLAYERANIMEVENT_FLINCH_RIGHTLEG;
  12416. break;
  12417. case HITGROUP_STOMACH:
  12418. case HITGROUP_CHEST:
  12419. case HITGROUP_GEAR:
  12420. case HITGROUP_GENERIC:
  12421. default:
  12422. // just get a generic flinch.
  12423. flinchEvent = PLAYERANIMEVENT_FLINCH_CHEST;
  12424. break;
  12425. }
  12426. DoAnimationEvent( flinchEvent );
  12427. }
  12428. //-----------------------------------------------------------------------------
  12429. // Purpose: Plays the crit sound that players that get crit hear
  12430. //-----------------------------------------------------------------------------
  12431. float CTFPlayer::PlayCritReceivedSound( void )
  12432. {
  12433. float flCritPainLength = 0;
  12434. // Play a custom pain sound to the guy taking the damage
  12435. CSingleUserRecipientFilter receiverfilter( this );
  12436. EmitSound_t params;
  12437. params.m_flSoundTime = 0;
  12438. params.m_pSoundName = "TFPlayer.CritPain";
  12439. params.m_pflSoundDuration = &flCritPainLength;
  12440. EmitSound( receiverfilter, entindex(), params );
  12441. return flCritPainLength;
  12442. }
  12443. //-----------------------------------------------------------------------------
  12444. // Purpose:
  12445. //-----------------------------------------------------------------------------
  12446. void CTFPlayer::PainSound( const CTakeDamageInfo &info )
  12447. {
  12448. // Don't make sounds if we just died. DeathSound will handle that.
  12449. if ( !IsAlive() )
  12450. return;
  12451. // no pain sounds while disguised, our man has supreme discipline
  12452. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  12453. return;
  12454. if ( m_flNextPainSoundTime > gpGlobals->curtime )
  12455. return;
  12456. // Don't play falling pain sounds, they have their own system
  12457. if ( info.GetDamageType() & DMG_FALL )
  12458. return;
  12459. // No sound for DMG_GENERIC
  12460. if ( info.GetDamageType() == 0 || info.GetDamageType() == DMG_PREVENT_PHYSICS_FORCE )
  12461. return;
  12462. if ( info.GetDamageType() & DMG_DROWN )
  12463. {
  12464. EmitSound( "TFPlayer.Drown" );
  12465. return;
  12466. }
  12467. if ( info.GetDamageType() & DMG_BURN )
  12468. {
  12469. // Looping fire pain sound is done in CTFPlayerShared::ConditionThink
  12470. return;
  12471. }
  12472. float flPainLength = 0;
  12473. bool bAttackerIsPlayer = ( info.GetAttacker() && info.GetAttacker()->IsPlayer() );
  12474. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  12475. Assert( pExpresser );
  12476. pExpresser->AllowMultipleScenes();
  12477. // speak a pain concept here, send to everyone but the attacker
  12478. CPASFilter filter( GetAbsOrigin() );
  12479. if ( bAttackerIsPlayer )
  12480. {
  12481. filter.RemoveRecipient( ToBasePlayer( info.GetAttacker() ) );
  12482. }
  12483. // play a crit sound to the victim ( us )
  12484. if ( info.GetDamageType() & DMG_CRITICAL )
  12485. {
  12486. flPainLength = PlayCritReceivedSound();
  12487. // remove us from hearing our own pain sound if we hear the crit sound
  12488. filter.RemoveRecipient( this );
  12489. }
  12490. char szResponse[AI_Response::MAX_RESPONSE_NAME];
  12491. if ( SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_PAIN, "damagecritical:1", szResponse, AI_Response::MAX_RESPONSE_NAME, &filter ) )
  12492. {
  12493. flPainLength = MAX( GetSceneDuration( szResponse ), flPainLength );
  12494. }
  12495. // speak a louder pain concept to just the attacker
  12496. if ( bAttackerIsPlayer )
  12497. {
  12498. CSingleUserRecipientFilter attackerFilter( ToBasePlayer( info.GetAttacker() ) );
  12499. SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_ATTACKER_PAIN, "damagecritical:1", szResponse, AI_Response::MAX_RESPONSE_NAME, &attackerFilter );
  12500. }
  12501. pExpresser->DisallowMultipleScenes();
  12502. m_flNextPainSoundTime = gpGlobals->curtime + flPainLength;
  12503. }
  12504. //-----------------------------------------------------------------------------
  12505. // Purpose:
  12506. //-----------------------------------------------------------------------------
  12507. void CTFPlayer::DeathSound( const CTakeDamageInfo &info )
  12508. {
  12509. // Don't make death sounds when choosing a class
  12510. if ( IsPlayerClass( TF_CLASS_UNDEFINED ) )
  12511. return;
  12512. TFPlayerClassData_t *pData = GetPlayerClass()->GetData();
  12513. if ( !pData )
  12514. return;
  12515. if ( m_bGoingFeignDeath )
  12516. {
  12517. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED ) && (m_Shared.GetDisguiseTeam() == GetTeamNumber());
  12518. if ( bDisguised )
  12519. {
  12520. // Use our disguise class, if we have one and will drop a disguise class corpse.
  12521. pData = g_pTFPlayerClassDataMgr->Get( m_Shared.GetDisguiseClass() );
  12522. if ( !pData )
  12523. return;
  12524. }
  12525. }
  12526. CTFPlayer *pAttacker = (CTFPlayer*)ToTFPlayer( info.GetAttacker() );
  12527. if ( pAttacker )
  12528. {
  12529. CTFWeaponBase *pWpn = pAttacker->GetActiveTFWeapon();
  12530. if ( pWpn && pWpn->IsSilentKiller() )
  12531. return;
  12532. }
  12533. int nDeathSoundOffset = DEATH_SOUND_FIRST;
  12534. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  12535. {
  12536. nDeathSoundOffset = IsMiniBoss() ? DEATH_SOUND_GIANT_MVM_FIRST : DEATH_SOUND_MVM_FIRST;
  12537. }
  12538. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() &&
  12539. GetTeamNumber() != TF_TEAM_PVE_INVADERS && !m_bGoingFeignDeath )
  12540. {
  12541. EmitSound( "MVM.PlayerDied" );
  12542. return;
  12543. }
  12544. if ( m_LastDamageType & DMG_FALL ) // Did we die from falling?
  12545. {
  12546. // They died in the fall. Play a splat sound.
  12547. EmitSound( "Player.FallGib" );
  12548. }
  12549. else if ( m_LastDamageType & DMG_BLAST )
  12550. {
  12551. EmitSound( pData->GetDeathSound( DEATH_SOUND_EXPLOSION + nDeathSoundOffset ) );
  12552. }
  12553. else if ( m_LastDamageType & DMG_CRITICAL )
  12554. {
  12555. EmitSound( pData->GetDeathSound( DEATH_SOUND_CRIT + nDeathSoundOffset ) );
  12556. PlayCritReceivedSound();
  12557. }
  12558. else if ( m_LastDamageType & DMG_CLUB )
  12559. {
  12560. EmitSound( pData->GetDeathSound( DEATH_SOUND_MELEE + nDeathSoundOffset ) );
  12561. }
  12562. else
  12563. {
  12564. EmitSound( pData->GetDeathSound( DEATH_SOUND_GENERIC + nDeathSoundOffset ) );
  12565. }
  12566. // Play an additional sound when we're in MvM and have a boss death
  12567. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && IsMiniBoss() )
  12568. {
  12569. switch ( GetPlayerClass()->GetClassIndex() )
  12570. {
  12571. case TF_CLASS_HEAVYWEAPONS:
  12572. {
  12573. EmitSound( "MVM.GiantHeavyExplodes" );
  12574. break;
  12575. }
  12576. default:
  12577. {
  12578. EmitSound( "MVM.GiantCommonExplodes" );
  12579. }
  12580. }
  12581. }
  12582. }
  12583. //-----------------------------------------------------------------------------
  12584. // Purpose:
  12585. //-----------------------------------------------------------------------------
  12586. const char* CTFPlayer::GetSceneSoundToken( void )
  12587. {
  12588. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  12589. {
  12590. if ( IsMiniBoss() )
  12591. {
  12592. return "M_MVM_";
  12593. }
  12594. else
  12595. {
  12596. return "MVM_";
  12597. }
  12598. }
  12599. else
  12600. {
  12601. return "";
  12602. }
  12603. }
  12604. //-----------------------------------------------------------------------------
  12605. // Purpose:
  12606. //-----------------------------------------------------------------------------
  12607. void CTFPlayer::StunSound( CTFPlayer* pAttacker, int iStunFlags, int iOldStunFlags )
  12608. {
  12609. if ( !IsAlive() )
  12610. return;
  12611. if ( !(iStunFlags & TF_STUN_CONTROLS) && !(iStunFlags & TF_STUN_LOSER_STATE) )
  12612. return;
  12613. if ( (iStunFlags & TF_STUN_BY_TRIGGER) && (iOldStunFlags != 0) )
  12614. return; // Only play stun triggered sounds when not already stunned.
  12615. // Play the stun sound for everyone but the attacker.
  12616. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  12617. Assert( pExpresser );
  12618. pExpresser->AllowMultipleScenes();
  12619. float flStunSoundLength = 0;
  12620. EmitSound_t params;
  12621. params.m_flSoundTime = 0;
  12622. if ( iStunFlags & TF_STUN_SPECIAL_SOUND )
  12623. {
  12624. params.m_pSoundName = "TFPlayer.StunImpactRange";
  12625. }
  12626. else if ( (iStunFlags & TF_STUN_LOSER_STATE) && !pAttacker )
  12627. {
  12628. params.m_pSoundName = "Halloween.PlayerScream";
  12629. }
  12630. else
  12631. {
  12632. params.m_pSoundName = "TFPlayer.StunImpact";
  12633. }
  12634. params.m_pflSoundDuration = &flStunSoundLength;
  12635. if ( pAttacker )
  12636. {
  12637. CPASFilter filter( GetAbsOrigin() );
  12638. filter.RemoveRecipient( pAttacker );
  12639. EmitSound( filter, entindex(), params );
  12640. // Play a louder pain sound for the person who got the stun.
  12641. CSingleUserRecipientFilter attackerFilter( pAttacker );
  12642. EmitSound( attackerFilter, pAttacker->entindex(), params );
  12643. }
  12644. else
  12645. {
  12646. EmitSound( params.m_pSoundName );
  12647. }
  12648. pExpresser->DisallowMultipleScenes();
  12649. // Suppress any pain sound that might come right after this stun sound.
  12650. m_flNextPainSoundTime = gpGlobals->curtime + 2.0f;
  12651. }
  12652. //-----------------------------------------------------------------------------
  12653. // Purpose: called when this player burns another player
  12654. //-----------------------------------------------------------------------------
  12655. void CTFPlayer::OnBurnOther( CTFPlayer *pTFPlayerVictim, CTFWeaponBase *pWeapon )
  12656. {
  12657. #define ACHIEVEMENT_BURN_TIME_WINDOW 30.0f
  12658. #define ACHIEVEMENT_BURN_VICTIMS 5
  12659. // add current time we burned another player to head of vector
  12660. m_aBurnOtherTimes.AddToHead( gpGlobals->curtime );
  12661. // remove any burn times that are older than the burn window from the list
  12662. float flTimeDiscard = gpGlobals->curtime - ACHIEVEMENT_BURN_TIME_WINDOW;
  12663. for ( int i = 1; i < m_aBurnOtherTimes.Count(); i++ )
  12664. {
  12665. if ( m_aBurnOtherTimes[i] < flTimeDiscard )
  12666. {
  12667. m_aBurnOtherTimes.RemoveMultiple( i, m_aBurnOtherTimes.Count() - i );
  12668. break;
  12669. }
  12670. }
  12671. // see if we've burned enough players in time window to satisfy achievement
  12672. if ( m_aBurnOtherTimes.Count() >= ACHIEVEMENT_BURN_VICTIMS )
  12673. {
  12674. AwardAchievement( ACHIEVEMENT_TF_BURN_PLAYERSINMINIMIMTIME );
  12675. }
  12676. // ACHIEVEMENT_TF_PYRO_KILL_SPIES - Awarded for igniting enemy spies who have active sappers on friendly building
  12677. if ( pTFPlayerVictim->IsPlayerClass(TF_CLASS_SPY))
  12678. {
  12679. CBaseObject *pSapper = pTFPlayerVictim->GetObjectOfType( OBJ_ATTACHMENT_SAPPER, 0 );
  12680. if ( pSapper )
  12681. {
  12682. AwardAchievement( ACHIEVEMENT_TF_PYRO_KILL_SPIES );
  12683. }
  12684. }
  12685. // ACHIEVEMENT_TF_PYRO_BURN_RJ_SOLDIER - Pyro ignited a rocket jumping soldier in mid-air
  12686. if ( pTFPlayerVictim->IsPlayerClass(TF_CLASS_SOLDIER) )
  12687. {
  12688. if ( pTFPlayerVictim->RocketJumped() && !pTFPlayerVictim->GetGroundEntity() )
  12689. {
  12690. AwardAchievement( ACHIEVEMENT_TF_PYRO_BURN_RJ_SOLDIER );
  12691. }
  12692. }
  12693. // ACHIEVEMENT_TF_PYRO_DEFEND_POINTS - Pyro kills targets capping control points
  12694. CTriggerAreaCapture *pAreaTrigger = pTFPlayerVictim->GetControlPointStandingOn();
  12695. if ( pAreaTrigger )
  12696. {
  12697. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  12698. if ( pCP && pCP->GetOwner() == GetTeamNumber() )
  12699. {
  12700. if ( TeamplayGameRules()->TeamMayCapturePoint( pTFPlayerVictim->GetTeamNumber(), pCP->GetPointIndex() ) &&
  12701. TeamplayGameRules()->PlayerMayCapturePoint( pTFPlayerVictim, pCP->GetPointIndex() ) )
  12702. {
  12703. AwardAchievement( ACHIEVEMENT_TF_PYRO_DEFEND_POINTS );
  12704. }
  12705. }
  12706. }
  12707. // ACHIEVEMENT_TF_MEDIC_ASSIST_PYRO
  12708. // if we're invuln, let the medic know that we burned someone
  12709. if ( m_Shared.InCond( TF_COND_INVULNERABLE ) || m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
  12710. {
  12711. int i;
  12712. int iNumHealers = m_Shared.GetNumHealers();
  12713. for ( i=0;i<iNumHealers;i++ )
  12714. {
  12715. // Send a message to all medics invulning the Pyro at this time
  12716. CTFPlayer *pMedic = ToTFPlayer( m_Shared.GetHealerByIndex( i ) );
  12717. if ( pMedic && pMedic->GetChargeEffectBeingProvided() == MEDIGUN_CHARGE_INVULN )
  12718. {
  12719. // Tell the clients involved in the ignition
  12720. CSingleUserRecipientFilter medic_filter( pMedic );
  12721. UserMessageBegin( medic_filter, "PlayerIgnitedInv" );
  12722. WRITE_BYTE( entindex() );
  12723. WRITE_BYTE( pTFPlayerVictim->entindex() );
  12724. WRITE_BYTE( pMedic->entindex() );
  12725. MessageEnd();
  12726. }
  12727. }
  12728. }
  12729. // Tell the clients involved in the ignition
  12730. CRecipientFilter involved_filter;
  12731. involved_filter.AddRecipient( this );
  12732. involved_filter.AddRecipient( pTFPlayerVictim );
  12733. UserMessageBegin( involved_filter, "PlayerIgnited" );
  12734. WRITE_BYTE( entindex() );
  12735. WRITE_BYTE( pTFPlayerVictim->entindex() );
  12736. WRITE_BYTE( pWeapon ? pWeapon->GetWeaponID() : 0 );
  12737. MessageEnd();
  12738. IGameEvent *event = gameeventmanager->CreateEvent( "player_ignited" );
  12739. if ( event )
  12740. {
  12741. event->SetInt( "pyro_entindex", entindex() );
  12742. event->SetInt( "victim_entindex", pTFPlayerVictim->entindex() );
  12743. event->SetInt( "weaponid", pWeapon ? pWeapon->GetWeaponID() : 0 );
  12744. gameeventmanager->FireEvent( event, true );
  12745. }
  12746. }
  12747. //-----------------------------------------------------------------------------
  12748. // Purpose: Returns true if the player is capturing a point.
  12749. //-----------------------------------------------------------------------------
  12750. bool CTFPlayer::IsCapturingPoint()
  12751. {
  12752. CTriggerAreaCapture *pAreaTrigger = GetControlPointStandingOn();
  12753. if ( pAreaTrigger )
  12754. {
  12755. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  12756. if ( pCP )
  12757. {
  12758. if ( TeamplayGameRules()->TeamMayCapturePoint( GetTeamNumber(), pCP->GetPointIndex() ) &&
  12759. TeamplayGameRules()->PlayerMayCapturePoint( this, pCP->GetPointIndex() ) )
  12760. {
  12761. // if we own this point, we're no longer "capturing" it
  12762. return pCP->GetOwner() != GetTeamNumber();
  12763. }
  12764. }
  12765. }
  12766. return false;
  12767. }
  12768. //-----------------------------------------------------------------------------
  12769. // Purpose:
  12770. //-----------------------------------------------------------------------------
  12771. CTFTeam *CTFPlayer::GetTFTeam( void )
  12772. {
  12773. CTFTeam *pTeam = dynamic_cast<CTFTeam *>( GetTeam() );
  12774. Assert( pTeam );
  12775. return pTeam;
  12776. }
  12777. //-----------------------------------------------------------------------------
  12778. // Purpose:
  12779. //-----------------------------------------------------------------------------
  12780. CTFTeam *CTFPlayer::GetOpposingTFTeam( void )
  12781. {
  12782. if ( TFTeamMgr() )
  12783. {
  12784. int iTeam = GetTeamNumber();
  12785. if ( iTeam == TF_TEAM_RED )
  12786. {
  12787. return TFTeamMgr()->GetTeam( TF_TEAM_BLUE );
  12788. }
  12789. else if ( iTeam == TF_TEAM_BLUE )
  12790. {
  12791. return TFTeamMgr()->GetTeam( TF_TEAM_RED );
  12792. }
  12793. }
  12794. return NULL;
  12795. }
  12796. //-----------------------------------------------------------------------------
  12797. // Purpose: Give this player the "i just teleported" effect for 12 seconds
  12798. //-----------------------------------------------------------------------------
  12799. void CTFPlayer::TeleportEffect( void )
  12800. {
  12801. m_Shared.AddCond( TF_COND_TELEPORTED );
  12802. float flDuration = 12.f;
  12803. if ( TFGameRules()->IsMannVsMachineMode() && m_bIsABot && IsBotOfType( TF_BOT_TYPE ) )
  12804. {
  12805. flDuration = 30.f;
  12806. }
  12807. // Also removed on death
  12808. SetContextThink( &CTFPlayer::RemoveTeleportEffect, gpGlobals->curtime + flDuration, "TFPlayer_TeleportEffect" );
  12809. }
  12810. //-----------------------------------------------------------------------------
  12811. // Purpose: Remove the teleporter effect
  12812. //-----------------------------------------------------------------------------
  12813. void CTFPlayer::RemoveTeleportEffect( void )
  12814. {
  12815. m_Shared.RemoveCond( TF_COND_TELEPORTED );
  12816. }
  12817. //-----------------------------------------------------------------------------
  12818. // Purpose:
  12819. //-----------------------------------------------------------------------------
  12820. void CTFPlayer::StopRagdollDeathAnim( void )
  12821. {
  12822. CTFRagdoll *pRagdoll = dynamic_cast<CTFRagdoll*>( m_hRagdoll.Get() );
  12823. if ( pRagdoll )
  12824. {
  12825. pRagdoll->m_iDamageCustom = 0;
  12826. }
  12827. }
  12828. //-----------------------------------------------------------------------------
  12829. // Purpose:
  12830. //-----------------------------------------------------------------------------
  12831. void CTFPlayer::CreateRagdollEntity( void )
  12832. {
  12833. CreateRagdollEntity( false, false, false, false, false, false, false, false );
  12834. }
  12835. //-----------------------------------------------------------------------------
  12836. // Purpose: Create a ragdoll entity to pass to the client.
  12837. //-----------------------------------------------------------------------------
  12838. void CTFPlayer::CreateRagdollEntity( bool bGib, bool bBurning, bool bElectrocuted, bool bOnGround, bool bCloakedCorpse, bool bGoldRagdoll, bool bIceRagdoll, bool bBecomeAsh, int iDamageCustom, bool bCritOnHardHit )
  12839. {
  12840. // If we already have a ragdoll destroy it.
  12841. CTFRagdoll *pRagdoll = dynamic_cast<CTFRagdoll*>( m_hRagdoll.Get() );
  12842. if( pRagdoll )
  12843. {
  12844. UTIL_Remove( pRagdoll );
  12845. pRagdoll = NULL;
  12846. }
  12847. Assert( pRagdoll == NULL );
  12848. // Create a ragdoll.
  12849. pRagdoll = dynamic_cast<CTFRagdoll*>( CreateEntityByName( "tf_ragdoll" ) );
  12850. if ( pRagdoll )
  12851. {
  12852. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  12853. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  12854. pRagdoll->m_vecForce = m_vecForce;
  12855. pRagdoll->m_nForceBone = m_nForceBone;
  12856. Assert( entindex() >= 1 && entindex() <= MAX_PLAYERS );
  12857. pRagdoll->m_iPlayerIndex.Set( entindex() );
  12858. pRagdoll->m_bGib = bGib;
  12859. pRagdoll->m_bBurning = bBurning;
  12860. pRagdoll->m_bElectrocuted = bElectrocuted;
  12861. pRagdoll->m_bOnGround = bOnGround;
  12862. pRagdoll->m_bCloaked = bCloakedCorpse;
  12863. pRagdoll->m_iDamageCustom = iDamageCustom;
  12864. pRagdoll->m_iTeam = GetTeamNumber();
  12865. pRagdoll->m_iClass = GetPlayerClass()->GetClassIndex();
  12866. pRagdoll->m_bGoldRagdoll = bGoldRagdoll;
  12867. pRagdoll->m_bIceRagdoll = bIceRagdoll;
  12868. pRagdoll->m_bBecomeAsh = bBecomeAsh;
  12869. pRagdoll->m_bCritOnHardHit = bCritOnHardHit;
  12870. pRagdoll->m_flHeadScale = m_flHeadScale;
  12871. pRagdoll->m_flTorsoScale = m_flTorsoScale;
  12872. pRagdoll->m_flHandScale = m_flHandScale;
  12873. }
  12874. // Turn off the player.
  12875. AddSolidFlags( FSOLID_NOT_SOLID );
  12876. AddEffects( EF_NODRAW | EF_NOSHADOW );
  12877. SetMoveType( MOVETYPE_NONE );
  12878. // Add additional gib setup.
  12879. if ( bGib )
  12880. {
  12881. m_nRenderFX = kRenderFxRagdoll;
  12882. }
  12883. // Save ragdoll handle.
  12884. m_hRagdoll = pRagdoll;
  12885. }
  12886. //-----------------------------------------------------------------------------
  12887. // Purpose: Destroy's a ragdoll, called with a player is disconnecting.
  12888. //-----------------------------------------------------------------------------
  12889. void CTFPlayer::DestroyRagdoll( void )
  12890. {
  12891. CTFRagdoll *pRagdoll = dynamic_cast<CTFRagdoll*>( m_hRagdoll.Get() );
  12892. if( pRagdoll )
  12893. {
  12894. UTIL_Remove( pRagdoll );
  12895. }
  12896. // Remove the feign death ragdoll at the same time.
  12897. pRagdoll = dynamic_cast<CTFRagdoll*>( m_hFeignRagdoll.Get() );
  12898. if( pRagdoll )
  12899. {
  12900. UTIL_Remove( pRagdoll );
  12901. }
  12902. }
  12903. //-----------------------------------------------------------------------------
  12904. // Purpose: The player appears to die, creating a corpse and silently stealthing.
  12905. // Occurs when a player takes damage with the dead ringer active
  12906. //-----------------------------------------------------------------------------
  12907. void CTFPlayer::SpyDeadRingerDeath( const CTakeDamageInfo& info )
  12908. {
  12909. // Can't feign death if we're actually dead or if we're not a spy.
  12910. if ( !IsAlive() || !IsPlayerClass( TF_CLASS_SPY ) )
  12911. return;
  12912. // Can't feign death if we're already stealthed.
  12913. if ( m_Shared.InCond( TF_COND_STEALTHED ) )
  12914. return;
  12915. // Can't feign death if we aren't at full cloak energy.
  12916. if ( !CanGoInvisible( true ) || ( m_Shared.GetSpyCloakMeter() < 100.0f ) )
  12917. return;
  12918. m_Shared.SetSpyCloakMeter( 50.0f );
  12919. m_bGoingFeignDeath = true;
  12920. FeignDeath( info );
  12921. // Go feign death.
  12922. m_Shared.AddCond( TF_COND_FEIGN_DEATH, tf_feign_death_duration.GetFloat() );
  12923. m_bGoingFeignDeath = false;
  12924. }
  12925. //-----------------------------------------------------------------------------
  12926. // Purpose: The player appears to die, creating a corpse
  12927. //-----------------------------------------------------------------------------
  12928. void CTFPlayer::FeignDeath( const CTakeDamageInfo& info )
  12929. {
  12930. if ( HasTheFlag() )
  12931. {
  12932. DropFlag();
  12933. }
  12934. // Dead Ringer death removes Powerup Rune for authenticity
  12935. DropRune();
  12936. // Only drop disguised ragdoll & weapon if we're disguised as a teammate.
  12937. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED ) && (m_Shared.GetDisguiseTeam() == GetTeamNumber());
  12938. // We want the ragdoll to burn if the player was burning and was not disguised as a pyro.
  12939. bool bBurning = m_Shared.InCond( TF_COND_BURNING ) && (!bDisguised || (TF_CLASS_PYRO != m_Shared.GetDisguiseClass()));
  12940. // Stop us from burning and other effects that would give the game away.
  12941. m_Shared.RemoveCond( TF_COND_BURNING );
  12942. m_Shared.RemoveCond( TF_COND_BLEEDING );
  12943. RemoveTeleportEffect();
  12944. // Fake death audio.
  12945. EmitSound( "BaseCombatCharacter.StopWeaponSounds" );
  12946. SpeakConceptIfAllowed( MP_CONCEPT_DIED );
  12947. DeathSound( info );
  12948. // Check if we should create gibs.
  12949. bool bGib = ShouldGib( info );
  12950. SetGibbedOnLastDeath( bGib );
  12951. // Fake death notice.
  12952. TFGameRules()->DeathNotice( this, info );
  12953. // Drop an empty ammo pack!
  12954. if ( ShouldDropAmmoPack() )
  12955. {
  12956. DropAmmoPack( info, true /*Empty*/, bDisguised );
  12957. }
  12958. if ( TFGameRules()->IsInMedievalMode() )
  12959. {
  12960. DropHealthPack( info, true );
  12961. }
  12962. if ( GetActiveTFWeapon() )
  12963. {
  12964. int iDropHealthOnKill = 0;
  12965. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetActiveTFWeapon(), iDropHealthOnKill, drop_health_pack_on_kill );
  12966. if ( iDropHealthOnKill == 1 )
  12967. {
  12968. DropHealthPack( info, true );
  12969. }
  12970. }
  12971. CTFPlayer *pTFPlayer = ToTFPlayer( info.GetAttacker() );
  12972. if ( pTFPlayer )
  12973. {
  12974. int iKillForcesAttackerToLaugh = 0;
  12975. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pTFPlayer, iKillForcesAttackerToLaugh, kill_forces_attacker_to_laugh );
  12976. if ( iKillForcesAttackerToLaugh == 1 )
  12977. {
  12978. // force the attacker to laugh!
  12979. pTFPlayer->Taunt( TAUNT_MISC_ITEM, MP_CONCEPT_TAUNT_LAUGH );
  12980. }
  12981. CTFWeaponInvis *pWpn = (CTFWeaponInvis *)Weapon_OwnsThisID( TF_WEAPON_INVIS );
  12982. if ( pWpn && pWpn->HasFeignDeath() )
  12983. {
  12984. DropDeathCallingCard( pTFPlayer, this );
  12985. }
  12986. }
  12987. // Create a ragdoll.
  12988. CreateFeignDeathRagdoll( info, bGib, bBurning, bDisguised );
  12989. // Note that we succeeded for stats tracking.
  12990. EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( GetEntityForLoadoutSlot( LOADOUT_POSITION_PDA2 ) ),
  12991. this,
  12992. pTFPlayer, // in this case the "victim" is the person doing the damage
  12993. kKillEaterEvent_DeathsFeigned );
  12994. }
  12995. //-----------------------------------------------------------------------------
  12996. // Purpose: Create a ragdoll entity for feign death. Does not hide the player.
  12997. // Creates an entirely seperate ragdoll that isn't used for client death cam or other real death stuff.
  12998. //-----------------------------------------------------------------------------
  12999. void CTFPlayer::CreateFeignDeathRagdoll( const CTakeDamageInfo& info, bool bGib, bool bBurning, bool bDisguised )
  13000. {
  13001. // If we already have a feigning ragdoll destroy it.
  13002. CTFRagdoll *pRagdoll = dynamic_cast<CTFRagdoll*>( m_hFeignRagdoll.Get() );
  13003. if( pRagdoll )
  13004. {
  13005. UTIL_Remove( pRagdoll );
  13006. pRagdoll = NULL;
  13007. }
  13008. Assert( pRagdoll == NULL );
  13009. // Create a ragdoll.
  13010. pRagdoll = dynamic_cast<CTFRagdoll*>( CreateEntityByName( "tf_ragdoll" ) );
  13011. if ( pRagdoll )
  13012. {
  13013. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  13014. pRagdoll->m_vecRagdollVelocity = m_vecFeignDeathVelocity;
  13015. pRagdoll->m_vecForce = CalcDamageForceVector( info );
  13016. pRagdoll->m_nForceBone = m_nForceBone;
  13017. Assert( entindex() >= 1 && entindex() <= MAX_PLAYERS );
  13018. pRagdoll->m_iPlayerIndex.Set( entindex() );
  13019. pRagdoll->m_bGib = bGib;
  13020. pRagdoll->m_bBurning = bBurning;
  13021. pRagdoll->m_bElectrocuted = false;
  13022. pRagdoll->m_bFeignDeath = true;
  13023. pRagdoll->m_bWasDisguised = bDisguised;
  13024. pRagdoll->m_bBecomeAsh = false;
  13025. pRagdoll->m_bOnGround = (bool) (GetFlags() & FL_ONGROUND);
  13026. pRagdoll->m_iDamageCustom = info.GetDamageCustom();
  13027. pRagdoll->m_bCritOnHardHit = false;
  13028. pRagdoll->m_flHeadScale = m_flHeadScale;
  13029. pRagdoll->m_flTorsoScale = m_flTorsoScale;
  13030. pRagdoll->m_flHandScale = m_flHandScale;
  13031. {
  13032. int iGoldRagdoll = 0;
  13033. if ( info.GetWeapon() )
  13034. {
  13035. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iGoldRagdoll, set_turn_to_gold );
  13036. }
  13037. pRagdoll->m_bGoldRagdoll = iGoldRagdoll != 0;
  13038. int iIceRagdoll = 0;
  13039. if ( info.GetWeapon() )
  13040. {
  13041. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iIceRagdoll, set_turn_to_ice );
  13042. }
  13043. pRagdoll->m_bIceRagdoll = iIceRagdoll != 0;
  13044. int iRagdollsBecomeAsh = 0;
  13045. if ( info.GetWeapon() )
  13046. {
  13047. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iRagdollsBecomeAsh, ragdolls_become_ash );
  13048. }
  13049. pRagdoll->m_bBecomeAsh = iRagdollsBecomeAsh != 0;
  13050. int iRagdollsPlasmaEffect = 0;
  13051. if ( info.GetWeapon() )
  13052. {
  13053. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iRagdollsPlasmaEffect, ragdolls_plasma_effect );
  13054. }
  13055. if ( iRagdollsPlasmaEffect )
  13056. {
  13057. pRagdoll->m_iDamageCustom = TF_DMG_CUSTOM_PLASMA;
  13058. }
  13059. int iCritOnHardHit = 0;
  13060. if ( info.GetWeapon() )
  13061. {
  13062. CALL_ATTRIB_HOOK_INT_ON_OTHER( info.GetWeapon(), iCritOnHardHit, crit_on_hard_hit );
  13063. }
  13064. pRagdoll->m_bCritOnHardHit = iCritOnHardHit != 0;
  13065. }
  13066. // If we are disguised, make the ragdoll look like our disguise.
  13067. if ( bDisguised )
  13068. {
  13069. pRagdoll->m_iTeam = m_Shared.GetDisguiseTeam();
  13070. pRagdoll->m_iClass = m_Shared.GetDisguiseClass();
  13071. }
  13072. else
  13073. {
  13074. pRagdoll->m_iTeam = GetTeamNumber();
  13075. pRagdoll->m_iClass = GetPlayerClass()->GetClassIndex();
  13076. }
  13077. }
  13078. // Exaggerate ragdoll velocity if recently hit by blast damage.
  13079. if ( !bGib && ( info.GetDamageType() & DMG_BLAST ) )
  13080. {
  13081. Vector vForceModifier = info.GetDamageForce();
  13082. vForceModifier.x *= 1.5;
  13083. vForceModifier.y *= 1.5;
  13084. vForceModifier.z *= 1;
  13085. pRagdoll->m_vecForce = vForceModifier;
  13086. }
  13087. // Save ragdoll handle.
  13088. m_hFeignRagdoll = pRagdoll;
  13089. }
  13090. //-----------------------------------------------------------------------------
  13091. // Purpose:
  13092. //-----------------------------------------------------------------------------
  13093. void CTFPlayer::Weapon_FrameUpdate( void )
  13094. {
  13095. BaseClass::Weapon_FrameUpdate();
  13096. if ( m_hOffHandWeapon.Get() && m_hOffHandWeapon->IsWeaponVisible() )
  13097. {
  13098. m_hOffHandWeapon->Operator_FrameUpdate( this );
  13099. }
  13100. }
  13101. //-----------------------------------------------------------------------------
  13102. // Purpose:
  13103. // Input :
  13104. // Output :
  13105. //-----------------------------------------------------------------------------
  13106. void CTFPlayer::Weapon_HandleAnimEvent( animevent_t *pEvent )
  13107. {
  13108. BaseClass::Weapon_HandleAnimEvent( pEvent );
  13109. if ( m_hOffHandWeapon.Get() )
  13110. {
  13111. m_hOffHandWeapon->Operator_HandleAnimEvent( pEvent, this );
  13112. }
  13113. }
  13114. //-----------------------------------------------------------------------------
  13115. // Purpose:
  13116. // Input :
  13117. // Output :
  13118. //-----------------------------------------------------------------------------
  13119. void CTFPlayer::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget , const Vector *pVelocity )
  13120. {
  13121. }
  13122. //-----------------------------------------------------------------------------
  13123. // Purpose: Call this when this player fires a weapon to allow other systems to react
  13124. //-----------------------------------------------------------------------------
  13125. void CTFPlayer::OnMyWeaponFired( CBaseCombatWeapon *weapon )
  13126. {
  13127. BaseClass::OnMyWeaponFired( weapon );
  13128. // mark region as 'in combat'
  13129. if ( m_inCombatThrottleTimer.IsElapsed() )
  13130. {
  13131. CTFWeaponBase *tfWeapon = static_cast< CTFWeaponBase * >( weapon );
  13132. if ( !tfWeapon )
  13133. {
  13134. return;
  13135. }
  13136. switch ( tfWeapon->GetWeaponID() )
  13137. {
  13138. case TF_WEAPON_MEDIGUN:
  13139. case TF_WEAPON_PDA:
  13140. case TF_WEAPON_PDA_ENGINEER_BUILD:
  13141. case TF_WEAPON_PDA_ENGINEER_DESTROY:
  13142. case TF_WEAPON_PDA_SPY:
  13143. case TF_WEAPON_BUILDER:
  13144. case TF_WEAPON_DISPENSER:
  13145. case TF_WEAPON_INVIS:
  13146. case TF_WEAPON_LUNCHBOX:
  13147. case TF_WEAPON_BUFF_ITEM:
  13148. case TF_WEAPON_PUMPKIN_BOMB:
  13149. case TF_WEAPON_WRENCH: // skip this so engineer building doesn't mark 'in combat'
  13150. case TF_WEAPON_PDA_SPY_BUILD:
  13151. // not a 'combat' weapon
  13152. return;
  13153. };
  13154. // important to keep this at one second, so rate cvars make sense (units/sec)
  13155. m_inCombatThrottleTimer.Start( 1.0f );
  13156. // only search up/down StepHeight as a cheap substitute for line of sight
  13157. CUtlVector< CNavArea * > nearbyAreaVector;
  13158. CollectSurroundingAreas( &nearbyAreaVector, GetLastKnownArea(), tf_nav_in_combat_range.GetFloat(), StepHeight, StepHeight );
  13159. for( int i=0; i<nearbyAreaVector.Count(); ++i )
  13160. {
  13161. static_cast< CTFNavArea * >( nearbyAreaVector[i] )->OnCombat();
  13162. }
  13163. }
  13164. }
  13165. //-----------------------------------------------------------------------------
  13166. // Purpose: Remove invisibility, called when player attacks
  13167. //-----------------------------------------------------------------------------
  13168. void CTFPlayer::RemoveInvisibility( void )
  13169. {
  13170. if ( !m_Shared.IsStealthed() )
  13171. return;
  13172. // remove quickly
  13173. CTFPlayer *pProvider = ToTFPlayer( m_Shared.GetConditionProvider( TF_COND_STEALTHED_USER_BUFF ) );
  13174. bool bAEStealth = ( m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF ) &&
  13175. pProvider &&
  13176. ( pProvider->IsPlayerClass( TF_CLASS_SPY ) ? true : false ) &&
  13177. ( pProvider != this ) );
  13178. if ( m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF ) )
  13179. {
  13180. m_Shared.AddCond( TF_COND_STEALTHED_USER_BUFF_FADING, ( bAEStealth ) ? 4.f : 0.5f );
  13181. }
  13182. m_Shared.FadeInvis( bAEStealth ? 2.f : 0.5f );
  13183. }
  13184. //-----------------------------------------------------------------------------
  13185. // Purpose:
  13186. //-----------------------------------------------------------------------------
  13187. bool CTFPlayer::SayAskForBall()
  13188. {
  13189. if ( !TFGameRules() || !TFGameRules()->IsPasstimeMode()
  13190. || ( m_Shared.AskForBallTime() > gpGlobals->curtime ) )
  13191. {
  13192. return false;
  13193. }
  13194. CPasstimeBall *pBall = g_pPasstimeLogic->GetBall();
  13195. if ( !pBall )
  13196. {
  13197. return false;
  13198. }
  13199. CTFPlayer *pBallCarrier = pBall->GetCarrier();
  13200. if ( !pBallCarrier )
  13201. {
  13202. return false;
  13203. }
  13204. HudNotification_t cantCarryReason;
  13205. if ( !CPasstimeGun::BValidPassTarget( pBallCarrier, this, &cantCarryReason ) )
  13206. {
  13207. if ( cantCarryReason )
  13208. {
  13209. CSingleUserReliableRecipientFilter filter( this );
  13210. TFGameRules()->SendHudNotification( filter, cantCarryReason );
  13211. }
  13212. return false;
  13213. }
  13214. CRecipientFilter filter;
  13215. filter.AddRecipient( this );
  13216. filter.AddRecipient( pBallCarrier );
  13217. filter.MakeReliable();
  13218. EmitSound( filter, entindex(), "Passtime.AskForBall" );
  13219. ++CTF_GameStats.m_passtimeStats.summary.nTotalPassRequests;
  13220. m_Shared.SetAskForBallTime( gpGlobals->curtime + 5.0f );
  13221. return true;
  13222. }
  13223. //-----------------------------------------------------------------------------
  13224. // Purpose:
  13225. //-----------------------------------------------------------------------------
  13226. void CTFPlayer::SaveMe( void )
  13227. {
  13228. if ( !IsAlive() || IsPlayerClass( TF_CLASS_UNDEFINED ) || GetTeamNumber() < TF_TEAM_RED )
  13229. return;
  13230. m_bSaveMeParity = !m_bSaveMeParity;
  13231. }
  13232. //-----------------------------------------------------------------------------
  13233. // Purpose: drops the flag
  13234. //-----------------------------------------------------------------------------
  13235. void CC_DropItem( void )
  13236. {
  13237. CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
  13238. if ( !pPlayer )
  13239. return;
  13240. if ( pPlayer->m_Shared.IsCarryingRune() )
  13241. {
  13242. pPlayer->DropRune();
  13243. return;
  13244. }
  13245. if ( pPlayer->HasTheFlag() )
  13246. {
  13247. pPlayer->DropFlag();
  13248. }
  13249. }
  13250. static ConCommand dropitem( "dropitem", CC_DropItem, "Drop the flag." );
  13251. //-----------------------------------------------------------------------------
  13252. // Purpose:
  13253. //-----------------------------------------------------------------------------
  13254. CObserverPoint::CObserverPoint()
  13255. {
  13256. m_bMatchSummary = false;
  13257. }
  13258. //-----------------------------------------------------------------------------
  13259. // Purpose:
  13260. //-----------------------------------------------------------------------------
  13261. void CObserverPoint::Activate( void )
  13262. {
  13263. BaseClass::Activate();
  13264. if ( m_bMatchSummary )
  13265. {
  13266. // sanity check to make sure the competitive match summary target is disabled until we're ready for it
  13267. SetDisabled( true );
  13268. }
  13269. if ( m_iszAssociateTeamEntityName != NULL_STRING )
  13270. {
  13271. m_hAssociatedTeamEntity = gEntList.FindEntityByName( NULL, m_iszAssociateTeamEntityName );
  13272. if ( !m_hAssociatedTeamEntity )
  13273. {
  13274. Warning("info_observer_point (%s) couldn't find associated team entity named '%s'\n", GetDebugName(), STRING(m_iszAssociateTeamEntityName) );
  13275. }
  13276. }
  13277. }
  13278. //-----------------------------------------------------------------------------
  13279. // Purpose:
  13280. //-----------------------------------------------------------------------------
  13281. bool CObserverPoint::CanUseObserverPoint( CTFPlayer *pPlayer )
  13282. {
  13283. if ( m_bDisabled )
  13284. return false;
  13285. // Only spectate observer points on control points in the current miniround
  13286. if ( g_pObjectiveResource->PlayingMiniRounds() && m_hAssociatedTeamEntity )
  13287. {
  13288. CTeamControlPoint *pPoint = dynamic_cast<CTeamControlPoint*>(m_hAssociatedTeamEntity.Get());
  13289. if ( pPoint )
  13290. {
  13291. bool bInRound = g_pObjectiveResource->IsInMiniRound( pPoint->GetPointIndex() );
  13292. if ( !bInRound )
  13293. return false;
  13294. }
  13295. }
  13296. if ( m_hAssociatedTeamEntity && mp_forcecamera.GetInt() == OBS_ALLOW_TEAM )
  13297. {
  13298. // don't care about this check during a team win
  13299. if ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_TEAM_WIN )
  13300. {
  13301. // If we don't own the associated team entity, we can't use this point
  13302. if ( m_hAssociatedTeamEntity->GetTeamNumber() != pPlayer->GetTeamNumber() && pPlayer->GetTeamNumber() >= FIRST_GAME_TEAM )
  13303. return false;
  13304. }
  13305. }
  13306. return true;
  13307. }
  13308. //-----------------------------------------------------------------------------
  13309. // Purpose:
  13310. //-----------------------------------------------------------------------------
  13311. int CObserverPoint::UpdateTransmitState()
  13312. {
  13313. return SetTransmitState( FL_EDICT_ALWAYS );
  13314. }
  13315. //-----------------------------------------------------------------------------
  13316. // Purpose:
  13317. //-----------------------------------------------------------------------------
  13318. void CObserverPoint::InputEnable( inputdata_t &inputdata )
  13319. {
  13320. m_bDisabled = false;
  13321. }
  13322. //-----------------------------------------------------------------------------
  13323. // Purpose:
  13324. //-----------------------------------------------------------------------------
  13325. void CObserverPoint::InputDisable( inputdata_t &inputdata )
  13326. {
  13327. m_bDisabled = true;
  13328. }
  13329. BEGIN_DATADESC( CObserverPoint )
  13330. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  13331. DEFINE_KEYFIELD( m_bDefaultWelcome, FIELD_BOOLEAN, "defaultwelcome" ),
  13332. DEFINE_KEYFIELD( m_iszAssociateTeamEntityName, FIELD_STRING, "associated_team_entity" ),
  13333. DEFINE_KEYFIELD( m_flFOV, FIELD_FLOAT, "fov" ),
  13334. DEFINE_KEYFIELD( m_bMatchSummary, FIELD_BOOLEAN, "match_summary" ),
  13335. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  13336. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  13337. END_DATADESC()
  13338. LINK_ENTITY_TO_CLASS( info_observer_point, CObserverPoint );
  13339. //-----------------------------------------------------------------------------
  13340. // Purpose: Builds a list of entities that this player can observe.
  13341. // Returns the index into the list of the player's current observer target.
  13342. //-----------------------------------------------------------------------------
  13343. int CTFPlayer::BuildObservableEntityList( void )
  13344. {
  13345. m_hObservableEntities.Purge();
  13346. int iCurrentIndex = -1;
  13347. // Add all the map-placed observer points
  13348. CBaseEntity *pObserverPoint = gEntList.FindEntityByClassname( NULL, "info_observer_point" );
  13349. while ( pObserverPoint )
  13350. {
  13351. m_hObservableEntities.AddToTail( pObserverPoint );
  13352. if ( m_hObserverTarget.Get() == pObserverPoint )
  13353. {
  13354. iCurrentIndex = (m_hObservableEntities.Count()-1);
  13355. }
  13356. pObserverPoint = gEntList.FindEntityByClassname( pObserverPoint, "info_observer_point" );
  13357. }
  13358. // Add all the players
  13359. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  13360. {
  13361. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  13362. if ( pPlayer )
  13363. {
  13364. m_hObservableEntities.AddToTail( pPlayer );
  13365. if ( m_hObserverTarget.Get() == pPlayer )
  13366. {
  13367. iCurrentIndex = (m_hObservableEntities.Count()-1);
  13368. }
  13369. }
  13370. }
  13371. // Add all my objects
  13372. int iNumObjects = GetObjectCount();
  13373. for ( int i = 0; i < iNumObjects; i++ )
  13374. {
  13375. CBaseObject *pObj = GetObject( i );
  13376. if ( pObj )
  13377. {
  13378. m_hObservableEntities.AddToTail( pObj );
  13379. if ( m_hObserverTarget.Get() == pObj )
  13380. {
  13381. iCurrentIndex = ( m_hObservableEntities.Count() - 1 );
  13382. }
  13383. }
  13384. }
  13385. #ifdef TF_RAID_MODE
  13386. // Add all of the objects for my team if we're in Raid mode
  13387. if ( TFGameRules() && TFGameRules()->IsRaidMode() )
  13388. {
  13389. CTFTeam *pTeam = TFTeamMgr()->GetTeam( TF_TEAM_BLUE );
  13390. if ( pTeam )
  13391. {
  13392. int nTeamObjectCount = pTeam->GetNumObjects();
  13393. for ( int iObject = 0; iObject < nTeamObjectCount; ++iObject )
  13394. {
  13395. CBaseObject *pObj = pTeam->GetObject( iObject );
  13396. if ( !pObj )
  13397. continue;
  13398. // we've already added our own buildings in the previous loop
  13399. if ( pObj->GetOwner() == this )
  13400. continue;
  13401. m_hObservableEntities.AddToTail( pObj );
  13402. if ( m_hObserverTarget.Get() == pObj )
  13403. {
  13404. iCurrentIndex = ( m_hObservableEntities.Count() - 1 );
  13405. }
  13406. }
  13407. }
  13408. }
  13409. #endif // TF_RAID_MODE
  13410. // If there are any team_train_watchers, add the train they are linked to
  13411. CTeamTrainWatcher *pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( NULL, "team_train_watcher" ) );
  13412. while ( pWatcher )
  13413. {
  13414. if ( !pWatcher->IsDisabled() )
  13415. {
  13416. CBaseEntity *pTrain = pWatcher->GetTrainEntity();
  13417. if ( pTrain )
  13418. {
  13419. m_hObservableEntities.AddToTail( pTrain );
  13420. if ( m_hObserverTarget.Get() == pTrain )
  13421. {
  13422. iCurrentIndex = (m_hObservableEntities.Count()-1);
  13423. }
  13424. }
  13425. }
  13426. pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( pWatcher, "team_train_watcher" ) );
  13427. }
  13428. // observe active bosses
  13429. if ( TFGameRules()->GetActiveBoss() )
  13430. {
  13431. m_hObservableEntities.AddToTail( TFGameRules()->GetActiveBoss() );
  13432. if ( m_hObserverTarget.Get() == TFGameRules()->GetActiveBoss() )
  13433. {
  13434. iCurrentIndex = ( m_hObservableEntities.Count() - 1 );
  13435. }
  13436. }
  13437. return iCurrentIndex;
  13438. }
  13439. //-----------------------------------------------------------------------------
  13440. // Purpose:
  13441. //-----------------------------------------------------------------------------
  13442. int CTFPlayer::GetNextObserverSearchStartPoint( bool bReverse )
  13443. {
  13444. int iDir = bReverse ? -1 : 1;
  13445. int startIndex = BuildObservableEntityList();
  13446. int iMax = m_hObservableEntities.Count()-1;
  13447. startIndex += iDir;
  13448. if (startIndex > iMax)
  13449. startIndex = 0;
  13450. else if (startIndex < 0)
  13451. startIndex = iMax;
  13452. return startIndex;
  13453. }
  13454. //-----------------------------------------------------------------------------
  13455. // Purpose:
  13456. //-----------------------------------------------------------------------------
  13457. CBaseEntity *CTFPlayer::FindNextObserverTarget(bool bReverse)
  13458. {
  13459. int startIndex = GetNextObserverSearchStartPoint( bReverse );
  13460. int currentIndex = startIndex;
  13461. int iDir = bReverse ? -1 : 1;
  13462. int iMax = m_hObservableEntities.Count()-1;
  13463. // Make sure the current index is within the max. Can happen if we were previously
  13464. // spectating an object which has been destroyed.
  13465. if ( startIndex > iMax )
  13466. {
  13467. currentIndex = startIndex = 1;
  13468. }
  13469. do
  13470. {
  13471. CBaseEntity *nextTarget = m_hObservableEntities[currentIndex];
  13472. if ( IsValidObserverTarget( nextTarget ) )
  13473. return nextTarget;
  13474. currentIndex += iDir;
  13475. // Loop through the entities
  13476. if (currentIndex > iMax)
  13477. {
  13478. currentIndex = 0;
  13479. }
  13480. else if (currentIndex < 0)
  13481. {
  13482. currentIndex = iMax;
  13483. }
  13484. } while ( currentIndex != startIndex );
  13485. return NULL;
  13486. }
  13487. //-----------------------------------------------------------------------------
  13488. // Purpose:
  13489. //-----------------------------------------------------------------------------
  13490. bool CTFPlayer::IsValidObserverTarget(CBaseEntity * target)
  13491. {
  13492. if ( !target || target == this )
  13493. return false;
  13494. // if we are coaching, the target is always valid
  13495. if ( target && m_hStudent == target && target->IsPlayer() )
  13496. {
  13497. return true;
  13498. }
  13499. bool bAllowInTournament = false;
  13500. if ( TFGameRules()->IsMannVsMachineMode() )
  13501. {
  13502. bAllowInTournament = true;
  13503. }
  13504. if ( TFGameRules()->IsPasstimeMode() && (target == TFGameRules()->GetObjectiveObserverTarget()) )
  13505. {
  13506. return true;
  13507. }
  13508. if ( target && !target->IsPlayer() )
  13509. {
  13510. //Can only spectate players in Tournament Mode
  13511. if ( TFGameRules()->IsInTournamentMode() == true && !bAllowInTournament )
  13512. return false;
  13513. CObserverPoint *pObsPoint = dynamic_cast<CObserverPoint *>(target);
  13514. if ( pObsPoint && !pObsPoint->CanUseObserverPoint( this ) )
  13515. return false;
  13516. CFuncTrackTrain *pTrain = dynamic_cast<CFuncTrackTrain *>(target);
  13517. if ( pTrain )
  13518. {
  13519. // can only spec the trains while the round is running
  13520. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
  13521. return false;
  13522. }
  13523. if ( GetTeamNumber() == TEAM_SPECTATOR )
  13524. return true;
  13525. switch ( mp_forcecamera.GetInt() )
  13526. {
  13527. case OBS_ALLOW_ALL : break;
  13528. case OBS_ALLOW_TEAM : if ( target->GetTeamNumber() != TEAM_UNASSIGNED && GetTeamNumber() != target->GetTeamNumber() )
  13529. return false;
  13530. break;
  13531. case OBS_ALLOW_NONE : return false;
  13532. }
  13533. return true;
  13534. }
  13535. return BaseClass::IsValidObserverTarget( target );
  13536. }
  13537. //-----------------------------------------------------------------------------
  13538. // Purpose:
  13539. //-----------------------------------------------------------------------------
  13540. void CTFPlayer::PickWelcomeObserverPoint( void )
  13541. {
  13542. //Don't just spawn at the world origin, find a nice spot to look from while we choose our team and class.
  13543. CObserverPoint *pObserverPoint = (CObserverPoint *)gEntList.FindEntityByClassname( NULL, "info_observer_point" );
  13544. while ( pObserverPoint )
  13545. {
  13546. if ( IsValidObserverTarget( pObserverPoint ) )
  13547. {
  13548. SetObserverTarget( pObserverPoint );
  13549. }
  13550. if ( pObserverPoint->IsDefaultWelcome() )
  13551. break;
  13552. pObserverPoint = (CObserverPoint *)gEntList.FindEntityByClassname( pObserverPoint, "info_observer_point" );
  13553. }
  13554. }
  13555. //-----------------------------------------------------------------------------
  13556. // Purpose:
  13557. //-----------------------------------------------------------------------------
  13558. bool CTFPlayer::SetObserverTarget(CBaseEntity *target)
  13559. {
  13560. ClearZoomOwner();
  13561. SetFOV( this, 0 );
  13562. if ( !BaseClass::SetObserverTarget(target) )
  13563. return false;
  13564. CObserverPoint *pObsPoint = dynamic_cast<CObserverPoint *>(target);
  13565. if ( pObsPoint )
  13566. {
  13567. SetViewOffset( vec3_origin );
  13568. JumptoPosition( target->GetAbsOrigin(), target->EyeAngles() );
  13569. SetFOV( pObsPoint, pObsPoint->m_flFOV );
  13570. }
  13571. if ( !m_bArenaIsAFK )
  13572. {
  13573. m_flLastAction = gpGlobals->curtime;
  13574. }
  13575. return true;
  13576. }
  13577. //-----------------------------------------------------------------------------
  13578. // Purpose: Find the nearest team member within the distance of the origin.
  13579. // Favor players who are the same class.
  13580. //-----------------------------------------------------------------------------
  13581. CBaseEntity *CTFPlayer::FindNearestObservableTarget( Vector vecOrigin, float flMaxDist )
  13582. {
  13583. CTeam *pTeam = GetTeam();
  13584. CBaseEntity *pReturnTarget = NULL;
  13585. bool bFoundClass = false;
  13586. float flCurDistSqr = (flMaxDist * flMaxDist);
  13587. int iNumPlayers = pTeam->GetNumPlayers();
  13588. if ( pTeam->GetTeamNumber() == TEAM_SPECTATOR )
  13589. {
  13590. iNumPlayers = gpGlobals->maxClients;
  13591. }
  13592. for ( int i = 0; i < iNumPlayers; i++ )
  13593. {
  13594. CTFPlayer *pPlayer = NULL;
  13595. if ( pTeam->GetTeamNumber() == TEAM_SPECTATOR )
  13596. {
  13597. pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  13598. }
  13599. else
  13600. {
  13601. pPlayer = ToTFPlayer( pTeam->GetPlayer(i) );
  13602. }
  13603. if ( !pPlayer )
  13604. continue;
  13605. if ( !IsValidObserverTarget(pPlayer) )
  13606. continue;
  13607. float flDistSqr = ( pPlayer->GetAbsOrigin() - vecOrigin ).LengthSqr();
  13608. if ( flDistSqr < flCurDistSqr )
  13609. {
  13610. // If we've found a player matching our class already, this guy needs
  13611. // to be a matching class and closer to boot.
  13612. if ( !bFoundClass || pPlayer->IsPlayerClass( GetPlayerClass()->GetClassIndex() ) )
  13613. {
  13614. pReturnTarget = pPlayer;
  13615. flCurDistSqr = flDistSqr;
  13616. if ( pPlayer->IsPlayerClass( GetPlayerClass()->GetClassIndex() ) )
  13617. {
  13618. bFoundClass = true;
  13619. }
  13620. }
  13621. }
  13622. else if ( !bFoundClass )
  13623. {
  13624. if ( pPlayer->IsPlayerClass( GetPlayerClass()->GetClassIndex() ) )
  13625. {
  13626. pReturnTarget = pPlayer;
  13627. flCurDistSqr = flDistSqr;
  13628. bFoundClass = true;
  13629. }
  13630. }
  13631. }
  13632. if ( !bFoundClass && IsPlayerClass( TF_CLASS_ENGINEER ) )
  13633. {
  13634. // let's spectate our sentry instead, we didn't find any other engineers to spec
  13635. int iNumObjects = GetObjectCount();
  13636. for ( int i=0;i<iNumObjects;i++ )
  13637. {
  13638. CBaseObject *pObj = GetObject(i);
  13639. if ( pObj && pObj->GetType() == OBJ_SENTRYGUN )
  13640. {
  13641. pReturnTarget = pObj;
  13642. }
  13643. }
  13644. }
  13645. return pReturnTarget;
  13646. }
  13647. //-----------------------------------------------------------------------------
  13648. // Purpose:
  13649. //-----------------------------------------------------------------------------
  13650. void CTFPlayer::FindInitialObserverTarget( void )
  13651. {
  13652. // if there is a Boss active, watch him
  13653. if ( TFGameRules()->GetActiveBoss() )
  13654. {
  13655. m_hObserverTarget.Set( TFGameRules()->GetActiveBoss() );
  13656. }
  13657. // If we're on a team (i.e. not a pure observer), try and find
  13658. // a target that'll give the player the most useful information.
  13659. if ( GetTeamNumber() >= FIRST_GAME_TEAM )
  13660. {
  13661. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  13662. if ( pMaster )
  13663. {
  13664. // Has our forward cap point been contested recently?
  13665. int iFarthestPoint = TFGameRules()->GetFarthestOwnedControlPoint( GetTeamNumber(), false );
  13666. if ( iFarthestPoint != -1 )
  13667. {
  13668. float flTime = pMaster->PointLastContestedAt( iFarthestPoint );
  13669. if ( flTime != -1 && flTime > (gpGlobals->curtime - 30) )
  13670. {
  13671. // Does it have an associated viewpoint?
  13672. CBaseEntity *pObserverPoint = gEntList.FindEntityByClassname( NULL, "info_observer_point" );
  13673. while ( pObserverPoint )
  13674. {
  13675. CObserverPoint *pObsPoint = assert_cast<CObserverPoint *>(pObserverPoint);
  13676. if ( pObsPoint && pObsPoint->m_hAssociatedTeamEntity == pMaster->GetControlPoint(iFarthestPoint) )
  13677. {
  13678. if ( IsValidObserverTarget( pObsPoint ) )
  13679. {
  13680. m_hObserverTarget.Set( pObsPoint );
  13681. return;
  13682. }
  13683. }
  13684. pObserverPoint = gEntList.FindEntityByClassname( pObserverPoint, "info_observer_point" );
  13685. }
  13686. }
  13687. }
  13688. // Has the point beyond our farthest been contested lately?
  13689. iFarthestPoint += (ObjectiveResource()->GetBaseControlPointForTeam( GetTeamNumber() ) == 0 ? 1 : -1);
  13690. if ( iFarthestPoint >= 0 && iFarthestPoint < MAX_CONTROL_POINTS )
  13691. {
  13692. float flTime = pMaster->PointLastContestedAt( iFarthestPoint );
  13693. if ( flTime != -1 && flTime > (gpGlobals->curtime - 30) )
  13694. {
  13695. // Try and find a player near that cap point
  13696. CBaseEntity *pCapPoint = pMaster->GetControlPoint(iFarthestPoint);
  13697. if ( pCapPoint )
  13698. {
  13699. CBaseEntity *pTarget = FindNearestObservableTarget( pCapPoint->GetAbsOrigin(), 1500 );
  13700. if ( pTarget )
  13701. {
  13702. m_hObserverTarget.Set( pTarget );
  13703. return;
  13704. }
  13705. }
  13706. }
  13707. }
  13708. }
  13709. }
  13710. // Find the nearest guy near myself
  13711. CBaseEntity *pTarget = FindNearestObservableTarget( GetAbsOrigin(), FLT_MAX );
  13712. if ( pTarget )
  13713. {
  13714. m_hObserverTarget.Set( pTarget );
  13715. }
  13716. }
  13717. //-----------------------------------------------------------------------------
  13718. // Purpose:
  13719. //-----------------------------------------------------------------------------
  13720. void CTFPlayer::ValidateCurrentObserverTarget( void )
  13721. {
  13722. // If our current target is a dead player who's gibbed / died, refind as if
  13723. // we were finding our initial target, so we end up somewhere useful.
  13724. if ( m_hObserverTarget && m_hObserverTarget->IsPlayer() )
  13725. {
  13726. CBasePlayer *player = ToBasePlayer( m_hObserverTarget );
  13727. if ( player->m_lifeState == LIFE_DEAD || player->m_lifeState == LIFE_DYING )
  13728. {
  13729. // if we are coaching, don't switch
  13730. if ( m_hStudent == m_hObserverTarget )
  13731. {
  13732. return;
  13733. }
  13734. // Once we're past the pause after death, find a new target
  13735. if ( (player->GetDeathTime() + DEATH_ANIMATION_TIME ) < gpGlobals->curtime )
  13736. {
  13737. FindInitialObserverTarget();
  13738. }
  13739. return;
  13740. }
  13741. }
  13742. if ( m_hObserverTarget && !m_hObserverTarget->IsPlayer() )
  13743. {
  13744. // can only spectate players in-eye
  13745. if ( m_iObserverMode == OBS_MODE_IN_EYE )
  13746. {
  13747. ForceObserverMode( OBS_MODE_CHASE );
  13748. }
  13749. }
  13750. BaseClass::ValidateCurrentObserverTarget();
  13751. }
  13752. //-----------------------------------------------------------------------------
  13753. // Purpose:
  13754. //-----------------------------------------------------------------------------
  13755. void CTFPlayer::CheckObserverSettings()
  13756. {
  13757. // make sure we are always observing the student
  13758. if ( m_hObserverTarget && m_hStudent && m_hStudent != m_hObserverTarget )
  13759. {
  13760. SetObserverTarget( m_hStudent );
  13761. }
  13762. else if ( TFGameRules() )
  13763. {
  13764. // is there a current entity that is the required spectator target?
  13765. if ( TFGameRules()->GetRequiredObserverTarget() )
  13766. {
  13767. SetObserverTarget( TFGameRules()->GetRequiredObserverTarget() );
  13768. return;
  13769. }
  13770. if ( TFGameRules()->IsPasstimeMode() && g_pPasstimeLogic && (GetObserverMode() == OBS_MODE_POI) )
  13771. {
  13772. CPasstimeBall *pBall = g_pPasstimeLogic->GetBall();
  13773. if ( !pBall || ((m_hObserverTarget.Get() == pBall) && pBall->BOutOfPlay()) )
  13774. {
  13775. FindInitialObserverTarget();
  13776. }
  13777. else if ( !pBall->BOutOfPlay() && (GetObserverTarget() != TFGameRules()->GetObjectiveObserverTarget()) )
  13778. {
  13779. SetObserverTarget( TFGameRules()->GetObjectiveObserverTarget() );
  13780. }
  13781. return;
  13782. }
  13783. // make sure we're not trying to spec the train during a team win
  13784. // if we are, switch to spectating the last control point instead (where the train ended)
  13785. if ( m_hObserverTarget && m_hObserverTarget->IsBaseTrain() && TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
  13786. {
  13787. // find the nearest spectator point to use instead of the train
  13788. CObserverPoint *pObserverPoint = (CObserverPoint *)gEntList.FindEntityByClassname( NULL, "info_observer_point" );
  13789. CObserverPoint *pClosestPoint = NULL;
  13790. float flMinDistance = -1.0f;
  13791. Vector vecTrainOrigin = m_hObserverTarget->GetAbsOrigin();
  13792. while ( pObserverPoint )
  13793. {
  13794. if ( IsValidObserverTarget( pObserverPoint ) )
  13795. {
  13796. float flDist = pObserverPoint->GetAbsOrigin().DistTo( vecTrainOrigin );
  13797. if ( flMinDistance < 0 || flDist < flMinDistance )
  13798. {
  13799. flMinDistance = flDist;
  13800. pClosestPoint = pObserverPoint;
  13801. }
  13802. }
  13803. pObserverPoint = (CObserverPoint *)gEntList.FindEntityByClassname( pObserverPoint, "info_observer_point" );
  13804. }
  13805. if ( pClosestPoint )
  13806. {
  13807. SetObserverTarget( pClosestPoint );
  13808. }
  13809. }
  13810. }
  13811. BaseClass::CheckObserverSettings();
  13812. }
  13813. //-----------------------------------------------------------------------------
  13814. // Purpose:
  13815. //-----------------------------------------------------------------------------
  13816. void CTFPlayer::Touch( CBaseEntity *pOther )
  13817. {
  13818. CTFPlayer *pVictim = ToTFPlayer( pOther );
  13819. if ( pVictim )
  13820. {
  13821. // ACHIEVEMENT_TF_SPY_BUMP_CLOAKED_SPY
  13822. if ( !m_Shared.IsAlly( pVictim ) )
  13823. {
  13824. if ( IsPlayerClass( TF_CLASS_SPY ) && pVictim->IsPlayerClass( TF_CLASS_SPY ) )
  13825. {
  13826. if ( m_Shared.InCond( TF_COND_STEALTHED ) && pVictim->m_Shared.InCond( TF_COND_STEALTHED ) )
  13827. {
  13828. AwardAchievement( ACHIEVEMENT_TF_SPY_BUMP_CLOAKED_SPY );
  13829. }
  13830. }
  13831. }
  13832. CheckUncoveringSpies( pVictim );
  13833. // ACHIEVEMENT_TF_HEAVY_BLOCK_INVULN_HEAVY
  13834. if ( !m_Shared.IsAlly( pVictim ) )
  13835. {
  13836. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) && pVictim->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  13837. {
  13838. CTFTeam *pTeam = GetGlobalTFTeam( GetTeamNumber() );
  13839. if ( pTeam && pTeam->GetRole() == TEAM_ROLE_DEFENDERS )
  13840. {
  13841. if ( m_Shared.InCond( TF_COND_INVULNERABLE ) || m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
  13842. {
  13843. if ( pVictim->m_Shared.InCond( TF_COND_INVULNERABLE ) || pVictim->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
  13844. {
  13845. float flMaxSpeed = 50.0f * 50.0f;
  13846. if ( ( GetAbsVelocity().LengthSqr() < flMaxSpeed ) && ( pVictim->GetAbsVelocity().LengthSqr() < flMaxSpeed ) )
  13847. {
  13848. AwardAchievement( ACHIEVEMENT_TF_HEAVY_BLOCK_INVULN_HEAVY );
  13849. }
  13850. }
  13851. }
  13852. }
  13853. }
  13854. // ****************************************************************************************************************
  13855. // Halloween Karts
  13856. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) && m_flHalloweenKartPushEventTime < gpGlobals->curtime )
  13857. {
  13858. // calculate a force and save it off, it is used on a later frame cause it is to late to apply the force here
  13859. float flImpactForce = GetLocalVelocity().Length();
  13860. if ( flImpactForce > 10.0f )
  13861. {
  13862. float flForceMult = 1.0f;
  13863. Vector vAim = GetLocalVelocity();
  13864. vAim.NormalizeInPlace();
  13865. Vector vOrigin = GetAbsOrigin();
  13866. // Force direction is velocity of the player in the case that this is a head on collison.
  13867. // Trace
  13868. trace_t pTrace;
  13869. Ray_t ray;
  13870. CTraceFilterOnlyNPCsAndPlayer pFilter( this, COLLISION_GROUP_NONE );
  13871. //tf_halloween_kart_impact_lookahead
  13872. //tf_halloween_kart_impact_bounds_scale
  13873. ray.Init( vOrigin, Vector( 0, 0, 16 ) + vOrigin + vAim * tf_halloween_kart_impact_lookahead.GetFloat(), GetPlayerMins() * tf_halloween_kart_impact_bounds_scale.GetFloat(), GetPlayerMaxs() * tf_halloween_kart_impact_bounds_scale.GetFloat() );
  13874. enginetrace->TraceRay( ray, MASK_SOLID, &pFilter, &pTrace );
  13875. Vector vecForceDirection;
  13876. vecForceDirection = vAim;
  13877. vecForceDirection.z += 0.60f;
  13878. vecForceDirection.NormalizeInPlace();
  13879. if ( pTrace.m_pEnt == pVictim )
  13880. {
  13881. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH ) )
  13882. {
  13883. flForceMult *= tf_halloween_kart_boost_impact_force.GetFloat();
  13884. // Stop moving
  13885. SetAbsVelocity( vec3_origin );
  13886. SetCurrentTauntMoveSpeed( 0 );
  13887. m_Shared.RemoveCond( TF_COND_HALLOWEEN_KART_DASH );
  13888. EmitSound( "BumperCar.BumpHard" );
  13889. }
  13890. else
  13891. {
  13892. SetAbsVelocity( GetAbsVelocity() * tf_halloween_kart_impact_feedback.GetFloat() );
  13893. SetCurrentTauntMoveSpeed( GetCurrentTauntMoveSpeed() * tf_halloween_kart_impact_feedback.GetFloat() );
  13894. EmitSound( "BumperCar.Bump" );
  13895. }
  13896. // Invul Crash
  13897. if ( m_Shared.InCond( TF_COND_INVULNERABLE_USER_BUFF ) )
  13898. {
  13899. flForceMult += 0.5f;
  13900. }
  13901. // Apply some kart damage
  13902. //Speed maxes at 800, normally at 300? we want about 10 damage a hit? 10-15?
  13903. int iDamage = (int)( ( flImpactForce / 50.0f + RandomInt( 13, 19 ) ) * tf_halloween_kart_impact_damage.GetFloat() );
  13904. // Apply force to enemy
  13905. vecForceDirection *= flImpactForce * flForceMult * tf_halloween_kart_impact_force.GetFloat();
  13906. pVictim->AddHalloweenKartPushEvent( this, NULL, NULL, vecForceDirection, iDamage );
  13907. }
  13908. else
  13909. {
  13910. DevMsg( "Collision with player not in Trace, %f Force \n", flImpactForce );
  13911. }
  13912. // can only give a kart push event every 0.2 seconds
  13913. if ( vecForceDirection.LengthSqr() > 100.0f )
  13914. {
  13915. m_flHalloweenKartPushEventTime = gpGlobals->curtime + tf_halloween_kart_impact_rate.GetFloat();
  13916. }
  13917. }
  13918. }
  13919. if ( ( m_Shared.GetPercentInvisible() < 0.10f ) &&
  13920. m_Shared.GetCarryingRuneType() == RUNE_PLAGUE &&
  13921. !m_Shared.IsAlly( pVictim ) &&
  13922. !pVictim->m_Shared.IsInvulnerable() &&
  13923. !pVictim->m_Shared.InCond( TF_COND_PLAGUE ) &&
  13924. pVictim->m_Shared.GetCarryingRuneType() != RUNE_RESIST )
  13925. {
  13926. pVictim->m_Shared.AddCond( TF_COND_PLAGUE, PERMANENT_CONDITION, this );
  13927. //Plague transmission event infects nearby eligible players on the same team. Only works for powerup carrier to host, not host to host.
  13928. const Vector& vecPos = pVictim->WorldSpaceCenter();
  13929. for ( int i = 0; i < pVictim->GetTeam()->GetNumPlayers(); i++ )
  13930. {
  13931. CTFPlayer *pTeamMate = ToTFPlayer( pVictim->GetTeam()->GetPlayer( i ) );
  13932. if ( pTeamMate && pTeamMate != pVictim && pTeamMate->IsAlive() && !pTeamMate->m_Shared.IsInvulnerable() && !pTeamMate->m_Shared.InCond( TF_COND_PLAGUE ) && pTeamMate->m_Shared.GetCarryingRuneType() != RUNE_RESIST )
  13933. {
  13934. // Only nearby teammates. Check for this before the more expensive visibility trace
  13935. if ( ( vecPos - pTeamMate->WorldSpaceCenter() ).LengthSqr() < ( 350 * 350 ) )
  13936. {
  13937. // Doesn't go through walls
  13938. if ( pVictim->FVisible( pTeamMate, MASK_SOLID ) )
  13939. {
  13940. pTeamMate->m_Shared.AddCond( TF_COND_PLAGUE, PERMANENT_CONDITION, this );
  13941. CPVSFilter filter( WorldSpaceCenter() );
  13942. Vector vStart = pVictim->EyePosition();
  13943. Vector vEnd = pTeamMate->GetAbsOrigin() + Vector( 0, 0, 56 );
  13944. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  13945. TE_TFParticleEffectComplex( filter, 0.f, "plague_transmission", vStart, QAngle( 0.f, 0.f, 0.f ), NULL, &controlPoint, pTeamMate, PATTACH_CUSTOMORIGIN );
  13946. }
  13947. }
  13948. }
  13949. }
  13950. }
  13951. }
  13952. }
  13953. BaseClass::Touch( pOther );
  13954. }
  13955. //-----------------------------------------------------------------------------
  13956. // Purpose:
  13957. //-----------------------------------------------------------------------------
  13958. void CTFPlayer::RefreshCollisionBounds( void )
  13959. {
  13960. BaseClass::RefreshCollisionBounds();
  13961. SetViewOffset( ( IsDucked() ) ? ( VEC_DUCK_VIEW_SCALED( this ) ) : ( GetClassEyeHeight() ) );
  13962. }
  13963. //-----------------------------------------------------------------------------
  13964. /**
  13965. * Invoked (by UpdateLastKnownArea) when we enter a new nav area (or it is reset to NULL)
  13966. */
  13967. void CTFPlayer::OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea )
  13968. {
  13969. VPROF_BUDGET( "CTFPlayer::OnNavAreaChanged", "NextBot" );
  13970. if ( !IsAlive() || GetTeamNumber() == TEAM_SPECTATOR )
  13971. {
  13972. return;
  13973. }
  13974. if ( leftArea )
  13975. {
  13976. // remove us from old visible set
  13977. NavAreaCollector wasVisible;
  13978. leftArea->ForAllPotentiallyVisibleAreas( wasVisible );
  13979. for( int i=0; i<wasVisible.m_area.Count(); ++i )
  13980. {
  13981. CTFNavArea *area = (CTFNavArea *)wasVisible.m_area[i];
  13982. area->RemovePotentiallyVisibleActor( this );
  13983. }
  13984. }
  13985. if ( enteredArea )
  13986. {
  13987. // add us to new visible set
  13988. // @todo: is it faster to only do this for the areas that changed between sets?
  13989. NavAreaCollector isVisible;
  13990. enteredArea->ForAllPotentiallyVisibleAreas( isVisible );
  13991. for( int i=0; i<isVisible.m_area.Count(); ++i )
  13992. {
  13993. CTFNavArea *area = (CTFNavArea *)isVisible.m_area[i];
  13994. area->AddPotentiallyVisibleActor( this );
  13995. }
  13996. }
  13997. }
  13998. //-----------------------------------------------------------------------------------------------------
  13999. // Return true if the given threat is aiming in our direction
  14000. bool CTFPlayer::IsThreatAimingTowardMe( CBaseEntity *threat, float cosTolerance ) const
  14001. {
  14002. CTFPlayer *player = ToTFPlayer( threat );
  14003. Vector to = GetAbsOrigin() - threat->GetAbsOrigin();
  14004. float threatRange = to.NormalizeInPlace();
  14005. Vector forward;
  14006. if ( player == NULL )
  14007. {
  14008. CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( threat );
  14009. if ( sentry )
  14010. {
  14011. // are we in range?
  14012. if ( threatRange < SENTRY_MAX_RANGE )
  14013. {
  14014. // is it pointing at us?
  14015. AngleVectors( sentry->GetTurretAngles(), &forward );
  14016. if ( DotProduct( to, forward ) > cosTolerance )
  14017. {
  14018. return true;
  14019. }
  14020. }
  14021. }
  14022. // not a player, not a sentry, not a threat?
  14023. return false;
  14024. }
  14025. // is the player pointing at me?
  14026. player->EyeVectors( &forward );
  14027. if ( DotProduct( to, forward ) > cosTolerance )
  14028. {
  14029. return true;
  14030. }
  14031. return false;
  14032. }
  14033. //-----------------------------------------------------------------------------------------------------
  14034. // Return true if the given threat is aiming in our direction and firing its weapon
  14035. bool CTFPlayer::IsThreatFiringAtMe( CBaseEntity *threat ) const
  14036. {
  14037. if ( IsThreatAimingTowardMe( threat ) )
  14038. {
  14039. CTFPlayer *player = ToTFPlayer( threat );
  14040. if ( player )
  14041. {
  14042. return player->IsFiringWeapon();
  14043. }
  14044. CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( threat );
  14045. if ( sentry )
  14046. {
  14047. return sentry->GetTimeSinceLastFired() < 1.0f;
  14048. }
  14049. }
  14050. return false;
  14051. }
  14052. //-----------------------------------------------------------------------------
  14053. // Purpose: Check to see if this player has seen through an enemy spy's disguise
  14054. //-----------------------------------------------------------------------------
  14055. void CTFPlayer::CheckUncoveringSpies( CTFPlayer *pTouchedPlayer )
  14056. {
  14057. // Only uncover enemies
  14058. if ( m_Shared.IsAlly( pTouchedPlayer ) )
  14059. {
  14060. return;
  14061. }
  14062. // Only uncover if they're stealthed
  14063. if ( !pTouchedPlayer->m_Shared.InCond( TF_COND_STEALTHED ) )
  14064. {
  14065. return;
  14066. }
  14067. // pulse their invisibility
  14068. pTouchedPlayer->m_Shared.OnSpyTouchedByEnemy();
  14069. }
  14070. //-----------------------------------------------------------------------------
  14071. // Purpose:
  14072. //-----------------------------------------------------------------------------
  14073. void CTFPlayer::DoNoiseMaker( void )
  14074. {
  14075. if ( gpGlobals->curtime < m_Shared.GetNextNoiseMakerTime() )
  14076. return;
  14077. CSteamID steamIDForPlayer;
  14078. GetSteamID( &steamIDForPlayer );
  14079. // Check to see that we have a noise maker item equipped. We intentionally
  14080. // want to check this to fix the infinite noise maker bugs.
  14081. CEconItemView *pItem = GetEquippedItemForLoadoutSlot( LOADOUT_POSITION_ACTION );
  14082. if ( !pItem )
  14083. return;
  14084. int iUnlimitedQuantity = 0;
  14085. CALL_ATTRIB_HOOK_INT( iUnlimitedQuantity, unlimited_quantity );
  14086. if ( pItem->GetItemQuantity() <= 0 && !iUnlimitedQuantity )
  14087. return;
  14088. perteamvisuals_t* vis = pItem->GetStaticData()->GetPerTeamVisual( 0 );
  14089. if ( !vis )
  14090. return;
  14091. int iNumSounds = 0;
  14092. for ( int i=0; i<MAX_VISUALS_CUSTOM_SOUNDS; ++i )
  14093. {
  14094. if ( vis->pszCustomSounds[i] )
  14095. iNumSounds++;
  14096. }
  14097. if ( iNumSounds == 0 )
  14098. return;
  14099. int rand = RandomInt( 0, iNumSounds-1 );
  14100. float flSoundLength = 0;
  14101. EmitSound_t params;
  14102. params.m_flSoundTime = 0;
  14103. params.m_pSoundName = vis->pszCustomSounds[rand];
  14104. params.m_pflSoundDuration = &flSoundLength;
  14105. CPASFilter filter( GetAbsOrigin() );
  14106. EmitSound( filter, entindex(), params );
  14107. // Add a particle effect.
  14108. const char *particleEffectName = pItem->GetStaticData()->GetParticleEffect( TEAM_UNASSIGNED );
  14109. if ( particleEffectName )
  14110. {
  14111. TE_TFParticleEffect( filter, 0.0, particleEffectName, PATTACH_POINT_FOLLOW, this, "head" );
  14112. }
  14113. float flDelay = 1.0f;
  14114. // Duck Badge Cooldown is based on badge level. Noisemaker is more like an easter egg
  14115. CSchemaAttributeDefHandle pAttr_DuckLevelBadge( "duck badge level" );
  14116. uint32 iDuckBadgeLevel = 0;
  14117. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttr_DuckLevelBadge, &iDuckBadgeLevel ) )
  14118. {
  14119. flDelay = 5.0f;
  14120. }
  14121. // Throttle the usage rate to sound duration plus some dead time.
  14122. m_Shared.SetNextNoiseMakerTime( gpGlobals->curtime + flSoundLength + flDelay );
  14123. }
  14124. //-----------------------------------------------------------------------------
  14125. // Purpose: Finds an open space for a high five partner. flTolerance specifies the maximum amount that should be allowed underneath position.
  14126. //-----------------------------------------------------------------------------
  14127. bool CTFPlayer::FindOpenTauntPartnerPosition( CEconItemView *pEconItemView, Vector &position, float *flTolerance )
  14128. {
  14129. if ( !pEconItemView || !pEconItemView->IsValid() )
  14130. return false;
  14131. const GameItemDefinition_t *pItemDef = pEconItemView->GetItemDefinition();
  14132. if ( !pItemDef || !pItemDef->GetTauntData() )
  14133. {
  14134. position = GetAbsOrigin();
  14135. *flTolerance = tf_highfive_height_tolerance.GetFloat();
  14136. return false;
  14137. }
  14138. const float flTauntSeparationForwardDistance = tf_highfive_separation_forward.GetFloat() != 0 ? tf_highfive_separation_forward.GetFloat() : pItemDef->GetTauntData()->GetTauntSeparationForwardDistance();
  14139. const float flTauntSeparationRightDistance = tf_highfive_separation_right.GetFloat() != 0 ? tf_highfive_separation_right.GetFloat() : pItemDef->GetTauntData()->GetTauntSeparationRightDistance();
  14140. bool ret = true;
  14141. Vector forward, right;
  14142. AngleVectors( GetAbsAngles(), &forward, &right, NULL );
  14143. Vector vecStart = GetAbsOrigin();
  14144. Vector vecEnd = vecStart + ( forward * flTauntSeparationForwardDistance ) + ( right * flTauntSeparationRightDistance );
  14145. *flTolerance = tf_highfive_height_tolerance.GetFloat();
  14146. trace_t result;
  14147. CTraceFilterIgnoreTeammates filter( this, COLLISION_GROUP_NONE, GetAllowedTauntPartnerTeam() );
  14148. UTIL_TraceHull( vecStart, vecEnd + ( forward * 2 ), VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result );
  14149. if ( result.DidHit() )
  14150. {
  14151. // something's directly in front of us, but let's allow for a little bit of variation since we might be standing on an uneven displacement
  14152. trace_t result2;
  14153. vecStart = GetAbsOrigin() + Vector( 0, 0, *flTolerance );
  14154. vecEnd = vecStart + ( forward * flTauntSeparationForwardDistance ) + ( right * flTauntSeparationRightDistance );
  14155. UTIL_TraceHull( vecStart, vecEnd + ( forward * 2 ), VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result2 );
  14156. // Now we can allow for twice the space underneath us.
  14157. *flTolerance *= 2;
  14158. if ( result2.DidHit() )
  14159. {
  14160. // Not enough space in front of us.
  14161. ret = false;
  14162. }
  14163. else
  14164. {
  14165. position = vecEnd;
  14166. }
  14167. }
  14168. else
  14169. {
  14170. position = vecEnd;
  14171. }
  14172. if( ret )
  14173. {
  14174. Vector vecStartCenter = WorldSpaceCenter();
  14175. // Scale up how far we test. Dont even let them get close.
  14176. Vector vecEndSuperSafe = vecStartCenter + ( forward * flTauntSeparationForwardDistance * 2.f ) + ( right * flTauntSeparationRightDistance );
  14177. // Dont allow crossing through the spawn room visualizers
  14178. ret = !PointsCrossRespawnRoomVisualizer( vecStartCenter, vecEndSuperSafe );
  14179. }
  14180. return ret;
  14181. }
  14182. //-----------------------------------------------------------------------------
  14183. // Purpose:
  14184. //-----------------------------------------------------------------------------
  14185. bool CTFPlayer::IsAllowedToInitiateTauntWithPartner( CEconItemView *pEconItemView, char *pszErrorMessage, int cubErrorMessage )
  14186. {
  14187. Vector vecEnd;
  14188. float flTolerance;
  14189. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  14190. return true;
  14191. bool ret = FindOpenTauntPartnerPosition( pEconItemView, vecEnd, &flTolerance );
  14192. // Check that there isn't too much space underneath the destination.
  14193. if ( ret )
  14194. {
  14195. trace_t result3;
  14196. CTraceFilterIgnoreTeammates filter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  14197. UTIL_TraceHull( vecEnd, vecEnd - Vector( 0, 0, flTolerance ), VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result3 );
  14198. if ( !result3.DidHit() )
  14199. {
  14200. if ( pszErrorMessage && cubErrorMessage > 0 )
  14201. {
  14202. V_strncpy( pszErrorMessage, "#TF_PartnerTaunt_TooHigh", cubErrorMessage );
  14203. }
  14204. ret = false;
  14205. }
  14206. }
  14207. else if ( pszErrorMessage && cubErrorMessage > 0 )
  14208. {
  14209. V_strncpy( pszErrorMessage, "#TF_PartnerTaunt_Blocked", cubErrorMessage );
  14210. }
  14211. return ret;
  14212. }
  14213. //-----------------------------------------------------------------------------
  14214. // Purpose:
  14215. //-----------------------------------------------------------------------------
  14216. bool CTFPlayer::IsWormsGearEquipped( void ) const
  14217. {
  14218. // If we have the Worms Gear equipped, play their custom sound
  14219. static CSchemaItemDefHandle ppItemDefWearables[] = { CSchemaItemDefHandle( "Worms Gear" ) };
  14220. return HasWearablesEquipped( ppItemDefWearables, ARRAYSIZE( ppItemDefWearables ) );
  14221. }
  14222. //-----------------------------------------------------------------------------
  14223. // Purpose:
  14224. //-----------------------------------------------------------------------------
  14225. bool CTFPlayer::IsRobotCostumeEquipped( void ) const
  14226. {
  14227. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_SOLDIER )
  14228. return false;
  14229. static CSchemaItemDefHandle ppItemDefWearables[] = { CSchemaItemDefHandle( "Idiot Box" ), CSchemaItemDefHandle( "Steel Pipes" ), CSchemaItemDefHandle( "Shoestring Budget" ) };
  14230. return HasWearablesEquipped( ppItemDefWearables, ARRAYSIZE( ppItemDefWearables ) );
  14231. }
  14232. //-----------------------------------------------------------------------------
  14233. // Purpose:
  14234. //-----------------------------------------------------------------------------
  14235. bool CTFPlayer::IsDemowolf( void ) const
  14236. {
  14237. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_DEMOMAN )
  14238. return false;
  14239. static CSchemaItemDefHandle ppItemDefWearables[] = { CSchemaItemDefHandle( "Hair of the Dog" ), CSchemaItemDefHandle( "Scottish Snarl" ), CSchemaItemDefHandle( "Pickled Paws" ) };
  14240. return HasWearablesEquipped( ppItemDefWearables, ARRAYSIZE( ppItemDefWearables ) );
  14241. }
  14242. //-----------------------------------------------------------------------------
  14243. // Purpose:
  14244. //-----------------------------------------------------------------------------
  14245. bool CTFPlayer::IsFrankenHeavy( void ) const
  14246. {
  14247. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_HEAVYWEAPONS )
  14248. return false;
  14249. static CSchemaItemDefHandle ppItemDefWearables[] = { CSchemaItemDefHandle( "Can Opener" ), CSchemaItemDefHandle( "Soviet Stitch-Up" ), CSchemaItemDefHandle( "Steel-Toed Stompers" ) };
  14250. return HasWearablesEquipped( ppItemDefWearables, ARRAYSIZE( ppItemDefWearables ) );
  14251. }
  14252. //-----------------------------------------------------------------------------
  14253. // Purpose:
  14254. //-----------------------------------------------------------------------------
  14255. bool CTFPlayer::IsFairyHeavy( void ) const
  14256. {
  14257. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_HEAVYWEAPONS )
  14258. return false;
  14259. static CSchemaItemDefHandle ppItemDefWearables[] = { CSchemaItemDefHandle( "The Grand Duchess Tutu" ), CSchemaItemDefHandle( "The Grand Duchess Fairy Wings" ), CSchemaItemDefHandle( "The Grand Duchess Tiara" ) };
  14260. return HasWearablesEquipped( ppItemDefWearables, ARRAYSIZE( ppItemDefWearables ) );
  14261. }
  14262. //-----------------------------------------------------------------------------
  14263. // Purpose:
  14264. //-----------------------------------------------------------------------------
  14265. bool CTFPlayer::IsZombieCostumeEquipped( void ) const
  14266. {
  14267. int iZombie = 0;
  14268. CALL_ATTRIB_HOOK_INT( iZombie, zombiezombiezombiezombie );
  14269. return iZombie != 0;
  14270. }
  14271. //-----------------------------------------------------------------------------
  14272. // Purpose:
  14273. //-----------------------------------------------------------------------------
  14274. bool CTFPlayer::HasWearablesEquipped( const CSchemaItemDefHandle *ppItemDefs, int nWearables ) const
  14275. {
  14276. for ( int i = 0; i < nWearables; i++ )
  14277. {
  14278. const CEconItemDefinition *pItemDef = ppItemDefs[i];
  14279. // Backwards because our wearable items are probably sitting in our cosmetic slots near
  14280. // the end of our list.
  14281. bool bHasWearable = false;
  14282. FOR_EACH_VEC_BACK( m_hMyWearables, wbl )
  14283. {
  14284. CEconWearable *pWearable = m_hMyWearables[wbl];
  14285. if ( pWearable &&
  14286. pWearable->GetAttributeContainer()->GetItem() &&
  14287. pWearable->GetAttributeContainer()->GetItem()->GetItemDefinition() == pItemDef )
  14288. {
  14289. bHasWearable = true;
  14290. break;
  14291. }
  14292. }
  14293. if ( !bHasWearable )
  14294. {
  14295. return false;
  14296. }
  14297. }
  14298. return true;
  14299. }
  14300. //-----------------------------------------------------------------------------
  14301. // Purpose: Returns the current concept for press-and-hold taunts or MP_CONCEPT_NONE if none is available.
  14302. //-----------------------------------------------------------------------------
  14303. int CTFPlayer::GetTauntConcept( CEconItemDefinition *pItemDef )
  14304. {
  14305. for ( int i=0; i<pItemDef->GetNumAnimations( GetTeamNumber() ); ++i )
  14306. {
  14307. animation_on_wearable_t* pAnim = pItemDef->GetAnimationData( GetTeamNumber(), i );
  14308. if ( pAnim && pAnim->pszActivity &&
  14309. !Q_stricmp( pAnim->pszActivity, "taunt_concept" ) )
  14310. {
  14311. const char* pszConcept = pAnim->pszReplacement;
  14312. if ( !pszConcept )
  14313. return true;
  14314. return GetMPConceptIndexFromString( pszConcept );
  14315. }
  14316. }
  14317. return MP_CONCEPT_NONE;
  14318. }
  14319. //-----------------------------------------------------------------------------
  14320. // Purpose:
  14321. //-----------------------------------------------------------------------------
  14322. bool CTFPlayer::PlayTauntSceneFromItem( CEconItemView *pEconItemView )
  14323. {
  14324. if ( !pEconItemView )
  14325. return false;
  14326. if ( !IsAllowedToTaunt() )
  14327. return false;
  14328. const GameItemDefinition_t *pItemDef = pEconItemView->GetItemDefinition();
  14329. if ( !pItemDef )
  14330. return false;
  14331. CTFTauntInfo *pTauntData = pItemDef->GetTauntData();
  14332. if ( !pTauntData )
  14333. return false;
  14334. int iClass = GetPlayerClass()->GetClassIndex();
  14335. // If we didn't find any custom taunts, then we're done
  14336. if ( pTauntData->GetIntroSceneCount( iClass ) == 0 )
  14337. {
  14338. return false;
  14339. }
  14340. int iScene = RandomInt( 0, pTauntData->GetIntroSceneCount( iClass ) - 1 );
  14341. const char* pszScene = pTauntData->GetIntroScene( iClass, iScene );
  14342. if ( pszScene )
  14343. {
  14344. int iTauntIndex = TAUNT_MISC_ITEM;
  14345. int iTauntConcept = 0;
  14346. // check if this is a long taunt
  14347. static CSchemaAttributeDefHandle pAttrDef_TauntPressAndHold( "taunt is press and hold" );
  14348. attrib_value_t iLongTaunt = 0;
  14349. if ( pEconItemView->FindAttribute( pAttrDef_TauntPressAndHold, &iLongTaunt ) && iLongTaunt != 0 )
  14350. {
  14351. iTauntIndex = TAUNT_LONG;
  14352. iTauntConcept = pTauntData->IsPartnerTaunt() ? MP_CONCEPT_PARTNER_TAUNT_READY : iTauntConcept;
  14353. m_bIsTauntInitiator = true;
  14354. ParseSharedTauntDataFromEconItemView( pEconItemView );
  14355. /*cant we just network over the "taunting item id", since client and server both know all the item defs,
  14356. then they can both look at attributes and we dont need to keep networking more and more stuff?*/
  14357. // check if this taunt can be mimic by other players
  14358. static CSchemaAttributeDefHandle pAttrDef_TauntMimic( "taunt mimic" );
  14359. attrib_value_t iTauntMimic = 0;
  14360. pEconItemView->FindAttribute( pAttrDef_TauntMimic, &iTauntMimic );
  14361. m_bTauntMimic = iTauntMimic != 0;
  14362. // check if we can initiate partner taunt (ignore mimic taunt to allow Conga initiation)
  14363. char szClientError[64];
  14364. if ( !m_bTauntMimic && pTauntData->IsPartnerTaunt() && !IsAllowedToInitiateTauntWithPartner( pEconItemView, szClientError, ARRAYSIZE( szClientError ) ) )
  14365. {
  14366. CSingleUserRecipientFilter filter( this );
  14367. EmitSound_t params;
  14368. params.m_pSoundName = "Player.DenyWeaponSelection";
  14369. EmitSound( filter, entindex(), params );
  14370. TFGameRules()->SendHudNotification( filter, szClientError, "ico_notify_partner_taunt" );
  14371. return false;
  14372. }
  14373. }
  14374. // Store this off so eventually we can let clients know which item ID is doing this taunt.
  14375. m_iTauntItemDefIndex = pEconItemView->GetItemDefIndex();
  14376. m_TauntEconItemView = *pEconItemView;
  14377. // Should we play a sound?
  14378. m_strTauntSoundName = "";
  14379. m_flTauntSoundTime = 0.f;
  14380. static CSchemaAttributeDefHandle pAttrDef_TauntSuccessSound( "taunt success sound" );
  14381. CAttribute_String attrTauntSuccessSound;
  14382. if ( pEconItemView->FindAttribute( pAttrDef_TauntSuccessSound, &attrTauntSuccessSound ) )
  14383. {
  14384. const char* pszTauntSoundName = attrTauntSuccessSound.value().c_str();
  14385. Assert( pszTauntSoundName && *pszTauntSoundName );
  14386. if ( pszTauntSoundName && *pszTauntSoundName )
  14387. {
  14388. m_strTauntSoundName = pszTauntSoundName;
  14389. static CSchemaAttributeDefHandle pAttrDef_TauntSuccessSoundOffset( "taunt success sound offset" );
  14390. attrib_value_t attrTauntSoundOffset = 0;
  14391. pEconItemView->FindAttribute( pAttrDef_TauntSuccessSoundOffset, &attrTauntSoundOffset );
  14392. float flTauntSoundOffset = (float&)attrTauntSoundOffset;
  14393. m_flTauntSoundTime = gpGlobals->curtime + flTauntSoundOffset;
  14394. }
  14395. }
  14396. // Should we play a looping sound?
  14397. m_flTauntSoundLoopTime = 0.f;
  14398. Assert( m_strTauntSoundLoopName.IsEmpty() );
  14399. m_strTauntSoundLoopName = "";
  14400. static CSchemaAttributeDefHandle pAttrDef_TauntSuccessSoundLoop( "taunt success sound loop" );
  14401. CAttribute_String attrTauntSuccessSoundLoop;
  14402. if ( pEconItemView->FindAttribute( pAttrDef_TauntSuccessSoundLoop, &attrTauntSuccessSoundLoop ) )
  14403. {
  14404. const char* pszTauntSoundLoopName = attrTauntSuccessSoundLoop.value().c_str();
  14405. Assert( pszTauntSoundLoopName && *pszTauntSoundLoopName );
  14406. if ( pszTauntSoundLoopName && *pszTauntSoundLoopName )
  14407. {
  14408. // play the looping sounds using the envelope controller
  14409. m_strTauntSoundLoopName = pszTauntSoundLoopName;
  14410. static CSchemaAttributeDefHandle pAttrDef_TauntSuccessSoundLoopOffset( "taunt success sound loop offset" );
  14411. attrib_value_t attrTauntSoundLoopOffset = 0;
  14412. pEconItemView->FindAttribute( pAttrDef_TauntSuccessSoundLoopOffset, &attrTauntSoundLoopOffset );
  14413. float flTauntSoundLoopOffset = (float&)attrTauntSoundLoopOffset;
  14414. m_flTauntSoundLoopTime = gpGlobals->curtime + flTauntSoundLoopOffset;
  14415. }
  14416. }
  14417. m_iTauntAttack = TAUNTATK_NONE;
  14418. m_flTauntAttackTime = 0.f;
  14419. static CSchemaAttributeDefHandle pAttrDef_TauntAttackName( "taunt attack name" );
  14420. const char* pszTauntAttackName = NULL;
  14421. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItemDef, pAttrDef_TauntAttackName, &pszTauntAttackName ) )
  14422. {
  14423. m_iTauntAttack = GetTauntAttackByName( pszTauntAttackName );
  14424. }
  14425. static CSchemaAttributeDefHandle pAttrDef_TauntAttackTime( "taunt attack time" );
  14426. float flTauntAttackTime = 0.f;
  14427. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItemDef, pAttrDef_TauntAttackTime, &flTauntAttackTime ) )
  14428. {
  14429. m_flTauntAttackTime = gpGlobals->curtime + flTauntAttackTime;
  14430. }
  14431. m_iPreTauntWeaponSlot = -1;
  14432. if ( GetActiveWeapon() )
  14433. {
  14434. m_iPreTauntWeaponSlot = GetActiveWeapon()->GetSlot();
  14435. }
  14436. static CSchemaAttributeDefHandle pAttrDef_TauntForceWeaponSlot( "taunt force weapon slot" );
  14437. const char* pszTauntForceWeaponSlotName = NULL;
  14438. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItemDef, pAttrDef_TauntForceWeaponSlot, &pszTauntForceWeaponSlotName ) )
  14439. {
  14440. int iForceWeaponSlot = StringFieldToInt( pszTauntForceWeaponSlotName, GetItemSchema()->GetWeaponTypeSubstrings() );
  14441. Weapon_Switch( Weapon_GetSlot( iForceWeaponSlot ) );
  14442. }
  14443. m_bInitTaunt = true;
  14444. // Allow voice commands, etc to be interrupted.
  14445. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  14446. Assert( pExpresser );
  14447. pExpresser->AllowMultipleScenes();
  14448. float flSceneDuration = PlayScene( pszScene );
  14449. OnTauntSucceeded( pszScene, iTauntIndex, iTauntConcept );
  14450. m_flNextAllowTauntRemapInputTime = iTauntIndex == TAUNT_LONG ? gpGlobals->curtime + flSceneDuration : -1.f;
  14451. pExpresser->DisallowMultipleScenes();
  14452. const char *pszTauntProp = pTauntData->GetProp( iClass );
  14453. if ( pszTauntProp )
  14454. {
  14455. const char *pszTauntPropScene = pTauntData->GetPropIntroScene( iClass );
  14456. if ( pszTauntPropScene )
  14457. {
  14458. CTFTauntProp *pProp = static_cast< CTFTauntProp * >( CreateEntityByName( "tf_taunt_prop" ) );
  14459. if ( pProp )
  14460. {
  14461. pProp->SetModel( pszTauntProp );
  14462. pProp->m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1;
  14463. DispatchSpawn( pProp );
  14464. pProp->SetAbsOrigin( GetAbsOrigin() );
  14465. pProp->SetAbsAngles( GetAbsAngles() );
  14466. pProp->SetEFlags( EFL_FORCE_CHECK_TRANSMIT );
  14467. pProp->PlayScene( pszTauntPropScene );
  14468. m_hTauntProp = pProp;
  14469. }
  14470. }
  14471. else
  14472. {
  14473. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  14474. if ( pWeapon && pWeapon->HideAttachmentsAndShowBodygroupsWhenPerformingWeaponIndependentTaunt() )
  14475. {
  14476. // If there's no prop scene, our weapon is being repurposed
  14477. pWeapon->SetIsBeingRepurposedForTaunt( true );
  14478. }
  14479. }
  14480. }
  14481. // check for achievement
  14482. static CSchemaItemDefHandle congaTaunt( "Conga Taunt" );
  14483. if ( pEconItemView->GetItemDefinition() == congaTaunt )
  14484. {
  14485. CUtlVector< CTFPlayer * > vecPlayers;
  14486. CollectPlayers( &vecPlayers, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
  14487. CollectPlayers( &vecPlayers, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
  14488. CUtlVector< CTFPlayer * > vecCongaLine;
  14489. FOR_EACH_VEC( vecPlayers, i )
  14490. {
  14491. CTFPlayer *pPlayer = vecPlayers[i];
  14492. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  14493. {
  14494. // is this player doing the Conga?
  14495. if ( pPlayer->GetTauntEconItemView() && ( pPlayer->GetTauntEconItemView()->GetItemDefinition() == congaTaunt ) )
  14496. {
  14497. vecCongaLine.AddToTail( pPlayer );
  14498. }
  14499. }
  14500. }
  14501. if ( vecCongaLine.Count() >= 10 )
  14502. {
  14503. FOR_EACH_VEC( vecCongaLine, i )
  14504. {
  14505. CTFPlayer *pPlayer = vecCongaLine[i];
  14506. if ( pPlayer )
  14507. {
  14508. pPlayer->AwardAchievement( ACHIEVEMENT_TF_TAUNT_CONGA_LINE );
  14509. }
  14510. }
  14511. }
  14512. }
  14513. // override FOV
  14514. m_iPreTauntFOV = GetFOV();
  14515. if ( pTauntData->GetFOV() != 0 )
  14516. {
  14517. SetFOV( this, pTauntData->GetFOV() );
  14518. }
  14519. #ifdef STAGING_ONLY
  14520. if ( tf_tauntcam_fov_override.GetInt() != 0 )
  14521. {
  14522. SetFOV( this, tf_tauntcam_fov_override.GetInt() );
  14523. }
  14524. #endif // STAGING_ONLY
  14525. m_TauntStage = TAUNT_INTRO;
  14526. return true;
  14527. }
  14528. return false;
  14529. }
  14530. //-----------------------------------------------------------------------------
  14531. // Purpose:
  14532. //-----------------------------------------------------------------------------
  14533. float CTFPlayer::PlayTauntRemapInputScene()
  14534. {
  14535. CTFTauntInfo *pTaunt = m_TauntEconItemView.GetStaticData()->GetTauntData();
  14536. if ( !pTaunt )
  14537. {
  14538. return -1.f;
  14539. }
  14540. if ( m_TauntStage != TAUNT_INTRO )
  14541. {
  14542. return -1.f;
  14543. }
  14544. int iClass = GetPlayerClass()->GetClassIndex();
  14545. const char *pszCurrentSceneFileName = GetSceneFilename( m_hTauntScene );
  14546. const char *pszSceneName = NULL;
  14547. for ( int iButtonIndex=0; iButtonIndex<pTaunt->GetTauntInputRemapCount(); ++iButtonIndex )
  14548. {
  14549. const CTFTauntInfo::TauntInputRemap_t& tauntRemap = pTaunt->GetTauntInputRemapScene( iButtonIndex );
  14550. if ( tauntRemap.m_vecButtonPressedScenes[iClass].IsEmpty() )
  14551. continue;
  14552. if ( m_afButtonPressed & tauntRemap.m_iButton )
  14553. {
  14554. int iRandomTaunt = RandomInt( 0, tauntRemap.m_vecButtonPressedScenes[iClass].Count() - 1 );
  14555. pszSceneName = tauntRemap.m_vecButtonPressedScenes[iClass][iRandomTaunt];
  14556. break;
  14557. }
  14558. const char *pszPressedScene = tauntRemap.m_vecButtonPressedScenes[iClass][0];
  14559. if ( m_nButtons & tauntRemap.m_iButton )
  14560. {
  14561. // already in this scene, try again later for next state
  14562. if ( FStrEq( pszCurrentSceneFileName, pszPressedScene ) )
  14563. {
  14564. return 0.f;
  14565. }
  14566. pszSceneName = pszPressedScene;
  14567. break;
  14568. }
  14569. else if ( FStrEq( pszCurrentSceneFileName, pszPressedScene ) && !tauntRemap.m_vecButtonReleasedScenes[iClass].IsEmpty() )
  14570. {
  14571. int iRandomTaunt = RandomInt( 0, tauntRemap.m_vecButtonReleasedScenes[iClass].Count() - 1 );
  14572. pszSceneName = tauntRemap.m_vecButtonReleasedScenes[iClass][iRandomTaunt];
  14573. break;
  14574. }
  14575. }
  14576. if ( pszSceneName )
  14577. {
  14578. StopScriptedScene( this, m_hTauntScene );
  14579. m_hTauntScene = NULL;
  14580. CMultiplayer_Expresser *pInitiatorExpresser = GetMultiplayerExpresser();
  14581. Assert( pInitiatorExpresser );
  14582. pInitiatorExpresser->AllowMultipleScenes();
  14583. // extend initiator's taunt duration to include actual high five
  14584. m_bInitTaunt = true;
  14585. float flSceneDuration = PlayScene( pszSceneName );
  14586. m_bInitTaunt = false;
  14587. pInitiatorExpresser->DisallowMultipleScenes();
  14588. return flSceneDuration;
  14589. }
  14590. return 0.f;
  14591. }
  14592. //-----------------------------------------------------------------------------
  14593. // Purpose:
  14594. //-----------------------------------------------------------------------------
  14595. void CTFPlayer::OnTauntSucceeded( const char* pszSceneName, int iTauntIndex /*= 0*/, int iTauntConcept /*= 0*/ )
  14596. {
  14597. float flDuration = GetSceneDuration( pszSceneName ) + 0.2f;
  14598. float flDurationMod = 1;
  14599. CALL_ATTRIB_HOOK_FLOAT( flDurationMod, mult_gesture_time ); // Modify by attributes.
  14600. flDuration /= flDurationMod;
  14601. // Set player state as taunting.
  14602. m_Shared.m_iTauntIndex = iTauntIndex;
  14603. m_Shared.m_iTauntConcept.Set( iTauntConcept );
  14604. m_flTauntStartTime = gpGlobals->curtime;
  14605. const itemid_t unTauntSourceItemID = m_TauntEconItemView.IsValid() ? m_TauntEconItemView.GetItemID() : INVALID_ITEM_ID;
  14606. m_Shared.m_unTauntSourceItemID_Low = unTauntSourceItemID & 0xffffffff;
  14607. m_Shared.m_unTauntSourceItemID_High = (unTauntSourceItemID >> 32) & 0xffffffff;
  14608. m_Shared.AddCond( TF_COND_TAUNTING );
  14609. if ( iTauntIndex == TAUNT_LONG )
  14610. {
  14611. m_flTauntRemoveTime = gpGlobals->curtime;
  14612. m_bAllowedToRemoveTaunt = false;
  14613. if ( iTauntConcept == MP_CONCEPT_PARTNER_TAUNT_READY )
  14614. {
  14615. GetReadyToTauntWithPartner();
  14616. }
  14617. m_flTauntYaw = BodyAngles().y;
  14618. }
  14619. else
  14620. {
  14621. m_flTauntRemoveTime = gpGlobals->curtime + flDuration;
  14622. m_bAllowedToRemoveTaunt = true;
  14623. }
  14624. m_angTauntCamera = EyeAngles();
  14625. // Slam velocity to zero.
  14626. SetAbsVelocity( vec3_origin );
  14627. // play custom set taunt particle if we have a full set equipped
  14628. if ( IsPlayerClass( TF_CLASS_SPY ) )
  14629. {
  14630. // FIX ME: We should be using string attribute type instead of float when we add code support to it
  14631. // Hand Coded for this effect which may change later
  14632. int iCustomTauntParticle = 0;
  14633. CALL_ATTRIB_HOOK_INT( iCustomTauntParticle, custom_taunt_particle_attr );
  14634. if ( iCustomTauntParticle )
  14635. {
  14636. DispatchParticleEffect( "set_taunt_saharan_spy", PATTACH_ABSORIGIN_FOLLOW, this );
  14637. }
  14638. }
  14639. // set initial taunt yaw to make sure that the client anim not off because of lag
  14640. SetTauntYaw( GetAbsAngles()[YAW] );
  14641. m_vecTauntStartPosition = GetAbsOrigin();
  14642. // Strange Taunts
  14643. EconItemInterface_OnOwnerKillEaterEventNoPartner( &m_TauntEconItemView, this, kKillEaterEvent_TauntsPerformed );
  14644. }
  14645. //-----------------------------------------------------------------------------
  14646. // Purpose:
  14647. //-----------------------------------------------------------------------------
  14648. void CTFPlayer::Taunt( taunts_t iTauntIndex, int iTauntConcept )
  14649. {
  14650. if ( !IsAllowedToTaunt() )
  14651. return;
  14652. if ( iTauntIndex == TAUNT_LONG )
  14653. {
  14654. AssertMsg( false, "Long Taunt should be using the new system which reads scene names from item definitions" );
  14655. return;
  14656. }
  14657. // Heavies can purchase a rage-based knockback+stun effect in MvM,
  14658. // so ignore taunt and activate rage if we're at full rage
  14659. if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  14660. {
  14661. if ( GetActiveTFWeapon() && GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_MINIGUN )
  14662. {
  14663. int iRage = 0;
  14664. CALL_ATTRIB_HOOK_INT( iRage, generate_rage_on_dmg );
  14665. if ( iRage )
  14666. {
  14667. if ( m_Shared.GetRageMeter() >= 100.f )
  14668. {
  14669. m_Shared.m_bRageDraining = true;
  14670. EmitSound( "Heavy.Battlecry03" );
  14671. return;
  14672. }
  14673. if ( m_Shared.IsRageDraining() )
  14674. return;
  14675. }
  14676. }
  14677. }
  14678. // Allow voice commands, etc to be interrupted.
  14679. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  14680. Assert( pExpresser );
  14681. pExpresser->AllowMultipleScenes();
  14682. m_hTauntItem = NULL;
  14683. m_bInitTaunt = true;
  14684. char szResponse[AI_Response::MAX_RESPONSE_NAME];
  14685. bool bTauntSucceeded = false;
  14686. switch ( iTauntIndex )
  14687. {
  14688. case TAUNT_SHOW_ITEM:
  14689. iTauntConcept = MP_CONCEPT_PLAYER_SHOW_ITEM_TAUNT;
  14690. break;
  14691. // use the concept specified for these two
  14692. case TAUNT_MISC_ITEM:
  14693. case TAUNT_SPECIAL:
  14694. break;
  14695. default:
  14696. case TAUNT_BASE_WEAPON:
  14697. iTauntConcept = MP_CONCEPT_PLAYER_TAUNT;
  14698. break;
  14699. };
  14700. bTauntSucceeded = SpeakConceptIfAllowed( iTauntConcept, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME );
  14701. if ( bTauntSucceeded )
  14702. {
  14703. OnTauntSucceeded( szResponse, iTauntIndex, iTauntConcept );
  14704. }
  14705. else
  14706. {
  14707. m_bInitTaunt = false;
  14708. }
  14709. pExpresser->DisallowMultipleScenes();
  14710. m_flTauntAttackTime = 0;
  14711. m_iTauntAttack = TAUNTATK_NONE;
  14712. if ( !bTauntSucceeded )
  14713. return;
  14714. // should we play a sound?
  14715. CAttribute_String attrCosmeticTauntSound;
  14716. CALL_ATTRIB_HOOK_STRING( attrCosmeticTauntSound, cosmetic_taunt_sound );
  14717. const char* pszTauntSoundName = attrCosmeticTauntSound.value().c_str();
  14718. if ( pszTauntSoundName && *pszTauntSoundName )
  14719. {
  14720. EmitSound( pszTauntSoundName );
  14721. }
  14722. if ( iTauntIndex == TAUNT_SHOW_ITEM )
  14723. {
  14724. m_flTauntAttackTime = gpGlobals->curtime + 1.5;
  14725. m_iTauntAttack = TAUNTATK_SHOW_ITEM;
  14726. return;
  14727. }
  14728. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  14729. if ( iTauntIndex == TAUNT_BASE_WEAPON )
  14730. {
  14731. // phlogistinator
  14732. if ( IsPlayerClass( TF_CLASS_PYRO ) && m_Shared.GetRageMeter() >= 100.0f &&
  14733. StringHasPrefix( szResponse, "scenes/player/pyro/low/taunt01" ) )
  14734. {
  14735. // Pyro Rage!
  14736. CBaseCombatWeapon *pWeapon = GetActiveWeapon();
  14737. if ( pWeapon )
  14738. {
  14739. int iBuffType = 0;
  14740. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iBuffType, set_buff_type );
  14741. if ( iBuffType > 0 )
  14742. {
  14743. // Time for crits!
  14744. m_Shared.ActivateRageBuff( this, iBuffType );
  14745. // Pyro needs high defense while he's taunting
  14746. //m_Shared.AddCond( TF_COND_DEFENSEBUFF_HIGH, 3.0f );
  14747. m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, 2.60f );
  14748. m_Shared.AddCond( TF_COND_MEGAHEAL, 2.60f );
  14749. }
  14750. }
  14751. }
  14752. else if ( IsPlayerClass( TF_CLASS_SCOUT ) )
  14753. {
  14754. if ( m_Shared.InCond( TF_COND_PHASE ) == false )
  14755. {
  14756. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  14757. {
  14758. m_flTauntAttackTime = gpGlobals->curtime + 0.9;
  14759. m_iTauntAttack = TAUNTATK_SCOUT_DRINK;
  14760. }
  14761. }
  14762. }
  14763. else if ( IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  14764. {
  14765. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  14766. {
  14767. m_flTauntAttackTime = gpGlobals->curtime + 1.0;
  14768. m_iTauntAttack = TAUNTATK_HEAVY_EAT;
  14769. // Only count sandviches for "eat 100 sandviches" achievement
  14770. CTFLunchBox *pLunchbox = (CTFLunchBox*)pActiveWeapon;
  14771. if ( ( pLunchbox->GetLunchboxType() == LUNCHBOX_STANDARD ) || ( pLunchbox->GetLunchboxType() == LUNCHBOX_STANDARD_ROBO ) )
  14772. {
  14773. AwardAchievement( ACHIEVEMENT_TF_HEAVY_EAT_SANDWICHES );
  14774. }
  14775. }
  14776. }
  14777. }
  14778. else if ( iTauntIndex == TAUNT_SPECIAL )
  14779. {
  14780. if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  14781. {
  14782. // Wrenchmotron taunt teleport home effect
  14783. if ( !Q_stricmp( szResponse, "scenes/player/engineer/low/taunt_drg_melee.vcd" ) )
  14784. {
  14785. m_bIsTeleportingUsingEurekaEffect = true;
  14786. m_teleportHomeFlashTimer.Start( 1.9f );
  14787. // play teleport sound at location we are leaving
  14788. Vector soundOrigin = WorldSpaceCenter();
  14789. CPASAttenuationFilter filter( soundOrigin );
  14790. EmitSound_t ep;
  14791. ep.m_nChannel = CHAN_STATIC;
  14792. ep.m_pSoundName = "Weapon_DRG_Wrench.Teleport";
  14793. ep.m_flVolume = 1.0f;
  14794. ep.m_SoundLevel = SNDLVL_150dB;
  14795. ep.m_nFlags = 0;
  14796. ep.m_nPitch = PITCH_NORM;
  14797. ep.m_pOrigin = &soundOrigin;
  14798. int worldEntIndex = 0;
  14799. EmitSound( filter, worldEntIndex, ep );
  14800. }
  14801. }
  14802. }
  14803. // Setup taunt attacks. Hacky, but a lot easier to do than getting server side anim events working.
  14804. if ( IsPlayerClass(TF_CLASS_PYRO) )
  14805. {
  14806. if ( !V_stricmp( szResponse, "scenes/player/pyro/low/taunt02.vcd" ) )
  14807. {
  14808. m_flTauntAttackTime = gpGlobals->curtime + 2.1f;
  14809. m_iTauntAttack = TAUNTATK_PYRO_HADOUKEN;
  14810. }
  14811. else if ( !V_stricmp( szResponse, "scenes/player/pyro/low/taunt_bubbles.vcd" ) )
  14812. {
  14813. m_flTauntAttackTime = gpGlobals->curtime + 3.0f;
  14814. m_iTauntAttack = TAUNTATK_PYRO_ARMAGEDDON;
  14815. // We need to parent this to a target instead of the player because the player changing their camera view can twist the rainbow
  14816. CBaseEntity *pTarget = CreateEntityByName( "info_target" );
  14817. if ( pTarget )
  14818. {
  14819. DispatchSpawn( pTarget );
  14820. pTarget->SetAbsOrigin( GetAbsOrigin() );
  14821. pTarget->SetAbsAngles( GetAbsAngles() );
  14822. pTarget->SetEFlags( EFL_FORCE_CHECK_TRANSMIT );
  14823. pTarget->SetThink( &BaseClass::SUB_Remove );
  14824. pTarget->SetNextThink( gpGlobals->curtime + 8.0f );
  14825. CBaseEntity *pGround = GetGroundEntity();
  14826. if ( pGround && pGround->GetMoveType() == MOVETYPE_PUSH )
  14827. {
  14828. pTarget->SetParent( pGround );
  14829. }
  14830. }
  14831. DispatchParticleEffect( "pyrotaunt_rainbow_norainbow", PATTACH_ABSORIGIN_FOLLOW, pTarget );
  14832. }
  14833. else if ( !V_stricmp( szResponse, "scenes/player/pyro/low/taunt_scorch_shot.vcd" ) )
  14834. {
  14835. m_flTauntAttackTime = gpGlobals->curtime + 1.9f;
  14836. m_iTauntAttack = TAUNTATK_PYRO_SCORCHSHOT;
  14837. }
  14838. }
  14839. else if ( IsPlayerClass(TF_CLASS_HEAVYWEAPONS) )
  14840. {
  14841. if ( !V_stricmp( szResponse, "scenes/player/heavy/low/taunt03_v1.vcd" ) )
  14842. {
  14843. m_flTauntAttackTime = gpGlobals->curtime + 1.8;
  14844. m_iTauntAttack = TAUNTATK_HEAVY_HIGH_NOON;
  14845. }
  14846. else if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_FISTS )
  14847. {
  14848. CTFFists *pFists = dynamic_cast<CTFFists*>(pActiveWeapon);
  14849. if ( pFists && pFists->GetFistType() == FISTTYPE_RADIAL_BUFF )
  14850. {
  14851. m_flTauntAttackTime = gpGlobals->curtime + 1.0;
  14852. m_iTauntAttack = TAUNTATK_HEAVY_RADIAL_BUFF;
  14853. }
  14854. }
  14855. }
  14856. else if ( IsPlayerClass( TF_CLASS_SCOUT ) )
  14857. {
  14858. if ( !V_stricmp( szResponse, "scenes/player/scout/low/taunt05_v1.vcd" ) )
  14859. {
  14860. m_flTauntAttackTime = gpGlobals->curtime + 4.03f;
  14861. m_iTauntAttack = TAUNTATK_SCOUT_GRAND_SLAM;
  14862. }
  14863. }
  14864. else if ( IsPlayerClass( TF_CLASS_MEDIC ) )
  14865. {
  14866. if ( !V_stricmp( szResponse, "scenes/player/medic/low/taunt06.vcd" ) )
  14867. {
  14868. m_flTauntAttackTime = gpGlobals->curtime + 0.8f;
  14869. m_flTauntInhaleTime = gpGlobals->curtime + 1.8f;
  14870. const char *pszParticleEffect;
  14871. pszParticleEffect = ( GetTeamNumber() == TF_TEAM_RED ? "healhuff_red" : "healhuff_blu" );
  14872. DispatchParticleEffect( pszParticleEffect, PATTACH_POINT_FOLLOW, this, "eyes" );
  14873. m_iTauntAttack = TAUNTATK_MEDIC_INHALE;
  14874. }
  14875. else if ( !V_stricmp( szResponse, "scenes/player/medic/low/taunt08.vcd" ) )
  14876. {
  14877. m_flTauntAttackTime = gpGlobals->curtime + 2.2f;
  14878. m_iTauntAttack = TAUNTATK_MEDIC_UBERSLICE_IMPALE;
  14879. }
  14880. }
  14881. else if ( IsPlayerClass( TF_CLASS_SPY ) )
  14882. {
  14883. if ( !V_strnicmp( szResponse, "scenes/player/spy/low/taunt03", 29 ) ) // There's taunt03_v1 & taunt03_v2
  14884. {
  14885. m_flTauntAttackTime = gpGlobals->curtime + 1.8f;
  14886. m_iTauntAttack = TAUNTATK_SPY_FENCING_SLASH_A;
  14887. }
  14888. }
  14889. else if ( IsPlayerClass( TF_CLASS_SNIPER ) )
  14890. {
  14891. if ( !V_stricmp( szResponse, "scenes/player/sniper/low/taunt04.vcd" ) )
  14892. {
  14893. m_flTauntAttackTime = gpGlobals->curtime + 0.85f;
  14894. m_iTauntAttack = TAUNTATK_SNIPER_ARROW_STAB_IMPALE;
  14895. }
  14896. }
  14897. else if ( IsPlayerClass( TF_CLASS_SOLDIER ) )
  14898. {
  14899. if ( !V_stricmp( szResponse, "scenes/player/soldier/low/taunt05.vcd" ) )
  14900. {
  14901. if ( IsWormsGearEquipped() )
  14902. {
  14903. m_flTauntAttackTime = gpGlobals->curtime + 1.4f;
  14904. m_iTauntAttack = TAUNTATK_SOLDIER_GRENADE_KILL_WORMSIGN;
  14905. return;
  14906. }
  14907. m_flTauntAttackTime = gpGlobals->curtime + 3.5f;
  14908. m_iTauntAttack = TAUNTATK_SOLDIER_GRENADE_KILL;
  14909. }
  14910. }
  14911. else if ( IsPlayerClass( TF_CLASS_DEMOMAN ) )
  14912. {
  14913. if ( !V_stricmp( szResponse, "scenes/player/demoman/low/taunt09.vcd" ) )
  14914. {
  14915. m_flTauntAttackTime = gpGlobals->curtime + 2.55f;
  14916. m_iTauntAttack = TAUNTATK_DEMOMAN_BARBARIAN_SWING;
  14917. }
  14918. }
  14919. else if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  14920. {
  14921. if ( !V_stricmp( szResponse, "scenes/player/engineer/low/taunt07.vcd" ) )
  14922. {
  14923. m_flTauntAttackTime = gpGlobals->curtime + 3.695f;
  14924. m_iTauntAttack = TAUNTATK_ENGINEER_GUITAR_SMASH;
  14925. }
  14926. else if ( !V_stricmp( szResponse, "scenes/player/engineer/low/taunt09.vcd" ) )
  14927. {
  14928. m_flTauntAttackTime = gpGlobals->curtime + 3.2f;
  14929. m_iTauntAttack = TAUNTATK_ENGINEER_ARM_IMPALE;
  14930. }
  14931. }
  14932. }
  14933. //-----------------------------------------------------------------------------
  14934. // Purpose: Aborts a taunt in progress.
  14935. //-----------------------------------------------------------------------------
  14936. void CTFPlayer::CancelTaunt( void )
  14937. {
  14938. m_bIsTeleportingUsingEurekaEffect = false;
  14939. m_teleportHomeFlashTimer.Reset();
  14940. StopTaunt();
  14941. }
  14942. //-----------------------------------------------------------------------------
  14943. // Purpose: Stops taunting
  14944. //-----------------------------------------------------------------------------
  14945. void CTFPlayer::StopTaunt( void )
  14946. {
  14947. if ( m_hTauntScene.Get() )
  14948. {
  14949. StopScriptedScene( this, m_hTauntScene );
  14950. m_flTauntRemoveTime = 0.0f;
  14951. m_bAllowedToRemoveTaunt = true;
  14952. m_hTauntScene = NULL;
  14953. }
  14954. if ( m_hTauntProp.Get() )
  14955. {
  14956. UTIL_Remove( m_hTauntProp );
  14957. m_hTauntProp = NULL;
  14958. }
  14959. if ( IsReadyToTauntWithPartner() )
  14960. {
  14961. CancelTauntWithPartner();
  14962. }
  14963. StopTauntSoundLoop();
  14964. // reset the FOV
  14965. if ( m_TauntEconItemView.IsValid() )
  14966. {
  14967. SetFOV( this, m_iPreTauntFOV );
  14968. }
  14969. m_hHighFivePartner = NULL;
  14970. m_bAllowMoveDuringTaunt = false;
  14971. m_flTauntOutroTime = 0.f;
  14972. m_bTauntForceMoveForward = false;
  14973. m_flTauntForceMoveForwardSpeed = 0.f;
  14974. m_flTauntMoveAccelerationTime = 0.f;
  14975. m_flTauntTurnSpeed = 0.f;
  14976. m_flTauntTurnAccelerationTime = 0.f;
  14977. m_bTauntMimic = false;
  14978. m_bIsTauntInitiator = false;
  14979. m_TauntEconItemView.Invalidate();
  14980. m_flNextAllowTauntRemapInputTime = -1.f;
  14981. m_flCurrentTauntMoveSpeed = 0.f;
  14982. m_nActiveTauntSlot = LOADOUT_POSITION_INVALID;
  14983. m_iTauntItemDefIndex = INVALID_ITEM_DEF_INDEX;
  14984. m_TauntStage = TAUNT_NONE;
  14985. }
  14986. //-----------------------------------------------------------------------------
  14987. // Purpose:
  14988. //-----------------------------------------------------------------------------
  14989. void CTFPlayer::EndLongTaunt()
  14990. {
  14991. Assert( m_Shared.GetTauntIndex() == TAUNT_LONG );
  14992. m_bAllowedToRemoveTaunt = true;
  14993. m_flTauntRemoveTime = gpGlobals->curtime;
  14994. int iClass = GetPlayerClass()->GetClassIndex();
  14995. CTFTauntInfo *pTauntData = m_TauntEconItemView.GetStaticData()->GetTauntData();
  14996. if ( pTauntData )
  14997. {
  14998. // Make sure press-and-hold taunts last a minimum amount of time
  14999. float flMinTime = pTauntData->GetMinTauntTime();
  15000. if ( m_flTauntStartTime + flMinTime > gpGlobals->curtime )
  15001. {
  15002. m_flTauntRemoveTime = m_flTauntStartTime + flMinTime;
  15003. }
  15004. // should we play outro?
  15005. if ( pTauntData->GetOutroSceneCount( iClass ) > 0 )
  15006. {
  15007. m_bAllowedToRemoveTaunt = false;
  15008. m_flTauntOutroTime = m_flTauntRemoveTime;
  15009. }
  15010. }
  15011. }
  15012. //-----------------------------------------------------------------------------
  15013. // Purpose:
  15014. //-----------------------------------------------------------------------------
  15015. float CTFPlayer::PlayTauntOutroScene()
  15016. {
  15017. m_TauntStage = TAUNT_OUTRO;
  15018. float flDuration = 0.f;
  15019. int iClass = GetPlayerClass()->GetClassIndex();
  15020. CTFTauntInfo *pTauntData = m_TauntEconItemView.GetStaticData()->GetTauntData();
  15021. if ( pTauntData )
  15022. {
  15023. if ( pTauntData->GetOutroSceneCount( iClass ) > 0 )
  15024. {
  15025. // play outro
  15026. const char *pszOutroScene = pTauntData->GetOutroScene( iClass, RandomInt( 0, pTauntData->GetOutroSceneCount( iClass ) - 1 ) );
  15027. if ( m_hTauntScene.Get() )
  15028. {
  15029. StopScriptedScene( this, m_hTauntScene );
  15030. m_hTauntScene = NULL;
  15031. StopTauntSoundLoop();
  15032. }
  15033. // Allow voice commands, etc to be interrupted.
  15034. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  15035. Assert( pExpresser );
  15036. pExpresser->AllowMultipleScenes();
  15037. m_bInitTaunt = true;
  15038. flDuration = PlayScene( pszOutroScene );
  15039. OnTauntSucceeded( pszOutroScene, TAUNT_MISC_ITEM, MP_CONCEPT_HIGHFIVE_SUCCESS );
  15040. m_bInitTaunt = false;
  15041. pExpresser->DisallowMultipleScenes();
  15042. if ( m_hTauntProp != NULL )
  15043. {
  15044. const char *pszPropScene = pTauntData->GetPropOutroScene( iClass );
  15045. if ( pszPropScene )
  15046. {
  15047. m_hTauntProp->PlayScene( pszPropScene );
  15048. }
  15049. }
  15050. }
  15051. }
  15052. return flDuration;
  15053. }
  15054. //-----------------------------------------------------------------------------
  15055. // Purpose:
  15056. //-----------------------------------------------------------------------------
  15057. void CTFPlayer::HandleTauntCommand( int iTauntSlot )
  15058. {
  15059. if ( !IsAllowedToTaunt() )
  15060. return;
  15061. m_nActiveTauntSlot = LOADOUT_POSITION_INVALID;
  15062. if ( iTauntSlot > 0 && iTauntSlot <= 8 )
  15063. {
  15064. m_nActiveTauntSlot = LOADOUT_POSITION_TAUNT + iTauntSlot - 1;
  15065. CEconItemView* pItem = GetEquippedItemForLoadoutSlot( m_nActiveTauntSlot );
  15066. PlayTauntSceneFromItem( pItem );
  15067. return;
  15068. }
  15069. else
  15070. {
  15071. // Check if I should accept taunt with partner
  15072. CTFPlayer *initiator = FindPartnerTauntInitiator();
  15073. if ( initiator )
  15074. {
  15075. if ( initiator->m_bTauntMimic )
  15076. {
  15077. MimicTauntFromPartner( initiator );
  15078. }
  15079. else
  15080. {
  15081. AcceptTauntWithPartner( initiator );
  15082. }
  15083. return;
  15084. }
  15085. // does this weapon prevent player from doing manual taunt?
  15086. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  15087. if ( pActiveWeapon && !pActiveWeapon->AllowTaunts() )
  15088. return;
  15089. Taunt( TAUNT_BASE_WEAPON );
  15090. }
  15091. }
  15092. //-----------------------------------------------------------------------------
  15093. // Purpose:
  15094. //-----------------------------------------------------------------------------
  15095. void CTFPlayer::ClearTauntAttack()
  15096. {
  15097. m_flTauntAttackTime = 0.f;
  15098. m_flTauntInhaleTime = 0.f;
  15099. m_iTauntAttack = TAUNTATK_NONE;
  15100. m_iTauntAttackCount = 0;
  15101. m_iTauntRPSResult = 0;
  15102. }
  15103. //-----------------------------------------------------------------------------
  15104. // Purpose:
  15105. //-----------------------------------------------------------------------------
  15106. void CTFPlayer::HandleWeaponSlotAfterTaunt()
  15107. {
  15108. if ( m_iPreTauntWeaponSlot != -1 )
  15109. {
  15110. // switch back to the active weapon before taunting
  15111. Weapon_Switch( Weapon_GetSlot( m_iPreTauntWeaponSlot ) );
  15112. m_iPreTauntWeaponSlot = -1;
  15113. }
  15114. }
  15115. //-----------------------------------------------------------------------------
  15116. // Purpose:
  15117. //-----------------------------------------------------------------------------
  15118. static void DispatchRPSEffect( const CTFPlayer *pPlayer, const char* pszParticleName )
  15119. {
  15120. CEffectData data;
  15121. data.m_nHitBox = GetParticleSystemIndex( pszParticleName );
  15122. data.m_vOrigin = pPlayer->GetAbsOrigin() + Vector( 0, 0, 87.0f );
  15123. data.m_vAngles = vec3_angle;
  15124. CPASFilter intiatorFilter( data.m_vOrigin );
  15125. intiatorFilter.SetIgnorePredictionCull( true );
  15126. te->DispatchEffect( intiatorFilter, 0.0, data.m_vOrigin, "ParticleEffect", data );
  15127. }
  15128. //-----------------------------------------------------------------------------
  15129. // Purpose:
  15130. //-----------------------------------------------------------------------------
  15131. void CTFPlayer::DoTauntAttack( void )
  15132. {
  15133. if ( !IsTaunting() || !IsAlive() || m_iTauntAttack == TAUNTATK_NONE )
  15134. {
  15135. return;
  15136. }
  15137. int iTauntAttack = m_iTauntAttack;
  15138. m_iTauntAttack = TAUNTATK_NONE;
  15139. if ( iTauntAttack == TAUNTATK_PYRO_HADOUKEN || iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_A ||
  15140. iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_B || iTauntAttack == TAUNTATK_SPY_FENCING_STAB )
  15141. {
  15142. // Pyro Hadouken fireball attack
  15143. // Kill all enemies within a small volume in front of the player.
  15144. Vector vecForward;
  15145. AngleVectors( QAngle(0, m_angEyeAngles[YAW], 0), &vecForward );
  15146. Vector vecCenter = WorldSpaceCenter() + vecForward * 64;
  15147. Vector vecSize = Vector(24,24,24);
  15148. CBaseEntity *pList[256];
  15149. int count = UTIL_EntitiesInBox( pList, 256, vecCenter - vecSize, vecCenter + vecSize, FL_CLIENT|FL_OBJECT );
  15150. if ( count )
  15151. {
  15152. // Launch them up a little
  15153. AngleVectors( QAngle(-45, m_angEyeAngles[YAW], 0), &vecForward );
  15154. for ( int i = 0; i < count; i++ )
  15155. {
  15156. // Team damage doesn't prevent us hurting ourself, so we do it manually here
  15157. if ( pList[i] == this )
  15158. continue;
  15159. if ( FVisible( pList[i], MASK_SOLID ) == false )
  15160. continue;
  15161. Vector vecPos = WorldSpaceCenter();
  15162. vecPos += (pList[i]->WorldSpaceCenter() - vecPos) * 0.75;
  15163. // Spy taunt does two quick slashes, followed by a killing blow
  15164. if ( iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_A || iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_B )
  15165. {
  15166. // No physics push so it doesn't push the player out of the range of the stab
  15167. pList[i]->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 100, vecPos, 25, DMG_SLASH | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_FENCING ) );
  15168. }
  15169. else if ( iTauntAttack == TAUNTATK_SPY_FENCING_STAB )
  15170. {
  15171. pList[i]->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 20000, vecPos, 500.0f, DMG_SLASH, TF_DMG_CUSTOM_TAUNTATK_FENCING ) );
  15172. }
  15173. else if ( iTauntAttack == TAUNTATK_PYRO_HADOUKEN )
  15174. {
  15175. pList[i]->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 25000, vecPos, 500.0f, DMG_BURN | DMG_IGNITE, TF_DMG_CUSTOM_TAUNTATK_HADOUKEN ) );
  15176. }
  15177. }
  15178. }
  15179. if ( iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_A )
  15180. {
  15181. m_iTauntAttack = TAUNTATK_SPY_FENCING_SLASH_B;
  15182. m_flTauntAttackTime = gpGlobals->curtime + 0.47;
  15183. }
  15184. else if ( iTauntAttack == TAUNTATK_SPY_FENCING_SLASH_B )
  15185. {
  15186. m_iTauntAttack = TAUNTATK_SPY_FENCING_STAB;
  15187. m_flTauntAttackTime = gpGlobals->curtime + 1.73;
  15188. }
  15189. if ( tf_debug_damage.GetBool() )
  15190. {
  15191. NDebugOverlay::Box( vecCenter, -vecSize, vecSize, 0, 255, 0, 40, 10 );
  15192. }
  15193. }
  15194. else if ( iTauntAttack == TAUNTATK_SOLDIER_GRENADE_KILL_WORMSIGN )
  15195. {
  15196. EmitSound( "Taunt.WormsHHG" );
  15197. m_iTauntAttack = TAUNTATK_SOLDIER_GRENADE_KILL;
  15198. m_flTauntAttackTime = gpGlobals->curtime + 2.1;
  15199. }
  15200. else if ( iTauntAttack == TAUNTATK_SOLDIER_GRENADE_KILL )
  15201. {
  15202. matrix3x4_t worldSpace;
  15203. MatrixCopy( EntityToWorldTransform(), worldSpace );
  15204. Vector bonePos;
  15205. QAngle boneAngles;
  15206. int iRightHand = LookupBone( "bip_hand_r" );
  15207. if ( iRightHand != -1 )
  15208. {
  15209. GetBonePosition( iRightHand, bonePos, boneAngles );
  15210. CPVSFilter filter( bonePos );
  15211. TE_TFExplosion( filter, 0.0f, bonePos, Vector(0,0,1), TF_WEAPON_GRENADELAUNCHER, entindex() );
  15212. CTakeDamageInfo info( this, this, GetActiveTFWeapon(), vec3_origin, bonePos, 200.f, DMG_BLAST | DMG_USEDISTANCEMOD, TF_DMG_CUSTOM_TAUNTATK_GRENADE, &bonePos );
  15213. CTFRadiusDamageInfo radiusinfo( &info, bonePos, 100.f );
  15214. TFGameRules()->RadiusDamage( radiusinfo );
  15215. }
  15216. }
  15217. else if ( iTauntAttack == TAUNTATK_SNIPER_ARROW_STAB_IMPALE || iTauntAttack == TAUNTATK_SNIPER_ARROW_STAB_KILL ||
  15218. iTauntAttack == TAUNTATK_ENGINEER_ARM_IMPALE || iTauntAttack == TAUNTATK_ENGINEER_ARM_KILL || iTauntAttack == TAUNTATK_ENGINEER_ARM_BLEND )
  15219. {
  15220. Vector vecForward;
  15221. AngleVectors( EyeAngles(), &vecForward );
  15222. Vector vecEnd = EyePosition() + vecForward * 128;
  15223. trace_t tr;
  15224. UTIL_TraceLine( EyePosition(), vecEnd, MASK_SOLID & ~CONTENTS_HITBOX, this, COLLISION_GROUP_PLAYER, &tr );
  15225. if ( tr.fraction < 1.0 )
  15226. {
  15227. CBaseEntity *pEnt = tr.m_pEnt;
  15228. if ( pEnt && pEnt->IsPlayer() && pEnt->GetTeamNumber() > LAST_SHARED_TEAM && pEnt->GetTeamNumber() != GetTeamNumber() )
  15229. {
  15230. CTFPlayer *pVictim = ToTFPlayer( pEnt );
  15231. switch ( iTauntAttack )
  15232. {
  15233. case TAUNTATK_SNIPER_ARROW_STAB_IMPALE:
  15234. case TAUNTATK_ENGINEER_ARM_IMPALE:
  15235. if ( pVictim )
  15236. {
  15237. // don't stun giants
  15238. if ( !pVictim->IsMiniBoss() )
  15239. {
  15240. pVictim->m_Shared.StunPlayer( 3.0f, 1.0, TF_STUN_BOTH | TF_STUN_NO_EFFECTS, this );
  15241. }
  15242. if ( iTauntAttack == TAUNTATK_ENGINEER_ARM_IMPALE )
  15243. {
  15244. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward, pEnt->WorldSpaceCenter(), 1, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_ENGINEER_ARM_KILL ) );
  15245. }
  15246. }
  15247. break;
  15248. case TAUNTATK_ENGINEER_ARM_BLEND:
  15249. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward, pEnt->WorldSpaceCenter(), 1, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_ENGINEER_ARM_KILL ) );
  15250. break;
  15251. case TAUNTATK_SNIPER_ARROW_STAB_KILL:
  15252. // Launch them up a little
  15253. vecForward = (WorldSpaceCenter() - pEnt->WorldSpaceCenter());
  15254. VectorNormalize( vecForward );
  15255. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 12000, pEnt->WorldSpaceCenter(), 500.0f, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_ARROW_STAB ) );
  15256. break;
  15257. case TAUNTATK_ENGINEER_ARM_KILL:
  15258. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 12000, pEnt->WorldSpaceCenter(), 500.0f, DMG_BLAST, TF_DMG_CUSTOM_TAUNTATK_ENGINEER_ARM_KILL ) );
  15259. break;
  15260. }
  15261. }
  15262. }
  15263. if ( iTauntAttack == TAUNTATK_SNIPER_ARROW_STAB_IMPALE )
  15264. {
  15265. m_iTauntAttack = TAUNTATK_SNIPER_ARROW_STAB_KILL;
  15266. m_flTauntAttackTime = gpGlobals->curtime + 1.30;
  15267. }
  15268. else if ( iTauntAttack == TAUNTATK_ENGINEER_ARM_IMPALE )
  15269. {
  15270. m_iTauntAttack = TAUNTATK_ENGINEER_ARM_BLEND;
  15271. m_flTauntAttackTime = gpGlobals->curtime + 0.05;
  15272. m_iTauntAttackCount = 0;
  15273. }
  15274. else if ( iTauntAttack == TAUNTATK_ENGINEER_ARM_BLEND )
  15275. {
  15276. m_iTauntAttack = TAUNTATK_ENGINEER_ARM_BLEND;
  15277. m_flTauntAttackTime = gpGlobals->curtime + 0.05;
  15278. m_iTauntAttackCount++;
  15279. if ( m_iTauntAttackCount == 13 )
  15280. {
  15281. m_iTauntAttack = TAUNTATK_ENGINEER_ARM_KILL;
  15282. }
  15283. }
  15284. }
  15285. else if ( iTauntAttack == TAUNTATK_HEAVY_EAT )
  15286. {
  15287. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  15288. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  15289. {
  15290. CTFLunchBox *pLunchbox = (CTFLunchBox*)pActiveWeapon;
  15291. pLunchbox->ApplyBiteEffects( this );
  15292. }
  15293. // Keep eating until the taunt is over
  15294. m_iTauntAttack = TAUNTATK_HEAVY_EAT;
  15295. m_flTauntAttackTime = gpGlobals->curtime + 1.0;
  15296. // If we're going to finish eating after this bite, say our line
  15297. if ( m_flTauntRemoveTime < m_flTauntAttackTime )
  15298. {
  15299. if ( IsSpeaking() )
  15300. {
  15301. // The player may technically still be speaking even though the actual VO is over and just
  15302. // hasn't been cleared yet. We need to force it to end so our next concept can be played.
  15303. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  15304. if ( pExpresser )
  15305. {
  15306. pExpresser->ForceNotSpeaking();
  15307. }
  15308. }
  15309. SpeakConceptIfAllowed( MP_CONCEPT_ATE_FOOD );
  15310. }
  15311. }
  15312. else if ( iTauntAttack == TAUNTATK_HEAVY_RADIAL_BUFF )
  15313. {
  15314. Vector vecOrg = GetAbsOrigin();
  15315. // Find nearby team mates and give them bonus health & crit chance
  15316. for ( int i = 0; i < GetTeam()->GetNumPlayers(); i++ )
  15317. {
  15318. CTFPlayer *pTeamPlayer = ToTFPlayer( GetTeam()->GetPlayer(i) );
  15319. if ( pTeamPlayer && pTeamPlayer->IsAlive() )
  15320. {
  15321. // If they're within the radius, give 'em the buff
  15322. if ( (vecOrg - pTeamPlayer->GetAbsOrigin()).LengthSqr() < (1024*1024) )
  15323. {
  15324. pTeamPlayer->TakeHealth( 50, DMG_GENERIC );
  15325. pTeamPlayer->m_Shared.AddTempCritBonus( 0.5 );
  15326. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  15327. if ( event )
  15328. {
  15329. event->SetInt( "amount", 50 );
  15330. event->SetInt( "entindex", pTeamPlayer->entindex() );
  15331. event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX );
  15332. gameeventmanager->FireEvent( event );
  15333. }
  15334. }
  15335. }
  15336. }
  15337. }
  15338. else if ( iTauntAttack == TAUNTATK_HEAVY_HIGH_NOON )
  15339. {
  15340. // Heavy "High Noon" attack
  15341. Vector vecForward;
  15342. AngleVectors( EyeAngles(), &vecForward );
  15343. Vector vecEnd = EyePosition() + vecForward * 500;
  15344. trace_t tr;
  15345. UTIL_TraceLine( EyePosition(), vecEnd, ( MASK_SOLID | CONTENTS_HITBOX ), this, COLLISION_GROUP_PLAYER, &tr );
  15346. // DebugDrawLine( EyePosition(), vecEnd, 0, 0, 255, true, 3.0f );
  15347. if ( tr.fraction < 1.0 )
  15348. {
  15349. CBaseEntity *pEnt = tr.m_pEnt;
  15350. if ( pEnt && pEnt->IsPlayer() && pEnt->GetTeamNumber() > LAST_SHARED_TEAM && pEnt->GetTeamNumber() != GetTeamNumber() )
  15351. {
  15352. // Launch them up a little
  15353. AngleVectors( QAngle(-45, m_angEyeAngles[YAW], 0), &vecForward );
  15354. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 25000, WorldSpaceCenter(), 500.0f, DMG_BULLET, TF_DMG_CUSTOM_TAUNTATK_HIGH_NOON ) );
  15355. }
  15356. }
  15357. }
  15358. else if ( iTauntAttack == TAUNTATK_SCOUT_DRINK )
  15359. {
  15360. if ( !m_Shared.IsControlStunned() )
  15361. {
  15362. // Check for CritBerry flavor
  15363. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  15364. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  15365. {
  15366. float flDropDeadTime = ( 100.f / tf_scout_energydrink_consume_rate.GetFloat() ) + 1.f; // Just in case. Normally over in 8 seconds.
  15367. CTFLunchBox *pLunchbox = static_cast< CTFLunchBox* >( pActiveWeapon );
  15368. if ( pLunchbox && pLunchbox->GetLunchboxType() == LUNCHBOX_ADDS_MINICRITS )
  15369. {
  15370. m_Shared.AddCond( TF_COND_ENERGY_BUFF, flDropDeadTime );
  15371. }
  15372. else
  15373. {
  15374. m_Shared.AddCond( TF_COND_PHASE, flDropDeadTime );
  15375. if ( HasTheFlag() )
  15376. {
  15377. bool bShouldDrop = true;
  15378. // Always allow teams to hear each other in TD mode
  15379. if ( TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  15380. {
  15381. bShouldDrop = false;
  15382. }
  15383. if ( bShouldDrop )
  15384. {
  15385. DropFlag();
  15386. }
  15387. }
  15388. }
  15389. SelectLastItem();
  15390. }
  15391. }
  15392. }
  15393. else if ( iTauntAttack == TAUNTATK_SCOUT_GRAND_SLAM )
  15394. {
  15395. // Find a player in front of us and knock 'em across the map.
  15396. // Same box logic as hadouken & pyro knockback.
  15397. Vector vecForward;
  15398. AngleVectors( QAngle(0, m_angEyeAngles[YAW], 0), &vecForward );
  15399. Vector vecCenter = WorldSpaceCenter() + vecForward * 64;
  15400. Vector vecSize = Vector(24,24,24);
  15401. CBaseEntity *pObjects[256];
  15402. int count = UTIL_EntitiesInBox( pObjects, 256, vecCenter - vecSize, vecCenter + vecSize, FL_CLIENT|FL_OBJECT );
  15403. if ( count )
  15404. {
  15405. for ( int i=0; i<count; i++ )
  15406. {
  15407. // Must be facing whoever we knock back.
  15408. Vector vecToTarget;
  15409. vecToTarget = pObjects[i]->WorldSpaceCenter() - WorldSpaceCenter();
  15410. VectorNormalize( vecToTarget );
  15411. float flDot = DotProduct( vecForward, vecToTarget );
  15412. if ( flDot < 0.80 )
  15413. continue;
  15414. CTFPlayer *pTarget = ToTFPlayer( pObjects[i] );
  15415. if ( !pTarget )
  15416. continue;
  15417. if ( pTarget->GetTeamNumber() == GetTeamNumber() )
  15418. continue;
  15419. // Do a quick trace and make sure we have LOS.
  15420. trace_t tr;
  15421. UTIL_TraceLine( WorldSpaceCenter(), pObjects[i]->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_PLAYER, &tr );
  15422. if ( tr.fraction < 1.0 )
  15423. continue;
  15424. pTarget->SetAbsVelocity( vec3_origin );
  15425. //pTarget->m_Shared.StunPlayer( 8.f, 1.f, TF_STUN_BOTH | TF_STUN_SPECIAL_SOUND );
  15426. pTarget->StunSound( this, TF_STUN_BOTH | TF_STUN_SPECIAL_SOUND );
  15427. pTarget->ApplyPunchImpulseX( RandomInt( 10, 15 ) );
  15428. AngleVectors( QAngle(-45, m_angEyeAngles[YAW], 0), &vecForward );
  15429. pTarget->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 130000, WorldSpaceCenter(), 500.0f, DMG_BULLET, TF_DMG_CUSTOM_TAUNTATK_GRAND_SLAM ) );
  15430. // Tell the achievement system we swatted someone.
  15431. IGameEvent *event = gameeventmanager->CreateEvent( "scout_grand_slam" );
  15432. if ( event )
  15433. {
  15434. event->SetInt( "scout_id", GetUserID() );
  15435. event->SetInt( "target_id", pTarget->GetUserID() );
  15436. gameeventmanager->FireEvent( event );
  15437. }
  15438. }
  15439. }
  15440. }
  15441. else if ( iTauntAttack == TAUNTATK_MEDIC_HEROIC_TAUNT )
  15442. {
  15443. // do these later
  15444. m_flTauntAttackTime = gpGlobals->curtime + 3.0f;
  15445. m_iTauntAttack = TAUNTATK_MEDIC_RELEASE_DOVES;
  15446. // send a reliable message to make sure the effect happens
  15447. CPVSFilter filter( GetAbsOrigin() );
  15448. UserMessageBegin( filter, "PlayerGodRayEffect" );
  15449. WRITE_BYTE( entindex() );
  15450. MessageEnd();
  15451. EmitSound( "Taunt.MedicHeroic" );
  15452. }
  15453. else if ( iTauntAttack == TAUNTATK_MEDIC_RELEASE_DOVES )
  15454. {
  15455. // not really a taunt "attack", just a hook to release some doves at the appropriate time
  15456. Vector launchSpot = ( WorldSpaceCenter() + GetAbsOrigin() ) / 2.0f;
  15457. for( int i=0; i<MEDIC_RELEASE_DOVE_COUNT; ++i )
  15458. {
  15459. Vector vecPos = launchSpot + Vector( 0, 0, RandomFloat( -10.0f, 20.0f ) );
  15460. SpawnClientsideFlyingBird( vecPos );
  15461. }
  15462. }
  15463. else if ( iTauntAttack == TAUNTATK_PYRO_ARMAGEDDON )
  15464. {
  15465. Vector origin( GetAbsOrigin() );
  15466. CPVSFilter filter( origin );
  15467. TE_TFExplosion( filter, 0.0f, origin, Vector( 0.0f, 0.0f, 1.0f ), TF_WEAPON_GRENADELAUNCHER, entindex() );
  15468. int nRandomPick[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  15469. CUtlVector< CTFPlayer* > vecDamagedPlayers;
  15470. const float flRadius = 100.0f;
  15471. const float flRadiusSqr = flRadius * flRadius;
  15472. CBaseEntity *pEntity = NULL;
  15473. for ( CEntitySphereQuery sphere( origin, flRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL && vecDamagedPlayers.Count() < ARRAYSIZE( nRandomPick ); sphere.NextEntity() )
  15474. {
  15475. // Skip players on the same team or who are invuln
  15476. CTFPlayer *pPlayer = ToTFPlayer( pEntity );
  15477. if ( !pPlayer || InSameTeam( pPlayer ) || pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE ) )
  15478. continue;
  15479. // CEntitySphereQuery actually does a box test. So we need to make sure the distance is less than the radius first.
  15480. Vector vecPos;
  15481. pEntity->CollisionProp()->CalcNearestPoint( origin, &vecPos );
  15482. if ( ( origin - vecPos ).LengthSqr() > flRadiusSqr )
  15483. continue;
  15484. // Finally LOS test
  15485. trace_t tr;
  15486. Vector vecSrc = WorldSpaceCenter();
  15487. Vector vecSpot = pEntity->WorldSpaceCenter();
  15488. CTraceFilterSimple filter( this, COLLISION_GROUP_PROJECTILE );
  15489. UTIL_TraceLine( vecSrc, vecSpot, MASK_SOLID_BRUSHONLY, &filter, &tr );
  15490. // If we don't trace the whole way to the target, and we didn't hit the target entity, we're blocked
  15491. if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
  15492. continue;
  15493. vecDamagedPlayers.AddToTail( pPlayer );
  15494. }
  15495. if ( vecDamagedPlayers.Count() )
  15496. {
  15497. int nBurnCount = 0;
  15498. float fDamage = 400.0f;
  15499. for ( int i = vecDamagedPlayers.Count() - 1; i >= 0; --i )
  15500. {
  15501. // Pick a random player
  15502. int nRand = RandomInt( 0, i );
  15503. CTFPlayer *pPlayer = vecDamagedPlayers[ nRandomPick[ nRand ] ];
  15504. if ( pPlayer )
  15505. {
  15506. bool bBurning = pPlayer->m_Shared.InCond( TF_COND_BURNING );
  15507. pPlayer->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vec3_origin, origin, fDamage, DMG_PLASMA, ( iTauntAttack == TAUNTATK_PYRO_ARMAGEDDON ) ? TF_DMG_CUSTOM_TAUNTATK_ARMAGEDDON : TF_DMG_CUSTOM_TAUNTATK_ALLCLASS_GUITAR_RIFF, &origin ) );
  15508. // If they weren't burning before but now they are, count it
  15509. if ( !bBurning && pPlayer->m_Shared.InCond( TF_COND_BURNING ) )
  15510. {
  15511. nBurnCount++;
  15512. }
  15513. // Next choice gets half that amount
  15514. fDamage /= 2;
  15515. // The end of the list moves overwrites the one we just picked
  15516. nRandomPick[ nRand ] = nRandomPick[ i ];
  15517. }
  15518. }
  15519. if ( iTauntAttack == TAUNTATK_PYRO_ARMAGEDDON )
  15520. {
  15521. if ( nBurnCount >= 3 )
  15522. {
  15523. AwardAchievement( ACHIEVEMENT_TF_PYRO_IGNITE_WITH_RAINBOW );
  15524. }
  15525. }
  15526. }
  15527. UTIL_ScreenShake( origin, 15.0, 150.0, 0.75f, 500.0f, SHAKE_START );
  15528. }
  15529. else if ( iTauntAttack == TAUNTATK_PYRO_SCORCHSHOT )
  15530. {
  15531. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  15532. if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_FLAREGUN )
  15533. {
  15534. CTFWeaponBaseGun *pGun = dynamic_cast< CTFWeaponBaseGun* >( pWeapon );
  15535. if ( pGun )
  15536. {
  15537. pGun->FireProjectile( this );
  15538. }
  15539. }
  15540. }
  15541. else if ( iTauntAttack == TAUNTATK_ALLCLASS_GUITAR_RIFF )
  15542. {
  15543. // We need to parent this to a target instead of the player because the player changing their camera view can twist the rainbow
  15544. CBaseEntity *pTarget = CreateEntityByName( "info_target" );
  15545. if ( pTarget )
  15546. {
  15547. DispatchSpawn( pTarget );
  15548. pTarget->SetAbsOrigin( GetAbsOrigin() );
  15549. pTarget->SetAbsAngles( GetAbsAngles() );
  15550. pTarget->SetEFlags( EFL_FORCE_CHECK_TRANSMIT );
  15551. pTarget->SetThink( &BaseClass::SUB_Remove );
  15552. pTarget->SetNextThink( gpGlobals->curtime + 6.0f );
  15553. CBaseEntity *pGround = GetGroundEntity();
  15554. if ( pGround && pGround->GetMoveType() == MOVETYPE_PUSH )
  15555. {
  15556. pTarget->SetParent( pGround );
  15557. }
  15558. }
  15559. CBroadcastRecipientFilter filter;
  15560. TE_TFParticleEffect( filter, 0.0, "bl_killtaunt", GetAbsOrigin(), GetAbsAngles(), pTarget, PATTACH_ABSORIGIN_FOLLOW );
  15561. EmitSound( "Taunt.GuitarRiff" );
  15562. }
  15563. else if ( iTauntAttack == TAUNTATK_MEDIC_INHALE )
  15564. {
  15565. int iHealed = TakeHealth( 1, DMG_GENERIC );
  15566. if ( iHealed > 0 )
  15567. {
  15568. CTF_GameStats.Event_PlayerHealedOther( this, iHealed );
  15569. }
  15570. // Keep eating until the taunt is over
  15571. if ( m_flTauntInhaleTime > gpGlobals->curtime )
  15572. {
  15573. m_iTauntAttack = TAUNTATK_MEDIC_INHALE;
  15574. m_flTauntAttackTime = gpGlobals->curtime + 0.1;
  15575. }
  15576. }
  15577. else if ( iTauntAttack == TAUNTATK_MEDIC_UBERSLICE_IMPALE || iTauntAttack == TAUNTATK_MEDIC_UBERSLICE_KILL )
  15578. {
  15579. Vector vecForward;
  15580. AngleVectors( EyeAngles(), &vecForward );
  15581. Vector vecEnd = EyePosition() + vecForward * 128;
  15582. trace_t tr;
  15583. UTIL_TraceLine( EyePosition(), vecEnd, MASK_SOLID & ~CONTENTS_HITBOX, this, COLLISION_GROUP_PLAYER, &tr );
  15584. if ( tr.fraction < 1.0 )
  15585. {
  15586. CBaseEntity *pEnt = tr.m_pEnt;
  15587. if ( pEnt && pEnt->IsPlayer() && pEnt->GetTeamNumber() > LAST_SHARED_TEAM && pEnt->GetTeamNumber() != GetTeamNumber() )
  15588. {
  15589. CTFPlayer *pVictim = ToTFPlayer( pEnt );
  15590. if ( iTauntAttack == TAUNTATK_MEDIC_UBERSLICE_IMPALE )
  15591. {
  15592. if ( pVictim )
  15593. {
  15594. // don't stun giants
  15595. if ( !pVictim->IsMiniBoss() )
  15596. {
  15597. pVictim->m_Shared.StunPlayer( 1.5f, 1.0, TF_STUN_BOTH | TF_STUN_NO_EFFECTS, this );
  15598. }
  15599. pVictim->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward, WorldSpaceCenter(), 1, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_UBERSLICE ) );
  15600. }
  15601. }
  15602. else
  15603. {
  15604. // Launch them up a little
  15605. vecForward = (WorldSpaceCenter() - pVictim->WorldSpaceCenter());
  15606. VectorNormalize( vecForward );
  15607. pVictim->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 12000, WorldSpaceCenter(), 500.0f, DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_TAUNTATK_UBERSLICE ) );
  15608. CWeaponMedigun *pMedigun = (CWeaponMedigun *) Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  15609. if ( pMedigun )
  15610. {
  15611. pMedigun->AddCharge( 0.5f );
  15612. }
  15613. }
  15614. }
  15615. }
  15616. if ( iTauntAttack == TAUNTATK_MEDIC_UBERSLICE_IMPALE )
  15617. {
  15618. m_iTauntAttack = TAUNTATK_MEDIC_UBERSLICE_KILL;
  15619. m_flTauntAttackTime = gpGlobals->curtime + 0.75;
  15620. }
  15621. }
  15622. else if ( iTauntAttack == TAUNTATK_DEMOMAN_BARBARIAN_SWING )
  15623. {
  15624. Vector vecForward;
  15625. AngleVectors( EyeAngles(), &vecForward );
  15626. Vector vecEnd = EyePosition() + vecForward * 128;
  15627. trace_t tr;
  15628. UTIL_TraceLine( EyePosition(), vecEnd, MASK_SOLID & ~CONTENTS_HITBOX, this, COLLISION_GROUP_PLAYER, &tr );
  15629. if ( tr.fraction < 1.0 )
  15630. {
  15631. CBaseEntity *pEnt = tr.m_pEnt;
  15632. if ( pEnt && pEnt->IsPlayer() && pEnt->GetTeamNumber() > LAST_SHARED_TEAM && pEnt->GetTeamNumber() != GetTeamNumber() )
  15633. {
  15634. vecForward = (WorldSpaceCenter() - pEnt->WorldSpaceCenter());
  15635. VectorNormalize( vecForward );
  15636. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 12000, WorldSpaceCenter(), 500.0f, DMG_CLUB, TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING ) );
  15637. }
  15638. }
  15639. }
  15640. else if ( iTauntAttack == TAUNTATK_ENGINEER_GUITAR_SMASH )
  15641. {
  15642. Vector vecForward;
  15643. AngleVectors( EyeAngles(), &vecForward );
  15644. Vector vecEnd = EyePosition() + vecForward * 128;
  15645. trace_t tr;
  15646. UTIL_TraceLine( EyePosition(), vecEnd, MASK_SOLID & ~CONTENTS_HITBOX, this, COLLISION_GROUP_PLAYER, &tr );
  15647. if ( tr.fraction < 1.0 )
  15648. {
  15649. CBaseEntity *pEnt = tr.m_pEnt;
  15650. if ( pEnt && pEnt->IsPlayer() && pEnt->GetTeamNumber() > LAST_SHARED_TEAM && pEnt->GetTeamNumber() != GetTeamNumber() )
  15651. {
  15652. vecForward = (WorldSpaceCenter() - pEnt->WorldSpaceCenter());
  15653. VectorNormalize( vecForward );
  15654. pEnt->TakeDamage( CTakeDamageInfo( this, this, GetActiveTFWeapon(), vecForward * 12, WorldSpaceCenter(), 500.0f, DMG_CLUB, TF_DMG_CUSTOM_TAUNTATK_ENGINEER_GUITAR_SMASH ) );
  15655. }
  15656. }
  15657. }
  15658. else if ( iTauntAttack == TAUNTATK_SHOW_ITEM )
  15659. {
  15660. if ( m_hTauntItem == NULL )
  15661. {
  15662. int itemCount = Inventory()->GetItemCount();
  15663. CUtlVector< CEconItemView * > hatVector;
  15664. for( int i=0; i<itemCount; ++i )
  15665. {
  15666. CEconItemView *econItemView = Inventory()->GetItem( i );
  15667. int iSlot = econItemView->GetStaticData()->GetLoadoutSlot( GetPlayerClass()->GetClassIndex() );
  15668. if ( iSlot == LOADOUT_POSITION_HEAD )
  15669. {
  15670. hatVector.AddToTail( econItemView );
  15671. }
  15672. }
  15673. if ( hatVector.Count() > 0 )
  15674. {
  15675. int which = RandomInt( 0, hatVector.Count()-1 );
  15676. CEconItemView *hatView = hatVector[ which ];
  15677. int iHandBone = LookupBone( "weapon_bone" );
  15678. if ( iHandBone != -1 )
  15679. {
  15680. Vector pos;
  15681. QAngle angles;
  15682. GetBonePosition( iHandBone, pos, angles );
  15683. pos = Vector( 0, 0, 50.0f );
  15684. m_hTauntItem = ItemGeneration()->GenerateItemFromScriptData( hatView, pos, angles, NULL );
  15685. if ( m_hTauntItem != NULL )
  15686. {
  15687. m_hTauntItem->AddSolidFlags( FSOLID_NOT_SOLID );
  15688. m_hTauntItem->SetOwnerEntity( this );
  15689. }
  15690. }
  15691. }
  15692. }
  15693. }
  15694. else if ( iTauntAttack == TAUNTATK_HIGHFIVE_PARTICLE )
  15695. {
  15696. if ( m_hHighFivePartner.Get() )
  15697. {
  15698. QAngle bodyAngles = BodyAngles();
  15699. bodyAngles.x = 0;
  15700. Vector vecForward, vecRight, vecUp;
  15701. AngleVectors( bodyAngles, &vecForward, &vecRight, &vecUp );
  15702. //Msg( "forward: %f %f %f right: %f %f %f up: %f %f %f\n", vecForward.x, vecForward.y, vecForward.z,
  15703. // vecRight.x, vecRight.y, vecRight.z,
  15704. // vecUp.x, vecUp.y, vecUp.z );
  15705. Vector vecParticle = GetAbsOrigin() + (vecForward * 30.0f) + (vecRight * -3.0f) + (vecUp * 87.0f);
  15706. //Msg( "particle: %f %f %f\n", vecParticle.x, vecParticle.y, vecParticle.z );
  15707. CEffectData data;
  15708. data.m_nHitBox = GetParticleSystemIndex( GetTeamNumber() == TF_TEAM_RED ? "highfive_red" : "highfive_blue" );
  15709. data.m_vOrigin = vecParticle;
  15710. data.m_vAngles = vec3_angle;
  15711. CPASFilter filter( data.m_vOrigin );
  15712. filter.SetIgnorePredictionCull( true );
  15713. te->DispatchEffect( filter, 0.0, data.m_vOrigin, "ParticleEffect", data );
  15714. }
  15715. }
  15716. else if ( iTauntAttack == TAUNTATK_RPS_PARTICLE )
  15717. {
  15718. if ( m_hHighFivePartner.Get() )
  15719. {
  15720. bool bInitiatorWin = ( m_iTauntRPSResult / 3 ) == 0;
  15721. // figure out for RPS
  15722. // 0:rock 1:paper 2:scissors
  15723. int iInitiator = m_iTauntRPSResult % 3;
  15724. int iReceiver = ( iInitiator + ( bInitiatorWin ? 2 : 1 ) ) % 3;
  15725. // offset to get the correct particle name
  15726. if ( bInitiatorWin )
  15727. {
  15728. iInitiator += 3;
  15729. }
  15730. else
  15731. {
  15732. iReceiver += 3;
  15733. }
  15734. if ( GetTeamNumber() == TF_TEAM_BLUE )
  15735. {
  15736. iInitiator += 6;
  15737. }
  15738. if ( m_hHighFivePartner->GetTeamNumber() == TF_TEAM_BLUE )
  15739. {
  15740. iReceiver += 6;
  15741. }
  15742. DispatchRPSEffect( this, s_pszTauntRPSParticleNames[iInitiator] );
  15743. DispatchRPSEffect( m_hHighFivePartner.Get(), s_pszTauntRPSParticleNames[iReceiver] );
  15744. // setup time to kill the opposing team loser
  15745. if ( GetTeamNumber() != m_hHighFivePartner->GetTeamNumber() )
  15746. {
  15747. m_iTauntAttack = TAUNTATK_RPS_KILL;
  15748. m_flTauntAttackTime = m_flTauntRemoveTime - 1.2f;
  15749. }
  15750. IGameEvent *event = gameeventmanager->CreateEvent( "rps_taunt_event" );
  15751. if ( event )
  15752. {
  15753. int iInitiatorRPS = m_iTauntRPSResult % 3;
  15754. int iReceiverRPS = ( iInitiatorRPS + ( bInitiatorWin ? 2 : 1 ) ) % 3;
  15755. event->SetInt( "winner", bInitiatorWin ? entindex() : m_hHighFivePartner.Get()->entindex() );
  15756. event->SetInt( "winner_rps", bInitiatorWin ? iInitiatorRPS : iReceiverRPS );
  15757. event->SetInt( "loser", bInitiatorWin ? m_hHighFivePartner.Get()->entindex() : entindex() );
  15758. event->SetInt( "loser_rps", bInitiatorWin ? iReceiverRPS : iInitiatorRPS );
  15759. gameeventmanager->FireEvent( event );
  15760. }
  15761. }
  15762. }
  15763. else if ( iTauntAttack == TAUNTATK_RPS_KILL )
  15764. {
  15765. if ( m_hHighFivePartner.Get() )
  15766. {
  15767. bool bInitiatorWin = ( m_iTauntRPSResult / 3 ) == 0;
  15768. CTFPlayer *pWinner = NULL;
  15769. CTFPlayer *pLoser = NULL;
  15770. if ( bInitiatorWin )
  15771. {
  15772. pWinner = this;
  15773. pLoser = m_hHighFivePartner.Get();
  15774. }
  15775. else
  15776. {
  15777. pWinner = m_hHighFivePartner.Get();
  15778. pLoser = this;
  15779. }
  15780. // gib the loser
  15781. pLoser->m_bSuicideExplode = true;
  15782. pLoser->TakeDamage( CTakeDamageInfo( pWinner, pWinner, NULL, 999, DMG_GENERIC, 0 ) );
  15783. }
  15784. }
  15785. // Particle Being played in VCD instead
  15786. //else if ( iTauntAttack == TAUNTATK_FLIP_LAND_PARTICLE )
  15787. //{
  15788. // if ( m_hHighFivePartner.Get() )
  15789. // {
  15790. // CEffectData data;
  15791. // data.m_nHitBox = GetParticleSystemIndex( GetTeamNumber() == TF_TEAM_RED ? "taunt_flip_land_red" : "taunt_flip_land_blue" );
  15792. // data.m_vOrigin = m_hHighFivePartner.Get()->GetAbsOrigin();
  15793. // data.m_vAngles = m_hHighFivePartner.Get()->GetAbsAngles();
  15794. // CPASFilter filter( data.m_vOrigin );
  15795. // filter.SetIgnorePredictionCull( true );
  15796. // te->DispatchEffect( filter, 0.0, data.m_vOrigin, "ParticleEffect", data );
  15797. // }
  15798. //}
  15799. }
  15800. //-----------------------------------------------------------------------------
  15801. // Purpose:
  15802. //-----------------------------------------------------------------------------
  15803. int CTFPlayer::GetSpecialDSP( void )
  15804. {
  15805. int iSpecialDSP = 0;
  15806. CALL_ATTRIB_HOOK_INT( iSpecialDSP, special_dsp );
  15807. return iSpecialDSP;
  15808. }
  15809. //-----------------------------------------------------------------------------
  15810. // Purpose: Play a one-shot scene
  15811. // Input :
  15812. // Output :
  15813. //-----------------------------------------------------------------------------
  15814. float CTFPlayer::PlayScene( const char *pszScene, float flDelay, AI_Response *response, IRecipientFilter *filter )
  15815. {
  15816. MDLCACHE_CRITICAL_SECTION();
  15817. // This is a lame way to detect a taunt!
  15818. if ( m_bInitTaunt )
  15819. {
  15820. m_bInitTaunt = false;
  15821. return InstancedScriptedScene( this, pszScene, &m_hTauntScene, flDelay, false, response, true, filter );
  15822. }
  15823. else
  15824. {
  15825. return InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response, true, filter );
  15826. }
  15827. }
  15828. //-----------------------------------------------------------------------------
  15829. // Purpose:
  15830. //-----------------------------------------------------------------------------
  15831. void CTFPlayer::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
  15832. {
  15833. BaseClass::ModifyOrAppendCriteria( criteriaSet );
  15834. // If we have 'disguiseclass' criteria, pretend that we are actually our
  15835. // disguise class. That way we just look up the scene we would play as if
  15836. // we were that class.
  15837. int disguiseIndex = criteriaSet.FindCriterionIndex( "disguiseclass" );
  15838. if ( disguiseIndex != -1 )
  15839. {
  15840. criteriaSet.AppendCriteria( "playerclass", criteriaSet.GetValue(disguiseIndex) );
  15841. }
  15842. else
  15843. {
  15844. if ( GetPlayerClass() )
  15845. {
  15846. criteriaSet.AppendCriteria( "playerclass", g_aPlayerClassNames_NonLocalized[ GetPlayerClass()->GetClassIndex() ] );
  15847. }
  15848. }
  15849. bool bRedTeam = ( GetTeamNumber() == TF_TEAM_RED );
  15850. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  15851. {
  15852. bRedTeam = ( m_Shared.GetDisguiseTeam() == TF_TEAM_RED );
  15853. }
  15854. criteriaSet.AppendCriteria( "OnRedTeam", bRedTeam ? "1" : "0" );
  15855. //=============================================================================
  15856. // HPE_BEGIN:
  15857. // [msmith] When in training, we kill a lot of guys... a WHOLE LOT. This was
  15858. // triggering some response sounds that got very annoying after a while.
  15859. //=============================================================================
  15860. if ( TFGameRules()->IsInTraining() )
  15861. {
  15862. criteriaSet.AppendCriteria( "recentkills", UTIL_VarArgs("%d", 0) );
  15863. }
  15864. else
  15865. {
  15866. criteriaSet.AppendCriteria( "recentkills", UTIL_VarArgs("%d", m_Shared.GetNumKillsInTime(30.0)) );
  15867. }
  15868. //=============================================================================
  15869. // HPE_END
  15870. //=============================================================================
  15871. int iTotalKills = 0;
  15872. PlayerStats_t *pStats = CTF_GameStats.FindPlayerStats( this );
  15873. if ( pStats )
  15874. {
  15875. iTotalKills = pStats->statsCurrentLife.m_iStat[TFSTAT_KILLS] + pStats->statsCurrentLife.m_iStat[TFSTAT_KILLASSISTS]+
  15876. pStats->statsCurrentLife.m_iStat[TFSTAT_BUILDINGSDESTROYED];
  15877. }
  15878. criteriaSet.AppendCriteria( "killsthislife", UTIL_VarArgs( "%d", iTotalKills ) );
  15879. criteriaSet.AppendCriteria( "disguised", m_Shared.InCond( TF_COND_DISGUISED ) ? "1" : "0" );
  15880. criteriaSet.AppendCriteria( "cloaked", ( m_Shared.IsStealthed() || m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) ? "1" : "0" );
  15881. criteriaSet.AppendCriteria( "invulnerable", m_Shared.InCond( TF_COND_INVULNERABLE ) ? "1" : "0" );
  15882. criteriaSet.AppendCriteria( "beinghealed", m_Shared.InCond( TF_COND_HEALTH_BUFF ) ? "1" : "0" );
  15883. criteriaSet.AppendCriteria( "waitingforplayers", (TFGameRules()->IsInWaitingForPlayers() || TFGameRules()->IsInPreMatch()) ? "1" : "0" );
  15884. criteriaSet.AppendCriteria( "stunned", m_Shared.IsControlStunned() ? "1" : "0" );
  15885. criteriaSet.AppendCriteria( "snared", m_Shared.IsSnared() ? "1" : "0" );
  15886. criteriaSet.AppendCriteria( "dodging", (m_Shared.InCond( TF_COND_PHASE ) || m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION )) ? "1" : "0" );
  15887. criteriaSet.AppendCriteria( "doublejumping", (m_Shared.GetAirDash()>0) ? "1" : "0" );
  15888. switch ( GetTFTeam()->GetRole() )
  15889. {
  15890. case TEAM_ROLE_DEFENDERS:
  15891. criteriaSet.AppendCriteria( "teamrole", "defense" );
  15892. break;
  15893. case TEAM_ROLE_ATTACKERS:
  15894. criteriaSet.AppendCriteria( "teamrole", "offense" );
  15895. break;
  15896. }
  15897. // Current weapon role
  15898. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  15899. if ( pActiveWeapon )
  15900. {
  15901. int iWeaponRole = pActiveWeapon->GetTFWpnData().m_iWeaponType;
  15902. switch( iWeaponRole )
  15903. {
  15904. case TF_WPN_TYPE_PRIMARY:
  15905. default:
  15906. criteriaSet.AppendCriteria( "weaponmode", "primary" );
  15907. break;
  15908. case TF_WPN_TYPE_SECONDARY:
  15909. criteriaSet.AppendCriteria( "weaponmode", "secondary" );
  15910. break;
  15911. case TF_WPN_TYPE_MELEE:
  15912. criteriaSet.AppendCriteria( "weaponmode", "melee" );
  15913. break;
  15914. case TF_WPN_TYPE_BUILDING:
  15915. criteriaSet.AppendCriteria( "weaponmode", "building" );
  15916. break;
  15917. case TF_WPN_TYPE_PDA:
  15918. criteriaSet.AppendCriteria( "weaponmode", "pda" );
  15919. break;
  15920. case TF_WPN_TYPE_ITEM1:
  15921. criteriaSet.AppendCriteria( "weaponmode", "item1" );
  15922. break;
  15923. case TF_WPN_TYPE_ITEM2:
  15924. criteriaSet.AppendCriteria( "weaponmode", "item2" );
  15925. break;
  15926. }
  15927. if ( WeaponID_IsSniperRifle( pActiveWeapon->GetWeaponID() ) )
  15928. {
  15929. CTFSniperRifle *pRifle = dynamic_cast<CTFSniperRifle*>(pActiveWeapon);
  15930. if ( pRifle && pRifle->IsZoomed() )
  15931. {
  15932. criteriaSet.AppendCriteria( "sniperzoomed", "1" );
  15933. }
  15934. }
  15935. else if ( pActiveWeapon->GetWeaponID() == TF_WEAPON_MINIGUN )
  15936. {
  15937. CTFMinigun *pMinigun = dynamic_cast<CTFMinigun*>(pActiveWeapon);
  15938. if ( pMinigun )
  15939. {
  15940. criteriaSet.AppendCriteria( "minigunfiretime", UTIL_VarArgs( "%.1f", pMinigun->GetFiringDuration() ) );
  15941. }
  15942. }
  15943. CEconItemView *pItem = pActiveWeapon->GetAttributeContainer()->GetItem();
  15944. if ( pItem && pItem->GetItemQuality() != AE_NORMAL )
  15945. {
  15946. criteriaSet.AppendCriteria( "item_name", pItem->GetStaticData()->GetDefinitionName() );
  15947. criteriaSet.AppendCriteria( "item_type_name", pItem->GetStaticData()->GetItemTypeName() );
  15948. }
  15949. }
  15950. // equipped loadout items
  15951. {
  15952. static const char* kSlotCriteriaName[CLASS_LOADOUT_POSITION_COUNT] =
  15953. {
  15954. "loadout_slot_primary", // LOADOUT_POSITION_PRIMARY = 0,
  15955. "loadout_slot_secondary", // LOADOUT_POSITION_SECONDARY,
  15956. "loadout_slot_melee", // LOADOUT_POSITION_MELEE,
  15957. "loadout_slot_utility", // LOADOUT_POSITION_UTILITY,
  15958. "loadout_slot_building", // LOADOUT_POSITION_BUILDING,
  15959. "loadout_slot_pda", // LOADOUT_POSITION_PDA,
  15960. "loadout_slot_pda2", // LOADOUT_POSITION_PDA2,
  15961. "loadout_slot_head", // LOADOUT_POSITION_HEAD,
  15962. "loadout_slot_misc", // LOADOUT_POSITION_MISC,
  15963. "loadout_slot_action", // LOADOUT_POSITION_ACTION,
  15964. "loadout_slot_misc2", // LOADOUT_POSITION_MISC2
  15965. "loadout_slot_taunt", // LOADOUT_POSITION_TAUNT
  15966. "loadout_slot_taunt2", // LOADOUT_POSITION_TAUNT2
  15967. "loadout_slot_taunt3", // LOADOUT_POSITION_TAUNT3
  15968. "loadout_slot_taunt4", // LOADOUT_POSITION_TAUNT4
  15969. "loadout_slot_taunt5", // LOADOUT_POSITION_TAUNT5
  15970. "loadout_slot_taunt6", // LOADOUT_POSITION_TAUNT6
  15971. "loadout_slot_taunt7", // LOADOUT_POSITION_TAUNT7
  15972. "loadout_slot_taunt8", // LOADOUT_POSITION_TAUNT8
  15973. #ifdef STAGING_ONLY
  15974. "loadout_slot_pda3", // LOADOUT_POSITION_PDA3,
  15975. //"loadout_slot_misc3", // LOADOUT_POSITION_MISC3
  15976. //"loadout_slot_misc4", // LOADOUT_POSITION_MISC4
  15977. //"loadout_slot_misc5", // LOADOUT_POSITION_MISC5
  15978. //"loadout_slot_misc6", // LOADOUT_POSITION_MISC6
  15979. //"loadout_slot_misc7", // LOADOUT_POSITION_MISC3
  15980. //"loadout_slot_misc8", // LOADOUT_POSITION_MISC4
  15981. //"loadout_slot_misc9", // LOADOUT_POSITION_MISC5
  15982. //"loadout_slot_misc10", // LOADOUT_POSITION_MISC6
  15983. "loadout_slot_building2", // LOADOUT_POSITION_BUILDING2,
  15984. #endif // STAGING_ONLY
  15985. };
  15986. COMPILE_TIME_ASSERT( ARRAYSIZE(kSlotCriteriaName) == CLASS_LOADOUT_POSITION_COUNT );
  15987. CEconItemView *pItem = NULL;
  15988. for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; ++i )
  15989. {
  15990. if ( m_EquippedLoadoutItemIndices[i] != LOADOUT_SLOT_USE_BASE_ITEM )
  15991. {
  15992. pItem = m_Inventory.GetInventoryItemByItemID( m_EquippedLoadoutItemIndices[i] );
  15993. if ( pItem )
  15994. {
  15995. criteriaSet.AppendCriteria( kSlotCriteriaName[i], pItem->GetStaticData()->GetDefinitionName() );
  15996. }
  15997. }
  15998. }
  15999. }
  16000. // Player under crosshair
  16001. trace_t tr;
  16002. Vector forward;
  16003. EyeVectors( &forward );
  16004. UTIL_TraceLine( EyePosition(), EyePosition() + (forward * MAX_TRACE_LENGTH), MASK_BLOCKLOS_AND_NPCS, this, COLLISION_GROUP_NONE, &tr );
  16005. if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
  16006. {
  16007. CBaseEntity *pEntity = tr.m_pEnt;
  16008. if ( pEntity && pEntity->IsPlayer() )
  16009. {
  16010. CTFPlayer *pTFPlayer = ToTFPlayer(pEntity);
  16011. if ( pTFPlayer )
  16012. {
  16013. int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
  16014. if ( !InSameTeam(pTFPlayer) )
  16015. {
  16016. // Prevent spotting stealthed enemies who haven't been exposed recently
  16017. if ( pTFPlayer->m_Shared.InCond( TF_COND_STEALTHED ) )
  16018. {
  16019. if ( pTFPlayer->m_Shared.GetLastStealthExposedTime() < (gpGlobals->curtime - 3.0) )
  16020. {
  16021. iClass = TF_CLASS_UNDEFINED;
  16022. }
  16023. else
  16024. {
  16025. iClass = TF_CLASS_SPY;
  16026. }
  16027. }
  16028. else if ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  16029. {
  16030. iClass = pTFPlayer->m_Shared.GetDisguiseClass();
  16031. }
  16032. }
  16033. if ( iClass > TF_CLASS_UNDEFINED && iClass <= TF_LAST_NORMAL_CLASS )
  16034. {
  16035. criteriaSet.AppendCriteria( "crosshair_on", g_aPlayerClassNames_NonLocalized[iClass] );
  16036. int iVisibleTeam = pTFPlayer->GetTeamNumber();
  16037. if ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  16038. {
  16039. iVisibleTeam = pTFPlayer->m_Shared.GetDisguiseTeam();
  16040. }
  16041. if ( iVisibleTeam != GetTeamNumber() )
  16042. {
  16043. criteriaSet.AppendCriteria( "crosshair_enemy", "yes" );
  16044. }
  16045. }
  16046. }
  16047. }
  16048. }
  16049. // Previous round win
  16050. bool bLoser = ( TFGameRules()->GetPreviousRoundWinners() != TEAM_UNASSIGNED && TFGameRules()->GetPreviousRoundWinners() != GetPrevRoundTeamNum() );
  16051. criteriaSet.AppendCriteria( "LostRound", bLoser ? "1" : "0" );
  16052. bool bPrevRoundTie = ( ( TFGameRules()->GetRoundsPlayed() > 0 ) && ( TFGameRules()->GetPreviousRoundWinners() == TEAM_UNASSIGNED ) );
  16053. criteriaSet.AppendCriteria( "PrevRoundWasTie", bPrevRoundTie ? "1" : "0" );
  16054. // Control points
  16055. CTriggerAreaCapture *pAreaTrigger = GetControlPointStandingOn();
  16056. if ( pAreaTrigger )
  16057. {
  16058. CTeamControlPoint *pCP = pAreaTrigger->GetControlPoint();
  16059. if ( pCP )
  16060. {
  16061. if ( pCP->GetOwner() == GetTeamNumber() )
  16062. {
  16063. criteriaSet.AppendCriteria( "OnFriendlyControlPoint", "1" );
  16064. }
  16065. else
  16066. {
  16067. if ( TeamplayGameRules()->TeamMayCapturePoint( GetTeamNumber(), pCP->GetPointIndex() ) &&
  16068. TeamplayGameRules()->PlayerMayCapturePoint( this, pCP->GetPointIndex() ) )
  16069. {
  16070. criteriaSet.AppendCriteria( "OnCappableControlPoint", "1" );
  16071. }
  16072. }
  16073. }
  16074. }
  16075. bool bIsBonusTime = false;
  16076. bool bGameOver = false;
  16077. // Current game state
  16078. criteriaSet.AppendCriteria( "GameRound", UTIL_VarArgs( "%d", TFGameRules()->State_Get() ) );
  16079. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
  16080. {
  16081. criteriaSet.AppendCriteria( "OnWinningTeam", ( TFGameRules()->GetWinningTeam() == GetTeamNumber() ) ? "1" : "0" );
  16082. bIsBonusTime = ( TFGameRules()->GetStateTransitionTime() > gpGlobals->curtime );
  16083. bGameOver = TFGameRules()->IsGameOver();
  16084. }
  16085. // Number of rounds played
  16086. criteriaSet.AppendCriteria( "RoundsPlayed", UTIL_VarArgs( "%d", TFGameRules()->GetRoundsPlayed() ) );
  16087. // Is this a 6v6 match?
  16088. CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
  16089. bool bIsComp6v6 = ( pMatch && pMatch->m_eMatchGroup == k_nMatchGroup_Ladder_6v6 );
  16090. criteriaSet.AppendCriteria( "IsComp6v6", bIsComp6v6 ? "1" : "0" );
  16091. bool bIsCompWinner = m_Shared.InCond( TF_COND_COMPETITIVE_WINNER );
  16092. criteriaSet.AppendCriteria( "IsCompWinner", bIsCompWinner ? "1" : "0" );
  16093. // Holiday Taunt
  16094. int iSpecialTaunt = 0;
  16095. if ( pActiveWeapon )
  16096. {
  16097. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iSpecialTaunt, special_taunt );
  16098. }
  16099. // only roll random halloween taunt if the active weapon doesn't have special taunt attribute
  16100. if ( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) && iSpecialTaunt == 0 )
  16101. {
  16102. if ( !TFGameRules()->IsMannVsMachineMode() || ( GetTeamNumber() != TF_TEAM_PVE_INVADERS ) )
  16103. {
  16104. if ( pActiveWeapon )
  16105. {
  16106. int iRageTaunt = 0;
  16107. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iRageTaunt, burn_damage_earns_rage );
  16108. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iRageTaunt, generate_rage_on_dmg );
  16109. int iWeaponID = pActiveWeapon->GetWeaponID();
  16110. if ( iWeaponID != TF_WEAPON_LUNCHBOX && !( iRageTaunt && m_Shared.GetRageMeter() >= 100.f ) )
  16111. {
  16112. float frand = (float) rand() / VALVE_RAND_MAX;
  16113. if ( frand < 0.4f )
  16114. {
  16115. criteriaSet.AppendCriteria( "IsHalloweenTaunt", "1" );
  16116. }
  16117. }
  16118. }
  16119. }
  16120. }
  16121. if ( TFGameRules()->IsHolidayActive( kHoliday_AprilFools ) && iSpecialTaunt == 0 )
  16122. {
  16123. if ( pActiveWeapon )
  16124. {
  16125. int iRageTaunt = 0;
  16126. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iRageTaunt, burn_damage_earns_rage );
  16127. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iRageTaunt, generate_rage_on_dmg );
  16128. int iWeaponID = pActiveWeapon->GetWeaponID();
  16129. if ( iWeaponID != TF_WEAPON_LUNCHBOX && !( iRageTaunt && m_Shared.GetRageMeter() >= 100.f ) )
  16130. {
  16131. float frand = (float)rand() / VALVE_RAND_MAX;
  16132. if ( frand < 0.8f )
  16133. {
  16134. criteriaSet.AppendCriteria( "IsAprilFoolsTaunt", "1" );
  16135. }
  16136. }
  16137. }
  16138. }
  16139. // Force the thriller taunt if we have the thriller condition
  16140. if( m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
  16141. {
  16142. criteriaSet.AppendCriteria( "IsHalloweenTaunt", "1" );
  16143. }
  16144. // Only allow these rules if in the holiday
  16145. if ( TFGameRules()->IsHolidayActive( kHoliday_HalloweenOrFullMoon ) && iSpecialTaunt == 0 )
  16146. {
  16147. // Halloween costume sets
  16148. if ( IsRobotCostumeEquipped() )
  16149. {
  16150. criteriaSet.AppendCriteria( "IsRobotCostume", "1" );
  16151. }
  16152. else if ( IsDemowolf() )
  16153. {
  16154. criteriaSet.AppendCriteria( "IsDemowolf", "1" );
  16155. }
  16156. else if ( IsFrankenHeavy() )
  16157. {
  16158. criteriaSet.AppendCriteria( "IsFrankenHeavy", "1" );
  16159. }
  16160. // Single items with response rules
  16161. else
  16162. {
  16163. static CSchemaAttributeDefHandle pAttrDef_AdditionalHalloweenResponseRule( "additional halloween response criteria name" );
  16164. FOR_EACH_VEC_BACK( m_hMyWearables, wbl )
  16165. {
  16166. CEconWearable *pWearable = m_hMyWearables[wbl];
  16167. if ( pWearable && pWearable->GetAttributeContainer()->GetItem() )
  16168. {
  16169. const char *pszAdditionalResponseRule = NULL;
  16170. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pWearable->GetAttributeContainer()->GetItem(), pAttrDef_AdditionalHalloweenResponseRule, &pszAdditionalResponseRule ) )
  16171. {
  16172. criteriaSet.AppendCriteria( pszAdditionalResponseRule, "1" );
  16173. }
  16174. }
  16175. }
  16176. }
  16177. // Zombie could work in addition to any of these
  16178. if ( IsZombieCostumeEquipped() )
  16179. {
  16180. criteriaSet.AppendCriteria( "IsZombieCostume", "1" );
  16181. }
  16182. }
  16183. if ( TFGameRules() && TFGameRules()->GetActiveBoss() && ( TFGameRules()->GetActiveBoss()->GetBossType() == HALLOWEEN_BOSS_MERASMUS ) )
  16184. {
  16185. CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
  16186. if ( pMerasmus )
  16187. {
  16188. if ( pMerasmus->IsHiding() )
  16189. {
  16190. criteriaSet.AppendCriteria( "IsMerasmusHiding", "1" );
  16191. }
  16192. }
  16193. }
  16194. bool bInHell = false;
  16195. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) && ( TFGameRules()->ArePlayersInHell() == true ) )
  16196. {
  16197. bInHell = true;
  16198. }
  16199. criteriaSet.AppendCriteria( "IsInHell", bInHell ? "1" : "0" );
  16200. if ( TFGameRules()->IsHolidayActive( kHoliday_HalloweenOrFullMoonOrValentines ) )
  16201. {
  16202. if ( IsFairyHeavy() )
  16203. {
  16204. criteriaSet.AppendCriteria( "IsFairyHeavy", "1" );
  16205. }
  16206. }
  16207. if ( TFGameRules()->IsMannVsMachineMode() )
  16208. {
  16209. if ( GetTeamNumber() == TF_TEAM_PVE_DEFENDERS )
  16210. {
  16211. criteriaSet.AppendCriteria( "IsMvMDefender", "1" );
  16212. }
  16213. }
  16214. }
  16215. //-----------------------------------------------------------------------------
  16216. // Purpose:
  16217. //-----------------------------------------------------------------------------
  16218. CTriggerAreaCapture *CTFPlayer::GetControlPointStandingOn( void )
  16219. {
  16220. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  16221. if ( root )
  16222. {
  16223. for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
  16224. {
  16225. CBaseEntity *pTouch = link->entityTouched;
  16226. if ( pTouch && pTouch->IsSolidFlagSet( FSOLID_TRIGGER ) && pTouch->IsBSPModel() )
  16227. {
  16228. CTriggerAreaCapture *pAreaTrigger = dynamic_cast<CTriggerAreaCapture*>(pTouch);
  16229. if ( pAreaTrigger )
  16230. return pAreaTrigger;
  16231. }
  16232. }
  16233. }
  16234. return NULL;
  16235. }
  16236. //-----------------------------------------------------------------------------
  16237. // Usable by CTFPlayers, not just CTFBots
  16238. class CTFPlayertPathCost : public IPathCost
  16239. {
  16240. public:
  16241. CTFPlayertPathCost( const CTFPlayer *me )
  16242. {
  16243. m_me = me;
  16244. m_stepHeight = StepHeight;
  16245. m_maxJumpHeight = 72.0f;
  16246. m_maxDropHeight = 200.0f;
  16247. }
  16248. virtual float operator()( CNavArea *baseArea, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length ) const
  16249. {
  16250. VPROF_BUDGET( "CTFPlayertPathCost::operator()", "NextBot" );
  16251. CTFNavArea *area = (CTFNavArea *)baseArea;
  16252. if ( fromArea == NULL )
  16253. {
  16254. // first area in path, no cost
  16255. return 0.0f;
  16256. }
  16257. else
  16258. {
  16259. if ( !m_me->IsAreaTraversable( area ) )
  16260. {
  16261. return -1.0f;
  16262. }
  16263. // don't path through enemy spawn rooms
  16264. if ( ( m_me->GetTeamNumber() == TF_TEAM_RED && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE ) ) ||
  16265. ( m_me->GetTeamNumber() == TF_TEAM_BLUE && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_RED ) ) )
  16266. {
  16267. if ( !TFGameRules()->RoundHasBeenWon() )
  16268. {
  16269. return -1.0f;
  16270. }
  16271. }
  16272. // compute distance traveled along path so far
  16273. float dist;
  16274. if ( ladder )
  16275. {
  16276. dist = ladder->m_length;
  16277. }
  16278. else if ( length > 0.0 )
  16279. {
  16280. dist = length;
  16281. }
  16282. else
  16283. {
  16284. dist = ( area->GetCenter() - fromArea->GetCenter() ).Length();
  16285. }
  16286. // check height change
  16287. float deltaZ = fromArea->ComputeAdjacentConnectionHeightChange( area );
  16288. if ( deltaZ >= m_stepHeight )
  16289. {
  16290. if ( deltaZ >= m_maxJumpHeight )
  16291. {
  16292. // too high to reach
  16293. return -1.0f;
  16294. }
  16295. // jumping is slower than flat ground
  16296. const float jumpPenalty = 2.0f;
  16297. dist *= jumpPenalty;
  16298. }
  16299. else if ( deltaZ < -m_maxDropHeight )
  16300. {
  16301. // too far to drop
  16302. return -1.0f;
  16303. }
  16304. float cost = dist + fromArea->GetCostSoFar();
  16305. return cost;
  16306. }
  16307. }
  16308. const CTFPlayer *m_me;
  16309. float m_stepHeight;
  16310. float m_maxJumpHeight;
  16311. float m_maxDropHeight;
  16312. };
  16313. //-----------------------------------------------------------------------------
  16314. // Given a vector of points, return the point we can actually travel to the quickest (requires a nav mesh)
  16315. CTeamControlPoint *CTFPlayer::SelectClosestControlPointByTravelDistance( CUtlVector< CTeamControlPoint * > *pointVector ) const
  16316. {
  16317. if ( !pointVector || pointVector->Count() == 0 )
  16318. {
  16319. return NULL;
  16320. }
  16321. if ( GetLastKnownArea() == NULL )
  16322. {
  16323. return NULL;
  16324. }
  16325. CTeamControlPoint *closestPoint = NULL;
  16326. float closestPointTravelRange = FLT_MAX;
  16327. CTFPlayertPathCost cost( this );
  16328. for( int i=0; i<pointVector->Count(); ++i )
  16329. {
  16330. CTeamControlPoint *point = pointVector->Element(i);
  16331. if ( IsBot() && point->ShouldBotsIgnore() )
  16332. continue;
  16333. float travelRange = NavAreaTravelDistance( GetLastKnownArea(), TheTFNavMesh()->GetControlPointCenterArea( point->GetPointIndex() ), cost );
  16334. if ( travelRange >= 0.0 && travelRange < closestPointTravelRange )
  16335. {
  16336. closestPoint = point;
  16337. closestPointTravelRange = travelRange;
  16338. }
  16339. }
  16340. return closestPoint;
  16341. }
  16342. //-----------------------------------------------------------------------------
  16343. // Purpose:
  16344. //-----------------------------------------------------------------------------
  16345. bool CTFPlayer::CanHearAndReadChatFrom( CBasePlayer *pPlayer )
  16346. {
  16347. // always can hear coach
  16348. if ( m_hCoach && m_hCoach == pPlayer )
  16349. return true;
  16350. // always can hear student
  16351. if ( m_hStudent && m_hStudent == pPlayer )
  16352. return true;
  16353. // can always hear the console unless we're ignoring all chat
  16354. if ( !pPlayer )
  16355. return m_iIgnoreGlobalChat != CHAT_IGNORE_ALL;
  16356. // check if we're ignoring all chat
  16357. if ( m_iIgnoreGlobalChat == CHAT_IGNORE_ALL )
  16358. return false;
  16359. // check if we're ignoring all but teammates
  16360. if ( ( m_iIgnoreGlobalChat == CHAT_IGNORE_TEAM ) && ( g_pGameRules->PlayerRelationship( this, pPlayer ) != GR_TEAMMATE ) )
  16361. return false;
  16362. // Always allow teams to hear each other in TD mode
  16363. if ( TFGameRules()->IsMannVsMachineMode() )
  16364. return ( GetTeamNumber() == pPlayer->GetTeamNumber() );
  16365. if ( pPlayer->m_lifeState != LIFE_ALIVE && m_lifeState == LIFE_ALIVE )
  16366. {
  16367. // Everyone can chat like normal when the round/game ends
  16368. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN || TFGameRules()->State_Get() == GR_STATE_GAME_OVER )
  16369. return true;
  16370. if ( !tf_gravetalk.GetBool() )
  16371. return false;
  16372. }
  16373. return true;
  16374. }
  16375. //-----------------------------------------------------------------------------
  16376. // Purpose:
  16377. //-----------------------------------------------------------------------------
  16378. bool CTFPlayer::CanBeAutobalanced()
  16379. {
  16380. if ( DuelMiniGame_IsInDuel( this ) )
  16381. return false;
  16382. if ( IsBot() )
  16383. return false;
  16384. if ( IsCoaching() )
  16385. return false;
  16386. if ( GetCoach() )
  16387. return false;
  16388. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  16389. return false;
  16390. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  16391. return false;
  16392. return true;
  16393. }
  16394. //-----------------------------------------------------------------------------
  16395. // Purpose:
  16396. //-----------------------------------------------------------------------------
  16397. IResponseSystem *CTFPlayer::GetResponseSystem()
  16398. {
  16399. int iClass = GetPlayerClass()->GetClassIndex();
  16400. if ( m_bSpeakingConceptAsDisguisedSpy && m_Shared.InCond( TF_COND_DISGUISED ) )
  16401. {
  16402. iClass = m_Shared.GetDisguiseClass();
  16403. }
  16404. bool bValidClass = ( iClass >= TF_CLASS_SCOUT && iClass <= TF_LAST_NORMAL_CLASS );
  16405. bool bValidConcept = ( m_iCurrentConcept >= 0 && m_iCurrentConcept < MP_TF_CONCEPT_COUNT );
  16406. Assert( bValidClass );
  16407. Assert( bValidConcept );
  16408. if ( !bValidClass || !bValidConcept )
  16409. {
  16410. return BaseClass::GetResponseSystem();
  16411. }
  16412. else
  16413. {
  16414. return TFGameRules()->m_ResponseRules[iClass].m_ResponseSystems[m_iCurrentConcept];
  16415. }
  16416. }
  16417. //-----------------------------------------------------------------------------
  16418. // Purpose:
  16419. //-----------------------------------------------------------------------------
  16420. bool CTFPlayer::SpeakConceptIfAllowed( int iConcept, const char *modifiers, char *pszOutResponseChosen, size_t bufsize, IRecipientFilter *filter )
  16421. {
  16422. if ( !IsAlive() )
  16423. return false;
  16424. bool bReturn = false;
  16425. if ( IsSpeaking() )
  16426. {
  16427. if ( iConcept != MP_CONCEPT_DIED )
  16428. return false;
  16429. }
  16430. if ( iConcept == MP_CONCEPT_PLAYER_ASK_FOR_BALL )
  16431. {
  16432. if ( !SayAskForBall() )
  16433. return false;
  16434. }
  16435. // Save the current concept.
  16436. m_iCurrentConcept = iConcept;
  16437. if ( m_Shared.InCond( TF_COND_DISGUISED ) && !filter && ( iConcept != MP_CONCEPT_KILLED_PLAYER ) )
  16438. {
  16439. CSingleUserRecipientFilter filter(this);
  16440. int iEnemyTeam = ( GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED;
  16441. // test, enemies and myself
  16442. CTeamRecipientFilter disguisedFilter( iEnemyTeam );
  16443. disguisedFilter.AddRecipient( this );
  16444. CMultiplayer_Expresser *pExpresser = GetMultiplayerExpresser();
  16445. Assert( pExpresser );
  16446. pExpresser->AllowMultipleScenes();
  16447. // play disguised concept to enemies and myself
  16448. char buf[128];
  16449. Q_snprintf( buf, sizeof(buf), "disguiseclass:%s", g_aPlayerClassNames_NonLocalized[ m_Shared.GetDisguiseClass() ] );
  16450. if ( modifiers )
  16451. {
  16452. Q_strncat( buf, ",", sizeof(buf), 1 );
  16453. Q_strncat( buf, modifiers, sizeof(buf), COPY_ALL_CHARACTERS );
  16454. }
  16455. m_bSpeakingConceptAsDisguisedSpy = true;
  16456. bool bPlayedDisguised = SpeakIfAllowed( g_pszMPConcepts[iConcept], buf, pszOutResponseChosen, bufsize, &disguisedFilter );
  16457. m_bSpeakingConceptAsDisguisedSpy = false;
  16458. // test, everyone except enemies and myself
  16459. CBroadcastRecipientFilter undisguisedFilter;
  16460. undisguisedFilter.RemoveRecipientsByTeam( GetGlobalTFTeam(iEnemyTeam) );
  16461. undisguisedFilter.RemoveRecipient( this );
  16462. // play normal concept to teammates
  16463. bool bPlayedNormally = SpeakIfAllowed( g_pszMPConcepts[iConcept], modifiers, pszOutResponseChosen, bufsize, &undisguisedFilter );
  16464. pExpresser->DisallowMultipleScenes();
  16465. bReturn = ( bPlayedDisguised || bPlayedNormally );
  16466. }
  16467. else
  16468. {
  16469. if ( IsPlayerClass( TF_CLASS_SOLDIER ) && !filter && iConcept == MP_CONCEPT_PLAYER_MEDIC )
  16470. {
  16471. // Prevent the medic call+effect when we have the weapon_blocks_healing attribute
  16472. CTFWeaponBase *pTFWeapon = GetActiveTFWeapon();
  16473. if ( pTFWeapon )
  16474. {
  16475. int iBlockHealing = 0;
  16476. CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFWeapon, iBlockHealing, weapon_blocks_healing );
  16477. if ( iBlockHealing )
  16478. return false;
  16479. }
  16480. }
  16481. // play normally
  16482. bReturn = SpeakIfAllowed( g_pszMPConcepts[iConcept], modifiers, pszOutResponseChosen, bufsize, filter );
  16483. }
  16484. //Add bubble on top of a player calling for medic.
  16485. if ( bReturn )
  16486. {
  16487. if ( iConcept == MP_CONCEPT_PLAYER_MEDIC )
  16488. {
  16489. SaveMe();
  16490. }
  16491. }
  16492. return bReturn;
  16493. }
  16494. //-----------------------------------------------------------------------------
  16495. // Purpose:
  16496. //-----------------------------------------------------------------------------
  16497. void CTFPlayer::UpdateExpression( void )
  16498. {
  16499. char szScene[ MAX_PATH ];
  16500. if ( !GetResponseSceneFromConcept( MP_CONCEPT_PLAYER_EXPRESSION, szScene, sizeof( szScene ) ) )
  16501. {
  16502. ClearExpression();
  16503. m_flNextRandomExpressionTime = gpGlobals->curtime + RandomFloat(30,40);
  16504. return;
  16505. }
  16506. // Ignore updates that choose the same scene
  16507. if ( m_iszExpressionScene != NULL_STRING && stricmp( STRING(m_iszExpressionScene), szScene ) == 0 )
  16508. return;
  16509. if ( m_hExpressionSceneEnt )
  16510. {
  16511. ClearExpression();
  16512. }
  16513. m_iszExpressionScene = AllocPooledString( szScene );
  16514. float flDuration = InstancedScriptedScene( this, szScene, &m_hExpressionSceneEnt, 0.0, true, NULL, true );
  16515. m_flNextRandomExpressionTime = gpGlobals->curtime + flDuration;
  16516. }
  16517. //-----------------------------------------------------------------------------
  16518. // Purpose:
  16519. //-----------------------------------------------------------------------------
  16520. void CTFPlayer::ClearExpression( void )
  16521. {
  16522. if ( m_hExpressionSceneEnt != NULL )
  16523. {
  16524. StopScriptedScene( this, m_hExpressionSceneEnt );
  16525. }
  16526. m_flNextRandomExpressionTime = gpGlobals->curtime;
  16527. }
  16528. //-----------------------------------------------------------------------------
  16529. // Purpose: Only show subtitle to enemy if we're disguised as the enemy
  16530. //-----------------------------------------------------------------------------
  16531. bool CTFPlayer::ShouldShowVoiceSubtitleToEnemy( void )
  16532. {
  16533. return ( m_Shared.InCond( TF_COND_DISGUISED ) && m_Shared.GetDisguiseTeam() != GetTeamNumber() );
  16534. }
  16535. //-----------------------------------------------------------------------------
  16536. // Purpose: Don't allow rapid-fire voice commands
  16537. //-----------------------------------------------------------------------------
  16538. bool CTFPlayer::CanSpeakVoiceCommand( void )
  16539. {
  16540. return ( gpGlobals->curtime > m_flNextVoiceCommandTime );
  16541. }
  16542. //-----------------------------------------------------------------------------
  16543. // Purpose: Note the time we're allowed to next speak a voice command
  16544. //-----------------------------------------------------------------------------
  16545. void CTFPlayer::NoteSpokeVoiceCommand( const char *pszScenePlayed )
  16546. {
  16547. Assert( pszScenePlayed );
  16548. float flTimeSinceAllowedVoice = gpGlobals->curtime - m_flNextVoiceCommandTime;
  16549. // if its longer than 5 seconds, reset the counter
  16550. if ( flTimeSinceAllowedVoice > 5.0f )
  16551. {
  16552. m_iVoiceSpamCounter = 0;
  16553. }
  16554. // if its less than a second past the allowed time, player is spamming
  16555. else if ( flTimeSinceAllowedVoice < 1.0f )
  16556. {
  16557. m_iVoiceSpamCounter++;
  16558. }
  16559. m_flNextVoiceCommandTime = gpGlobals->curtime + MIN( GetSceneDuration( pszScenePlayed ), tf_max_voice_speak_delay.GetFloat() );
  16560. if ( m_iVoiceSpamCounter > 0 )
  16561. {
  16562. m_flNextVoiceCommandTime += m_iVoiceSpamCounter * 0.5f;
  16563. }
  16564. }
  16565. //-----------------------------------------------------------------------------
  16566. // Purpose:
  16567. //-----------------------------------------------------------------------------
  16568. bool CTFPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
  16569. {
  16570. bool bIsMedic = false;
  16571. //Do Lag comp on medics trying to heal team mates.
  16572. if ( IsPlayerClass( TF_CLASS_MEDIC ) == true )
  16573. {
  16574. bIsMedic = true;
  16575. if ( pPlayer->GetTeamNumber() == GetTeamNumber() )
  16576. {
  16577. CWeaponMedigun *pWeapon = dynamic_cast <CWeaponMedigun*>( GetActiveWeapon() );
  16578. if ( pWeapon && pWeapon->GetHealTarget() )
  16579. {
  16580. if ( pWeapon->GetHealTarget() == pPlayer )
  16581. return true;
  16582. else
  16583. return false;
  16584. }
  16585. }
  16586. }
  16587. if ( pPlayer->GetTeamNumber() == GetTeamNumber() && bIsMedic == false )
  16588. return false;
  16589. // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
  16590. if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) )
  16591. return false;
  16592. const Vector &vMyOrigin = GetAbsOrigin();
  16593. const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
  16594. // get max distance player could have moved within max lag compensation time,
  16595. // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
  16596. float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
  16597. // If the player is within this distance, lag compensate them in case they're running past us.
  16598. if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance )
  16599. return true;
  16600. // If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
  16601. Vector vForward;
  16602. AngleVectors( pCmd->viewangles, &vForward );
  16603. Vector vDiff = vHisOrigin - vMyOrigin;
  16604. VectorNormalize( vDiff );
  16605. float flCosAngle = 0.707107f; // 45 degree angle
  16606. if ( vForward.Dot( vDiff ) < flCosAngle )
  16607. return false;
  16608. return true;
  16609. }
  16610. //-----------------------------------------------------------------------------
  16611. // Purpose:
  16612. //-----------------------------------------------------------------------------
  16613. void CTFPlayer::SpeakWeaponFire( int iCustomConcept )
  16614. {
  16615. if ( iCustomConcept == MP_CONCEPT_NONE )
  16616. {
  16617. if ( m_flNextSpeakWeaponFire > gpGlobals->curtime )
  16618. return;
  16619. iCustomConcept = MP_CONCEPT_FIREWEAPON;
  16620. }
  16621. m_flNextSpeakWeaponFire = gpGlobals->curtime + 5;
  16622. char szScene[ MAX_PATH ];
  16623. if ( !GetResponseSceneFromConcept( iCustomConcept, szScene, sizeof( szScene ) ) )
  16624. return;
  16625. float flDuration = InstancedScriptedScene( this, szScene, &m_hExpressionSceneEnt, 0.0, true, NULL, true );
  16626. m_flNextSpeakWeaponFire = gpGlobals->curtime + flDuration;
  16627. }
  16628. //-----------------------------------------------------------------------------
  16629. // Purpose:
  16630. //-----------------------------------------------------------------------------
  16631. void CTFPlayer::ClearWeaponFireScene( void )
  16632. {
  16633. m_flNextSpeakWeaponFire = gpGlobals->curtime;
  16634. }
  16635. //-----------------------------------------------------------------------------
  16636. // Purpose:
  16637. //-----------------------------------------------------------------------------
  16638. int CTFPlayer::DrawDebugTextOverlays(void)
  16639. {
  16640. int text_offset = BaseClass::DrawDebugTextOverlays();
  16641. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  16642. {
  16643. char tempstr[512];
  16644. Q_snprintf( tempstr, sizeof( tempstr ),"Health: %d / %d ( %.1f )", GetHealth(), GetMaxHealth(), (float)GetHealth() / (float)GetMaxHealth() );
  16645. EntityText(text_offset,tempstr,0);
  16646. text_offset++;
  16647. }
  16648. return text_offset;
  16649. }
  16650. //-----------------------------------------------------------------------------
  16651. // Purpose: Get response scene corresponding to concept
  16652. //-----------------------------------------------------------------------------
  16653. bool CTFPlayer::GetResponseSceneFromConcept( int iConcept, char *chSceneBuffer, int numSceneBufferBytes )
  16654. {
  16655. AI_Response response;
  16656. bool result = SpeakConcept( response, iConcept );
  16657. if ( result )
  16658. {
  16659. // Apply contexts
  16660. if ( response.IsApplyContextToWorld() )
  16661. {
  16662. CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) );
  16663. if ( pEntity )
  16664. {
  16665. pEntity->AddContext( response.GetContext() );
  16666. }
  16667. }
  16668. else
  16669. {
  16670. AddContext( response.GetContext() );
  16671. }
  16672. const char *szResponse = response.GetResponsePtr();
  16673. Q_strncpy( chSceneBuffer, szResponse, numSceneBufferBytes );
  16674. return true;
  16675. }
  16676. return false;
  16677. }
  16678. //-----------------------------------------------------------------------------
  16679. // Purpose:calculate a score for this player. higher is more likely to be switched
  16680. //-----------------------------------------------------------------------------
  16681. int CTFPlayer::CalculateTeamBalanceScore( void )
  16682. {
  16683. int iScore = BaseClass::CalculateTeamBalanceScore();
  16684. // switch engineers less often
  16685. if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  16686. {
  16687. iScore -= 120;
  16688. }
  16689. return iScore;
  16690. }
  16691. //-----------------------------------------------------------------------------
  16692. // Purpose: Exclude during win state
  16693. //-----------------------------------------------------------------------------
  16694. void CTFPlayer::AwardAchievement( int iAchievement, int iCount )
  16695. {
  16696. if ( TFGameRules()->State_Get() >= GR_STATE_TEAM_WIN )
  16697. {
  16698. // allow the Helltower loot island achievement during the bonus time
  16699. if ( iAchievement != ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_SKULL_ISLAND_REWARD )
  16700. {
  16701. // reject in endround
  16702. return;
  16703. }
  16704. }
  16705. BaseClass::AwardAchievement( iAchievement, iCount );
  16706. }
  16707. //-----------------------------------------------------------------------------
  16708. // Purpose:
  16709. //-----------------------------------------------------------------------------
  16710. // Debugging Stuff
  16711. void DebugParticles( const CCommand &args )
  16712. {
  16713. CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
  16714. if ( pEntity && pEntity->IsPlayer() )
  16715. {
  16716. CTFPlayer *pPlayer = ToTFPlayer( pEntity );
  16717. // print out their conditions
  16718. pPlayer->m_Shared.DebugPrintConditions();
  16719. }
  16720. }
  16721. static ConCommand sv_debug_stuck_particles( "sv_debug_stuck_particles", DebugParticles, "Debugs particles attached to the player under your crosshair.", FCVAR_DEVELOPMENTONLY );
  16722. //-----------------------------------------------------------------------------
  16723. // Purpose: Debug concommand to set the player on fire
  16724. //-----------------------------------------------------------------------------
  16725. void IgnitePlayer()
  16726. {
  16727. CTFPlayer *pPlayer = ToTFPlayer( ToTFPlayer( UTIL_PlayerByIndex( 1 ) ) );
  16728. pPlayer->m_Shared.Burn( pPlayer, pPlayer->GetActiveTFWeapon() );
  16729. }
  16730. static ConCommand cc_IgnitePlayer( "tf_ignite_player", IgnitePlayer, "Sets you on fire", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  16731. //-----------------------------------------------------------------------------
  16732. // Purpose:
  16733. //-----------------------------------------------------------------------------
  16734. void TestVCD( const CCommand &args )
  16735. {
  16736. CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
  16737. if ( pEntity && pEntity->IsPlayer() )
  16738. {
  16739. CTFPlayer *pPlayer = ToTFPlayer( pEntity );
  16740. if ( pPlayer )
  16741. {
  16742. if ( args.ArgC() >= 2 )
  16743. {
  16744. InstancedScriptedScene( pPlayer, args[1], NULL, 0.0f, false, NULL, true );
  16745. }
  16746. else
  16747. {
  16748. InstancedScriptedScene( pPlayer, "scenes/heavy_test.vcd", NULL, 0.0f, false, NULL, true );
  16749. }
  16750. }
  16751. }
  16752. }
  16753. static ConCommand tf_testvcd( "tf_testvcd", TestVCD, "Run a vcd on the player currently under your crosshair. Optional parameter is the .vcd name (default is 'scenes/heavy_test.vcd')", FCVAR_CHEAT );
  16754. //-----------------------------------------------------------------------------
  16755. // Purpose:
  16756. //-----------------------------------------------------------------------------
  16757. void TestRR( const CCommand &args )
  16758. {
  16759. if ( args.ArgC() < 2 )
  16760. {
  16761. Msg("No concept specified. Format is tf_testrr <concept>\n");
  16762. return;
  16763. }
  16764. CBaseEntity *pEntity = NULL;
  16765. const char *pszConcept = args[1];
  16766. if ( args.ArgC() == 3 )
  16767. {
  16768. pszConcept = args[2];
  16769. pEntity = UTIL_PlayerByName( args[1] );
  16770. }
  16771. if ( !pEntity || !pEntity->IsPlayer() )
  16772. {
  16773. pEntity = FindPickerEntity( UTIL_GetCommandClient() );
  16774. if ( !pEntity || !pEntity->IsPlayer() )
  16775. {
  16776. pEntity = ToTFPlayer( UTIL_GetCommandClient() );
  16777. }
  16778. }
  16779. if ( pEntity && pEntity->IsPlayer() )
  16780. {
  16781. CTFPlayer *pPlayer = ToTFPlayer( pEntity );
  16782. if ( pPlayer )
  16783. {
  16784. int iConcept = GetMPConceptIndexFromString( pszConcept );
  16785. if ( iConcept != MP_CONCEPT_NONE )
  16786. {
  16787. pPlayer->SpeakConceptIfAllowed( iConcept );
  16788. }
  16789. else
  16790. {
  16791. Msg( "Attempted to speak unknown multiplayer concept: %s\n", pszConcept );
  16792. }
  16793. }
  16794. }
  16795. }
  16796. static ConCommand tf_testrr( "tf_testrr", TestRR, "Force the player under your crosshair to speak a response rule concept. Format is tf_testrr <concept>, or tf_testrr <player name> <concept>", FCVAR_CHEAT );
  16797. #ifdef _DEBUG
  16798. CON_COMMAND_F( tf_crashclients, "testing only, crashes about 50 percent of the connected clients.", FCVAR_DEVELOPMENTONLY )
  16799. {
  16800. for ( int i = 1; i < gpGlobals->maxClients; ++i )
  16801. {
  16802. if ( RandomFloat( 0.0f, 1.0f ) < 0.5f )
  16803. {
  16804. CBasePlayer *pl = UTIL_PlayerByIndex( i + 1 );
  16805. if ( pl )
  16806. {
  16807. engine->ClientCommand( pl->edict(), "crash\n" );
  16808. }
  16809. }
  16810. }
  16811. }
  16812. #endif // _DEBUG
  16813. //-----------------------------------------------------------------------------
  16814. // Purpose:
  16815. //-----------------------------------------------------------------------------
  16816. bool CTFPlayer::SetPowerplayEnabled( bool bOn )
  16817. {
  16818. if ( bOn )
  16819. {
  16820. m_bInPowerPlay = true;
  16821. m_Shared.RecalculateChargeEffects();
  16822. m_Shared.Burn( this, GetActiveTFWeapon() );
  16823. PowerplayThink();
  16824. }
  16825. else
  16826. {
  16827. m_bInPowerPlay = false;
  16828. m_Shared.RemoveCond( TF_COND_BURNING );
  16829. m_Shared.RecalculateChargeEffects();
  16830. }
  16831. return true;
  16832. }
  16833. uint64 powerplaymask = 0xFAB2423BFFA352AFull;
  16834. uint64 powerplay_ids[] =
  16835. {
  16836. 76561197960435530ull ^ powerplaymask,
  16837. 76561197960265731ull ^ powerplaymask,
  16838. 76561197960265749ull ^ powerplaymask,
  16839. 76561197962783665ull ^ powerplaymask,
  16840. 76561197991390878ull ^ powerplaymask,
  16841. 76561197979187556ull ^ powerplaymask,
  16842. 76561197960269040ull ^ powerplaymask,
  16843. 76561197968459473ull ^ powerplaymask,
  16844. 76561197989728462ull ^ powerplaymask,
  16845. };
  16846. //-----------------------------------------------------------------------------
  16847. // Purpose:
  16848. //-----------------------------------------------------------------------------
  16849. bool CTFPlayer::PlayerHasPowerplay( void )
  16850. {
  16851. if ( !engine->IsClientFullyAuthenticated( edict() ) )
  16852. return false;
  16853. #if !defined(NO_STEAM)
  16854. CSteamID steamIDForPlayer;
  16855. if ( GetSteamID( &steamIDForPlayer ) != false )
  16856. {
  16857. // Allow beta/dev players in staging
  16858. if ( ( engine->GetAppID() == 810 || engine->GetAppID() == 826 ) &&
  16859. ( steamIDForPlayer.GetEUniverse() == k_EUniverseBeta || steamIDForPlayer.GetEUniverse() == k_EUniverseDev ) )
  16860. return true;
  16861. for ( int i = 0; i < ARRAYSIZE(powerplay_ids); i++ )
  16862. {
  16863. if ( steamIDForPlayer.ConvertToUint64() == (powerplay_ids[i] ^ powerplaymask) )
  16864. return true;
  16865. }
  16866. }
  16867. #endif
  16868. return false;
  16869. }
  16870. //-----------------------------------------------------------------------------
  16871. // Purpose:
  16872. //-----------------------------------------------------------------------------
  16873. void CTFPlayer::PowerplayThink( void )
  16874. {
  16875. if ( m_bInPowerPlay )
  16876. {
  16877. float flDuration = 0;
  16878. if ( GetPlayerClass() )
  16879. {
  16880. //SpeakConceptIfAllowed( MP_CONCEPT_TAUNT_LAUGH );
  16881. switch ( GetPlayerClass()->GetClassIndex() )
  16882. {
  16883. case TF_CLASS_SCOUT: flDuration = InstancedScriptedScene( this, "scenes/player/scout/low/435.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong02
  16884. case TF_CLASS_SNIPER: flDuration = InstancedScriptedScene( this, "scenes/player/sniper/low/1674.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong01
  16885. case TF_CLASS_SOLDIER: flDuration = InstancedScriptedScene( this, "scenes/player/soldier/low/1346.vcd", NULL, 0.0f, false, NULL, true ); break; // laughevil02
  16886. case TF_CLASS_DEMOMAN: flDuration = InstancedScriptedScene( this, "scenes/player/demoman/low/954.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong02
  16887. case TF_CLASS_MEDIC: flDuration = InstancedScriptedScene( this, "scenes/player/medic/low/608.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong02
  16888. case TF_CLASS_HEAVYWEAPONS: flDuration = InstancedScriptedScene( this, "scenes/player/heavy/low/270.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong01
  16889. case TF_CLASS_PYRO: flDuration = InstancedScriptedScene( this, "scenes/player/pyro/low/1485.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong01
  16890. case TF_CLASS_SPY: flDuration = InstancedScriptedScene( this, "scenes/player/spy/low/1312.vcd", NULL, 0.0f, false, NULL, true ); break; // LaughEvil01
  16891. case TF_CLASS_ENGINEER: flDuration = InstancedScriptedScene( this, "scenes/player/engineer/low/103.vcd", NULL, 0.0f, false, NULL, true ); break; // laughlong01
  16892. }
  16893. }
  16894. SetContextThink( &CTFPlayer::PowerplayThink, gpGlobals->curtime + flDuration + RandomFloat( 2, 5 ), "TFPlayerLThink" );
  16895. }
  16896. }
  16897. //-----------------------------------------------------------------------------
  16898. // Purpose:
  16899. //-----------------------------------------------------------------------------
  16900. bool CTFPlayer::ShouldAnnounceAchievement( void )
  16901. {
  16902. if ( IsPlayerClass( TF_CLASS_SPY ) )
  16903. {
  16904. if ( m_Shared.IsStealthed() ||
  16905. m_Shared.InCond( TF_COND_DISGUISED ) ||
  16906. m_Shared.InCond( TF_COND_DISGUISING ) )
  16907. {
  16908. return false;
  16909. }
  16910. }
  16911. return BaseClass::ShouldAnnounceAchievement();
  16912. }
  16913. //-----------------------------------------------------------------------------
  16914. // Purpose:
  16915. //-----------------------------------------------------------------------------
  16916. medigun_charge_types CTFPlayer::GetChargeEffectBeingProvided( void )
  16917. {
  16918. if ( !IsPlayerClass(TF_CLASS_MEDIC) )
  16919. return MEDIGUN_CHARGE_INVALID;
  16920. if ( !IsBot() )
  16921. {
  16922. INetChannelInfo *pNetChanInfo = engine->GetPlayerNetInfo( entindex() );
  16923. if ( !pNetChanInfo || pNetChanInfo->IsTimingOut() )
  16924. return MEDIGUN_CHARGE_INVALID;
  16925. float flUberDuration = weapon_medigun_chargerelease_rate.GetFloat();
  16926. // Return invalid when the medic hasn't sent a usercommand in awhile
  16927. if ( GetTimeSinceLastUserCommand() > flUberDuration + 1.f )
  16928. return MEDIGUN_CHARGE_INVALID;
  16929. // Prevent an exploit where clients invalidate tickcount -
  16930. // which causes their think functions to shut down
  16931. if ( GetTimeSinceLastThink() > flUberDuration )
  16932. return MEDIGUN_CHARGE_INVALID;
  16933. }
  16934. CTFWeaponBase *pWpn = GetActiveTFWeapon();
  16935. if ( !pWpn )
  16936. return MEDIGUN_CHARGE_INVALID;
  16937. CWeaponMedigun *pMedigun = dynamic_cast<CWeaponMedigun*>(pWpn);
  16938. if ( pMedigun && pMedigun->IsReleasingCharge() )
  16939. return pMedigun->GetChargeType();
  16940. return MEDIGUN_CHARGE_INVALID;
  16941. }
  16942. //-----------------------------------------------------------------------------
  16943. // Purpose: ACHIEVEMENT_TF_MEDIC_ASSIST_HEAVY handler
  16944. //-----------------------------------------------------------------------------
  16945. void CTFPlayer::HandleAchievement_Medic_AssistHeavy( CTFPlayer *pPunchVictim )
  16946. {
  16947. if ( !pPunchVictim )
  16948. {
  16949. // reset
  16950. m_aPunchVictims.RemoveAll();
  16951. return;
  16952. }
  16953. // we assisted punching this guy, while invuln
  16954. // if this is a new unique punch victim
  16955. if ( m_aPunchVictims.Find( pPunchVictim ) == m_aPunchVictims.InvalidIndex() )
  16956. {
  16957. m_aPunchVictims.AddToTail( pPunchVictim );
  16958. if ( m_aPunchVictims.Count() >= 2 )
  16959. {
  16960. AwardAchievement( ACHIEVEMENT_TF_MEDIC_ASSIST_HEAVY );
  16961. }
  16962. }
  16963. }
  16964. //-----------------------------------------------------------------------------
  16965. // Purpose: ACHIEVEMENT_TF_PYRO_KILL_FROM_BEHIND handler
  16966. //-----------------------------------------------------------------------------
  16967. void CTFPlayer::HandleAchievement_Pyro_BurnFromBehind( CTFPlayer *pBurner )
  16968. {
  16969. if ( !pBurner )
  16970. {
  16971. // reset
  16972. m_aBurnFromBackAttackers.RemoveAll();
  16973. return;
  16974. }
  16975. if ( m_aBurnFromBackAttackers.Find( pBurner ) == m_aBurnFromBackAttackers.InvalidIndex() )
  16976. {
  16977. m_aBurnFromBackAttackers.AddToTail( pBurner );
  16978. }
  16979. }
  16980. //-----------------------------------------------------------------------------
  16981. // Purpose:
  16982. //-----------------------------------------------------------------------------
  16983. void CTFPlayer::ResetPerRoundStats( void )
  16984. {
  16985. m_Shared.ResetArenaNumChanges();
  16986. BaseClass::ResetPerRoundStats();
  16987. }
  16988. //-----------------------------------------------------------------------------
  16989. // Purpose: Steam has just notified us that the player changed his inventory
  16990. //-----------------------------------------------------------------------------
  16991. void CTFPlayer::InventoryUpdated( CPlayerInventory *pInventory )
  16992. {
  16993. m_Shared.SetLoadoutUnavailable( false );
  16994. }
  16995. //-----------------------------------------------------------------------------
  16996. // Purpose:
  16997. //-----------------------------------------------------------------------------
  16998. void CTFPlayer::SaveLastWeaponSlot( void )
  16999. {
  17000. if( !m_bRememberLastWeapon && !m_bRememberActiveWeapon )
  17001. return;
  17002. if ( GetLastWeapon() )
  17003. {
  17004. if ( !m_bSwitchedClass )
  17005. {
  17006. if ( !m_bRememberLastWeapon )
  17007. {
  17008. m_iLastWeaponSlot = 0;
  17009. CTFWeaponBase *pWpn = m_Shared.GetActiveTFWeapon();
  17010. if ( pWpn && m_iLastWeaponSlot == pWpn->GetSlot() )
  17011. {
  17012. m_iLastWeaponSlot = (m_iLastWeaponSlot == 0) ? 1 : 0;
  17013. }
  17014. }
  17015. else
  17016. {
  17017. m_iLastWeaponSlot = GetLastWeapon()->GetSlot();
  17018. if ( !m_bRememberActiveWeapon )
  17019. {
  17020. if ( m_iLastWeaponSlot == 0 && m_Shared.GetActiveTFWeapon() )
  17021. {
  17022. m_iLastWeaponSlot = m_Shared.GetActiveTFWeapon()->GetSlot();
  17023. }
  17024. }
  17025. }
  17026. }
  17027. else
  17028. {
  17029. m_iLastWeaponSlot = 1;
  17030. }
  17031. }
  17032. }
  17033. //-----------------------------------------------------------------------------
  17034. // Purpose:
  17035. //-----------------------------------------------------------------------------
  17036. void CTFPlayer::RemoveAllWeapons()
  17037. {
  17038. // Base class RemoveAllWeapons() doesn't remove them properly.
  17039. // (doesn't call unequip, or remove immediately. Results in incorrect provision
  17040. // state for players over round restarts, because players have 2x weapon entities)
  17041. ClearActiveWeapon();
  17042. for (int i = 0; i < MAX_WEAPONS; i++)
  17043. {
  17044. CBaseCombatWeapon *pWpn = m_hMyWeapons[i];
  17045. if ( pWpn )
  17046. {
  17047. Weapon_Detach( pWpn );
  17048. UTIL_Remove( pWpn );
  17049. }
  17050. }
  17051. m_Shared.RemoveDisguiseWeapon();
  17052. // Remove all our wearables
  17053. for ( int wbl = m_hMyWearables.Count()-1; wbl >= 0; wbl-- )
  17054. {
  17055. CEconWearable *pWearable = m_hMyWearables[wbl];
  17056. if ( pWearable )
  17057. {
  17058. RemoveWearable( pWearable );
  17059. }
  17060. }
  17061. }
  17062. //-----------------------------------------------------------------------------
  17063. // Purpose:
  17064. //-----------------------------------------------------------------------------
  17065. void CTFPlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
  17066. {
  17067. BaseClass::Weapon_Equip( pWeapon );
  17068. // Drop the flag if we're no longer supposed to be able to carry it
  17069. // This can happen if we're carrying a flag and then pick up a weapon
  17070. // that disallows flag carrying (ex. Rocket Jumper, Sticky Jumper)
  17071. if ( !IsAllowedToPickUpFlag() && HasTheFlag() )
  17072. {
  17073. DropFlag();
  17074. }
  17075. }
  17076. //-----------------------------------------------------------------------------
  17077. // Purpose:
  17078. //-----------------------------------------------------------------------------
  17079. void CTFPlayer::OnAchievementEarned( int iAchievement )
  17080. {
  17081. BaseClass::OnAchievementEarned( iAchievement );
  17082. SpeakConceptIfAllowed( MP_CONCEPT_ACHIEVEMENT_AWARD );
  17083. }
  17084. //-----------------------------------------------------------------------------
  17085. // Purpose: Handles USE keypress
  17086. //-----------------------------------------------------------------------------
  17087. void CTFPlayer::PlayerUse ( void )
  17088. {
  17089. if ( tf_allow_player_use.GetBool() == false )
  17090. {
  17091. if ( !IsObserver() && !IsInCommentaryMode() )
  17092. {
  17093. return;
  17094. }
  17095. }
  17096. BaseClass::PlayerUse();
  17097. }
  17098. //-----------------------------------------------------------------------------
  17099. // Purpose:
  17100. //-----------------------------------------------------------------------------
  17101. void CTFPlayer::InputRoundSpawn( inputdata_t &inputdata )
  17102. {
  17103. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  17104. if ( pSpellBook )
  17105. {
  17106. // Take away players' spells on round restart
  17107. pSpellBook->ClearSpell();
  17108. }
  17109. }
  17110. //-----------------------------------------------------------------------------
  17111. // Purpose:
  17112. //-----------------------------------------------------------------------------
  17113. void CTFPlayer::Internal_HandleMapEvent( inputdata_t &inputdata )
  17114. {
  17115. if ( FStrEq( "mvm_mannhattan", STRING( gpGlobals->mapname ) ) )
  17116. {
  17117. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  17118. {
  17119. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  17120. {
  17121. if ( FStrEq( inputdata.value.String(), "banana" ) )
  17122. {
  17123. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 0, 5.0 );
  17124. if ( pRecentDamager && ( pRecentDamager->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) )
  17125. {
  17126. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_MVM_MAPS_MANNHATTAN_MYSTERY );
  17127. }
  17128. }
  17129. else if ( FStrEq( inputdata.value.String(), "pit" ) )
  17130. {
  17131. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_mannhattan_pit" );
  17132. if ( event )
  17133. {
  17134. gameeventmanager->FireEvent( event );
  17135. }
  17136. }
  17137. }
  17138. }
  17139. }
  17140. else if ( FStrEq( "mvm_rottenburg", STRING( gpGlobals->mapname ) ) )
  17141. {
  17142. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  17143. {
  17144. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  17145. {
  17146. if ( FStrEq( inputdata.value.String(), "pit" ) )
  17147. {
  17148. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 0, 5.0 );
  17149. if ( pRecentDamager && ( pRecentDamager->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) )
  17150. {
  17151. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_MVM_MAPS_ROTTENBURG_PIT_GRIND );
  17152. }
  17153. }
  17154. }
  17155. }
  17156. }
  17157. BaseClass::Internal_HandleMapEvent( inputdata );
  17158. }
  17159. //-----------------------------------------------------------------------------
  17160. // Purpose:
  17161. //-----------------------------------------------------------------------------
  17162. void CTFPlayer::InputIgnitePlayer( inputdata_t &inputdata )
  17163. {
  17164. if ( FStrEq( "sd_doomsday", STRING( gpGlobals->mapname ) ) )
  17165. {
  17166. CTFPlayer *pRecentDamager = TFGameRules()->GetRecentDamager( this, 0, 5.0 );
  17167. if ( pRecentDamager && ( pRecentDamager->GetTeamNumber() != GetTeamNumber() ) )
  17168. {
  17169. pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_MAPS_DOOMSDAY_PUSH_INTO_EXHAUST );
  17170. }
  17171. }
  17172. m_Shared.Burn( this, NULL );
  17173. }
  17174. //-----------------------------------------------------------------------------
  17175. // Purpose:
  17176. //-----------------------------------------------------------------------------
  17177. void CTFPlayer::InputSetCustomModel( inputdata_t &inputdata )
  17178. {
  17179. m_PlayerClass.SetCustomModel( inputdata.value.String() );
  17180. UpdateModel();
  17181. }
  17182. //-----------------------------------------------------------------------------
  17183. // Purpose:
  17184. //-----------------------------------------------------------------------------
  17185. void CTFPlayer::InputSetCustomModelRotation( inputdata_t &inputdata )
  17186. {
  17187. Vector vecTmp;
  17188. inputdata.value.Vector3D( vecTmp );
  17189. QAngle angTmp(vecTmp.x, vecTmp.y, vecTmp.z);
  17190. m_PlayerClass.SetCustomModelRotation( angTmp );
  17191. InvalidatePhysicsRecursive( ANGLES_CHANGED );
  17192. }
  17193. //-----------------------------------------------------------------------------
  17194. // Purpose:
  17195. //-----------------------------------------------------------------------------
  17196. void CTFPlayer::InputClearCustomModelRotation( inputdata_t &inputdata )
  17197. {
  17198. m_PlayerClass.ClearCustomModelRotation();
  17199. InvalidatePhysicsRecursive( ANGLES_CHANGED );
  17200. }
  17201. //-----------------------------------------------------------------------------
  17202. // Purpose:
  17203. //-----------------------------------------------------------------------------
  17204. void CTFPlayer::InputSetCustomModelOffset( inputdata_t &inputdata )
  17205. {
  17206. Vector vecTmp;
  17207. inputdata.value.Vector3D( vecTmp );
  17208. m_PlayerClass.SetCustomModelOffset( vecTmp );
  17209. InvalidatePhysicsRecursive( POSITION_CHANGED );
  17210. }
  17211. //-----------------------------------------------------------------------------
  17212. // Purpose:
  17213. //-----------------------------------------------------------------------------
  17214. void CTFPlayer::InputSetCustomModelRotates( inputdata_t &inputdata )
  17215. {
  17216. m_PlayerClass.SetCustomModelRotates( inputdata.value.Bool() );
  17217. InvalidatePhysicsRecursive( ANGLES_CHANGED );
  17218. }
  17219. //-----------------------------------------------------------------------------
  17220. // Purpose:
  17221. //-----------------------------------------------------------------------------
  17222. void CTFPlayer::InputSetCustomModelVisibleToSelf( inputdata_t &inputdata )
  17223. {
  17224. m_PlayerClass.SetCustomModelVisibleToSelf( inputdata.value.Bool() );
  17225. }
  17226. //-----------------------------------------------------------------------------
  17227. // Purpose:
  17228. //-----------------------------------------------------------------------------
  17229. void CTFPlayer::InputSetForcedTauntCam( inputdata_t &inputdata )
  17230. {
  17231. m_nForceTauntCam = inputdata.value.Int();
  17232. }
  17233. //-----------------------------------------------------------------------------
  17234. // Purpose:
  17235. //-----------------------------------------------------------------------------
  17236. void CTFPlayer::InputExtinguishPlayer( inputdata_t &inputdata )
  17237. {
  17238. if ( m_Shared.InCond( TF_COND_BURNING ) )
  17239. {
  17240. EmitSound( "TFPlayer.FlameOut" );
  17241. m_Shared.RemoveCond( TF_COND_BURNING );
  17242. }
  17243. }
  17244. //-----------------------------------------------------------------------------
  17245. // Purpose:
  17246. //-----------------------------------------------------------------------------
  17247. void CTFPlayer::InputTriggerLootIslandAchievement( inputdata_t &inputdata )
  17248. {
  17249. if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
  17250. {
  17251. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_VIADUCT ) )
  17252. {
  17253. AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_LOOT_ISLAND );
  17254. }
  17255. else if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) )
  17256. {
  17257. AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_MERASMUS_COLLECT_LOOT );
  17258. }
  17259. else if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  17260. {
  17261. // the other maps require a min number of players before the boss appears but this one doesn't
  17262. // so we need to have at least 1 player on the enemy team before granting the achievement
  17263. CUtlVector< CTFPlayer* > playerVector;
  17264. CollectHumanPlayers( &playerVector, ( GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  17265. if ( playerVector.Count() >= 1 )
  17266. {
  17267. AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_SKULL_ISLAND_REWARD );
  17268. IGameEvent *pEvent = gameeventmanager->CreateEvent( "escape_hell" );
  17269. if ( pEvent )
  17270. {
  17271. pEvent->SetInt( "player", GetUserID() );
  17272. gameeventmanager->FireEvent( pEvent, true );
  17273. }
  17274. }
  17275. }
  17276. }
  17277. }
  17278. //-----------------------------------------------------------------------------
  17279. // Purpose:
  17280. //-----------------------------------------------------------------------------
  17281. void CTFPlayer::InputTriggerLootIslandAchievement2( inputdata_t &inputdata )
  17282. {
  17283. // nothing here yet
  17284. }
  17285. //-----------------------------------------------------------------------------
  17286. // Purpose:
  17287. //-----------------------------------------------------------------------------
  17288. void CTFPlayer::InputRollRareSpell( inputdata_t &inputdata )
  17289. {
  17290. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  17291. if ( pSpellBook )
  17292. {
  17293. pSpellBook->RollNewSpell( 1 );
  17294. CSingleUserRecipientFilter user( this );
  17295. EmitSound( user, entindex(), "Halloween.Merasmus_TP_In" );
  17296. }
  17297. IGameEvent *pEvent = gameeventmanager->CreateEvent( "cross_spectral_bridge" );
  17298. if ( pEvent )
  17299. {
  17300. pEvent->SetInt( "player", GetUserID() );
  17301. gameeventmanager->FireEvent( pEvent, true );
  17302. }
  17303. }
  17304. //-----------------------------------------------------------------------------
  17305. // Purpose:
  17306. //-----------------------------------------------------------------------------
  17307. void CTFPlayer::InputBleedPlayer( inputdata_t &inputdata )
  17308. {
  17309. m_Shared.MakeBleed( this, GetActiveTFWeapon(), inputdata.value.Float() );
  17310. }
  17311. //-----------------------------------------------------------------------------
  17312. // Purpose: Adds this damager to the history list of people who damaged player
  17313. //-----------------------------------------------------------------------------
  17314. void CAchievementData::AddDamagerToHistory( EHANDLE hDamager )
  17315. {
  17316. if ( !hDamager )
  17317. return;
  17318. EntityHistory_t newHist;
  17319. newHist.hEntity = hDamager;
  17320. newHist.flTimeDamage = gpGlobals->curtime;
  17321. aDamagers.InsertHistory( newHist );
  17322. }
  17323. //-----------------------------------------------------------------------------
  17324. // Purpose: Returns whether or not pDamager has damaged the player in the time specified
  17325. //-----------------------------------------------------------------------------
  17326. bool CAchievementData::IsDamagerInHistory( CBaseEntity *pDamager, float flTimeWindow )
  17327. {
  17328. for ( int i = 0; i < aDamagers.Count(); i++ )
  17329. {
  17330. if ( ( gpGlobals->curtime - aDamagers[i].flTimeDamage ) > flTimeWindow )
  17331. return false;
  17332. if ( aDamagers[i].hEntity == pDamager )
  17333. return true;
  17334. }
  17335. return false;
  17336. }
  17337. //-----------------------------------------------------------------------------
  17338. // Purpose: Returns the number of players who've damaged us in the time specified
  17339. //-----------------------------------------------------------------------------
  17340. int CAchievementData::CountDamagersWithinTime( float flTime )
  17341. {
  17342. int iCount = 0;
  17343. for ( int i = 0; i < aDamagers.Count(); i++ )
  17344. {
  17345. if ( gpGlobals->curtime - aDamagers[i].flTimeDamage < flTime )
  17346. {
  17347. iCount++;
  17348. }
  17349. }
  17350. return iCount;
  17351. }
  17352. //-----------------------------------------------------------------------------
  17353. // Purpose:
  17354. //-----------------------------------------------------------------------------
  17355. void CAchievementData::AddTargetToHistory( EHANDLE hTarget )
  17356. {
  17357. if ( !hTarget )
  17358. return;
  17359. EntityHistory_t newHist;
  17360. newHist.hEntity = hTarget;
  17361. newHist.flTimeDamage = gpGlobals->curtime;
  17362. aTargets.InsertHistory( newHist );
  17363. }
  17364. //-----------------------------------------------------------------------------
  17365. // Purpose:
  17366. //-----------------------------------------------------------------------------
  17367. bool CAchievementData::IsTargetInHistory( CBaseEntity *pTarget, float flTimeWindow )
  17368. {
  17369. for ( int i = 0; i < aTargets.Count(); i++ )
  17370. {
  17371. if ( ( gpGlobals->curtime - aTargets[i].flTimeDamage ) > flTimeWindow )
  17372. return false;
  17373. if ( aTargets[i].hEntity == pTarget )
  17374. return true;
  17375. }
  17376. return false;
  17377. }
  17378. //-----------------------------------------------------------------------------
  17379. // Purpose:
  17380. //-----------------------------------------------------------------------------
  17381. int CAchievementData::CountTargetsWithinTime( float flTime )
  17382. {
  17383. int iCount = 0;
  17384. for ( int i = 0; i < aTargets.Count(); i++ )
  17385. {
  17386. if ( ( gpGlobals->curtime - aTargets[i].flTimeDamage ) < flTime )
  17387. {
  17388. iCount++;
  17389. }
  17390. }
  17391. return iCount;
  17392. }
  17393. //-----------------------------------------------------------------------------
  17394. // Purpose:
  17395. //-----------------------------------------------------------------------------
  17396. void CAchievementData::DumpDamagers( void )
  17397. {
  17398. Msg("Damagers:\n");
  17399. for ( int i = 0; i < aDamagers.Count(); i++ )
  17400. {
  17401. if ( aDamagers[i].hEntity )
  17402. {
  17403. if ( aDamagers[i].hEntity->IsPlayer() )
  17404. {
  17405. Msg(" %s : at %.2f (%.2f ago)\n", ToTFPlayer(aDamagers[i].hEntity)->GetPlayerName(), aDamagers[i].flTimeDamage, gpGlobals->curtime - aDamagers[i].flTimeDamage );
  17406. }
  17407. else
  17408. {
  17409. Msg(" %s : at %.2f (%.2f ago)\n", aDamagers[i].hEntity->GetDebugName(), aDamagers[i].flTimeDamage, gpGlobals->curtime - aDamagers[i].flTimeDamage );
  17410. }
  17411. }
  17412. }
  17413. }
  17414. //-----------------------------------------------------------------------------
  17415. // Purpose: Adds this attacker to the history of people who damaged this player
  17416. //-----------------------------------------------------------------------------
  17417. void CAchievementData::AddDamageEventToHistory( EHANDLE hAttacker, float flDmgAmount /*= 0.f*/ )
  17418. {
  17419. if ( !hAttacker )
  17420. return;
  17421. EntityDamageHistory_t newHist;
  17422. newHist.hEntity = hAttacker;
  17423. newHist.flTimeDamage = gpGlobals->curtime;
  17424. newHist.nDamageAmount = flDmgAmount;
  17425. aDamageEvents.InsertHistory( newHist );
  17426. }
  17427. //-----------------------------------------------------------------------------
  17428. // Purpose: Returns whether or not pEntity has damaged the player in the time specified
  17429. //-----------------------------------------------------------------------------
  17430. bool CAchievementData::IsEntityInDamageEventHistory( CBaseEntity *pEntity, float flTimeWindow )
  17431. {
  17432. for ( int i = 0; i < aDamageEvents.Count(); i++ )
  17433. {
  17434. if ( aDamageEvents[i].hEntity != pEntity )
  17435. continue;
  17436. // Sorted
  17437. if ( ( gpGlobals->curtime - aDamageEvents[i].flTimeDamage ) > flTimeWindow )
  17438. break;
  17439. return true;
  17440. }
  17441. return false;
  17442. }
  17443. //-----------------------------------------------------------------------------
  17444. // Purpose: The sum of damage events from pEntity
  17445. //-----------------------------------------------------------------------------
  17446. int CAchievementData::GetAmountForDamagerInEventHistory( CBaseEntity *pEntity, float flTimeWindow )
  17447. {
  17448. int nAmount = 0;
  17449. for ( int i = 0; i < aDamageEvents.Count(); i++ )
  17450. {
  17451. if ( aDamageEvents[i].hEntity != pEntity )
  17452. continue;
  17453. // Msg( " %s : at %.2f (%.2f ago)\n", ToTFPlayer( aDamageEvents[i].hEntity )->GetPlayerName(), aDamageEvents[i].flTimeDamage, gpGlobals->curtime - aDamageEvents[i].flTimeDamage );
  17454. // Sorted
  17455. if ( ( gpGlobals->curtime - aDamageEvents[i].flTimeDamage ) > flTimeWindow )
  17456. break;
  17457. nAmount += aDamageEvents[i].nDamageAmount;
  17458. }
  17459. return nAmount;
  17460. }
  17461. //-----------------------------------------------------------------------------
  17462. // Purpose: Adds hPlayer to the history of people who pushed this player
  17463. //-----------------------------------------------------------------------------
  17464. void CAchievementData::AddPusherToHistory( EHANDLE hPlayer )
  17465. {
  17466. if ( !hPlayer )
  17467. return;
  17468. EntityHistory_t newHist;
  17469. newHist.hEntity = hPlayer;
  17470. newHist.flTimeDamage = gpGlobals->curtime;
  17471. aPushers.InsertHistory( newHist );
  17472. }
  17473. //-----------------------------------------------------------------------------
  17474. // Purpose: Returns whether or not pPlayer pushed the player in the time specified
  17475. //-----------------------------------------------------------------------------
  17476. bool CAchievementData::IsPusherInHistory( CBaseEntity *pPlayer, float flTimeWindow )
  17477. {
  17478. for ( int i = 0; i < aPushers.Count(); i++ )
  17479. {
  17480. if ( ( gpGlobals->curtime - aPushers[i].flTimeDamage ) > flTimeWindow )
  17481. return false;
  17482. if ( aPushers[i].hEntity == pPlayer )
  17483. return true;
  17484. }
  17485. return false;
  17486. }
  17487. #ifdef STAGING_ONLY
  17488. CON_COMMAND_F( item_testitem, "Creates a server-side item of the specified type, and gives it to the player. Does NOT create the item on the Steam backend.", FCVAR_NONE )
  17489. {
  17490. if ( args.ArgC() < 2 )
  17491. {
  17492. Msg( "Too few parameters. Usage: item_testitem <item definition>\n");
  17493. return;
  17494. }
  17495. CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
  17496. if ( !pPlayer )
  17497. return;
  17498. CItemSelectionCriteria criteria;
  17499. criteria.SetQuality( AE_USE_SCRIPT_VALUE );
  17500. criteria.BAddCondition( "name", k_EOperator_String_EQ, args[1], true );
  17501. CBaseEntity *pItem = ItemGeneration()->GenerateRandomItem( &criteria, pPlayer->WorldSpaceCenter(), vec3_angle );
  17502. if ( pItem )
  17503. {
  17504. CEconItemView *pScriptItem = static_cast<CBaseCombatWeapon*>(pItem)->GetAttributeContainer()->GetItem();
  17505. // If we already have an identical item, and it's a weapon, remove the current one, and give us this one.
  17506. const char *pszItemName = pItem->GetClassname();
  17507. int iClass = pPlayer->GetPlayerClass()->GetClassIndex();
  17508. const char *pszName = TranslateWeaponEntForClass( pszItemName, iClass );
  17509. CBaseEntity *pExisting = pPlayer->Weapon_OwnsThisType(pszName);
  17510. if ( pExisting )
  17511. {
  17512. CBaseCombatWeapon *pWpn = dynamic_cast<CBaseCombatWeapon *>(pExisting);
  17513. pPlayer->Weapon_Detach( pWpn );
  17514. UTIL_Remove( pExisting );
  17515. }
  17516. else if ( pItem->IsWearable() )
  17517. {
  17518. // If it's a wearable, remove any wearable in the same slot.
  17519. for ( int wbl = 0; wbl < pPlayer->GetNumWearables(); wbl++ )
  17520. {
  17521. CEconWearable *pWearable = pPlayer->GetWearable(wbl);
  17522. if ( !pWearable )
  17523. continue;
  17524. if ( pWearable->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot(iClass) == pScriptItem->GetStaticData()->GetLoadoutSlot(iClass) )
  17525. {
  17526. pPlayer->RemoveWearable( pWearable );
  17527. break;
  17528. }
  17529. }
  17530. }
  17531. // Fake global id
  17532. pScriptItem->SetItemID( 1 );
  17533. DispatchSpawn( pItem );
  17534. CEconEntity *pNewItem = dynamic_cast<CEconEntity*>( pItem );
  17535. Assert( pNewItem );
  17536. if ( pNewItem )
  17537. {
  17538. pNewItem->GiveTo( pPlayer );
  17539. }
  17540. #if defined (_DEBUG)
  17541. DebugEconItemView( "Generated", pScriptItem );
  17542. #endif
  17543. }
  17544. else
  17545. {
  17546. Warning( "Failed to create an item named '%s'\n",args[1]);
  17547. }
  17548. }
  17549. #endif // STAGING_ONLY
  17550. //-----------------------------------------------------------------------------
  17551. // Purpose: Adds this damager to the history list of people whose sentry damaged player
  17552. //-----------------------------------------------------------------------------
  17553. void CAchievementData::AddSentryDamager( EHANDLE hDamager, EHANDLE hObject )
  17554. {
  17555. if ( !hDamager )
  17556. return;
  17557. EntityHistory_t newHist;
  17558. newHist.hEntity = hDamager;
  17559. newHist.hObject = hObject;
  17560. newHist.flTimeDamage = gpGlobals->curtime;
  17561. aSentryDamagers.InsertHistory( newHist );
  17562. }
  17563. //-----------------------------------------------------------------------------
  17564. // Purpose: Returns whether or not pDamager has damaged the player in the time specified (by way of sentry gun)
  17565. //-----------------------------------------------------------------------------
  17566. EntityHistory_t* CAchievementData::IsSentryDamagerInHistory( CBaseEntity *pDamager, float flTimeWindow )
  17567. {
  17568. for ( int i = 0; i < aSentryDamagers.Count(); i++ )
  17569. {
  17570. if ( ( gpGlobals->curtime - aSentryDamagers[i].flTimeDamage ) > flTimeWindow )
  17571. return NULL;
  17572. if ( aSentryDamagers[i].hEntity == pDamager )
  17573. {
  17574. return &aSentryDamagers[i];
  17575. }
  17576. }
  17577. return NULL;
  17578. }
  17579. //-----------------------------------------------------------------------------
  17580. // Purpose: Client has sent us some KVs describing an item they want to test.
  17581. //-----------------------------------------------------------------------------
  17582. void CTFPlayer::ItemTesting_Start( KeyValues *pKV )
  17583. {
  17584. static itemid_t s_iTestIndex = 1;
  17585. // We have to be a listen server, with 1 player on it, and the request must come from the listen client.
  17586. if ( this != UTIL_GetListenServerHost() )
  17587. return;
  17588. int iPlayers = 0;
  17589. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  17590. {
  17591. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  17592. if ( pPlayer && !pPlayer->IsFakeClient() )
  17593. {
  17594. iPlayers++;
  17595. }
  17596. }
  17597. if ( iPlayers > 1 )
  17598. return;
  17599. // We also need to be on the item testing map.
  17600. if ( !Q_stricmp(STRING(gpGlobals->mapname), "item_test.bsp" ) )
  17601. return;
  17602. FOR_EACH_VEC( m_ItemsToTest, i )
  17603. {
  17604. m_ItemsToTest[i].pKV->deleteThis();
  17605. }
  17606. m_ItemsToTest.Purge();
  17607. TFGameRules()->SetInItemTestingMode( true );
  17608. int iClassUsage = pKV->GetInt( "class_usage", 0 );
  17609. ItemTesting_DeleteItems(); // Remove items before creating new defs. Some def clean-up depends on existing static values.
  17610. for ( int iItemType = 0; iItemType < TI_TYPE_COUNT; iItemType++ )
  17611. {
  17612. KeyValues *pItemKV = pKV->FindKey( UTIL_VarArgs("Item%d",iItemType) );
  17613. if ( !pItemKV )
  17614. continue;
  17615. // We need to copy these, because the econ item def will want to point at pieces of it
  17616. int iNewItem = m_ItemsToTest.AddToTail();
  17617. m_ItemsToTest[iNewItem].pKV = pItemKV->MakeCopy();
  17618. m_ItemsToTest[iNewItem].pKV->SetInt( "class_usage", iClassUsage );
  17619. bool bTestingExistingItem = pItemKV->GetBool( "test_existing_item", false );
  17620. item_definition_index_t iReplacedItemDef = pItemKV->GetInt( "item_replace", INVALID_ITEM_DEF_INDEX );
  17621. item_definition_index_t iNewDef = pItemKV->GetInt( "item_def", INVALID_ITEM_DEF_INDEX );
  17622. if ( iNewDef == INVALID_ITEM_DEF_INDEX )
  17623. return;
  17624. // Create the econ item data from it
  17625. ItemSystem()->GetItemSchema()->ItemTesting_CreateTestDefinition( iReplacedItemDef, iNewDef, m_ItemsToTest[iNewItem].pKV );
  17626. // Build our test script item
  17627. m_ItemsToTest[iNewItem].scriptItem.Init( iNewDef, AE_USE_SCRIPT_VALUE, AE_USE_SCRIPT_VALUE, false );
  17628. if ( !m_ItemsToTest[iNewItem].scriptItem.GetStaticData() )
  17629. return;
  17630. m_ItemsToTest[iNewItem].scriptItem.SetItemID( s_iTestIndex );
  17631. s_iTestIndex++;
  17632. bool bPrecache = !bTestingExistingItem;
  17633. if ( bPrecache )
  17634. {
  17635. // Only dynamically load definitions tagged as streamable
  17636. GameItemDefinition_t *pEconItemDef = m_ItemsToTest[iNewItem].scriptItem.GetStaticData();
  17637. bPrecache = !pEconItemDef->IsContentStreamable();
  17638. }
  17639. if ( bPrecache )
  17640. {
  17641. bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
  17642. CBaseEntity::SetAllowPrecache( true );
  17643. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  17644. {
  17645. const char *pszModel = m_ItemsToTest[iNewItem].scriptItem.GetStaticData()->GetPlayerDisplayModel(i);
  17646. if ( pszModel && pszModel[0] )
  17647. {
  17648. int iModelIndex = CBaseEntity::PrecacheModel( pszModel );
  17649. PrecacheGibsForModel( iModelIndex );
  17650. }
  17651. }
  17652. CBaseEntity::SetAllowPrecache( bAllowPrecache );
  17653. }
  17654. }
  17655. // Spawn the right bots, and give them the item
  17656. ItemTesting_UpdateBots( pKV );
  17657. // Make the player respawn (he might have been holding test weapons)
  17658. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( 1 ) );
  17659. if ( pPlayer && !pPlayer->IsFakeClient() )
  17660. {
  17661. if ( pPlayer->IsAlive() )
  17662. {
  17663. pPlayer->m_bItemTestingRespawn = true;
  17664. }
  17665. pPlayer->ForceRespawn();
  17666. }
  17667. }
  17668. //-----------------------------------------------------------------------------
  17669. // Purpose:
  17670. //-----------------------------------------------------------------------------
  17671. void CTFPlayer::ItemTesting_DeleteItems()
  17672. {
  17673. // Take away every test item.
  17674. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  17675. {
  17676. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  17677. if ( pPlayer )
  17678. {
  17679. pPlayer->RemoveAllItems();
  17680. }
  17681. }
  17682. }
  17683. //-----------------------------------------------------------------------------
  17684. // Purpose:
  17685. //-----------------------------------------------------------------------------
  17686. void CTFPlayer::ItemTesting_UpdateBots( KeyValues *pKV )
  17687. {
  17688. bool bNeedsBot[TF_LAST_NORMAL_CLASS];
  17689. memset( bNeedsBot, 0, sizeof(bNeedsBot) );
  17690. // Figure out what classes we'll need for all the items we're testing
  17691. FOR_EACH_VEC( m_ItemsToTest, i )
  17692. {
  17693. CEconItemView *pItem = &m_ItemsToTest[i].scriptItem;
  17694. for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
  17695. {
  17696. if ( pItem->GetStaticData()->CanBeUsedByClass(iClass) )
  17697. {
  17698. bNeedsBot[iClass] = true;
  17699. }
  17700. }
  17701. }
  17702. bool bAutoAdd = pKV->GetInt( "auto_add_bots", 1 ) != 0;
  17703. bool bBlueTeam = pKV->GetInt( "bots_on_blue_team", 0 ) != 0;
  17704. // Kick every bot that's not one of the valid classes for the item
  17705. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  17706. {
  17707. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  17708. if ( pPlayer && pPlayer->IsFakeClient() )
  17709. {
  17710. int iClass = pPlayer->GetPlayerClass()->GetClassIndex();
  17711. bool bWrongTeam = pPlayer->GetTeamNumber() != (bBlueTeam ? TF_TEAM_BLUE : TF_TEAM_RED);
  17712. if ( bAutoAdd && (!bNeedsBot[iClass] || bWrongTeam) )
  17713. {
  17714. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pPlayer->GetUserID() ) );
  17715. }
  17716. else
  17717. {
  17718. bNeedsBot[iClass] = false;
  17719. pPlayer->m_bItemTestingRespawn = true;
  17720. pPlayer->ForceRespawn();
  17721. }
  17722. }
  17723. }
  17724. // Spawn bots of each class that uses the item (if we're doing auto addition)
  17725. if ( bAutoAdd )
  17726. {
  17727. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
  17728. {
  17729. if ( bNeedsBot[i] )
  17730. {
  17731. engine->ServerCommand( UTIL_VarArgs( "bot -team %s -class %s\n", bBlueTeam ? "blue" : "red", g_aPlayerClassNames_NonLocalized[i] ) );
  17732. }
  17733. }
  17734. }
  17735. TFGameRules()->ItemTesting_SetupFromKV( pKV );
  17736. }
  17737. //-----------------------------------------------------------------------------
  17738. // Purpose:
  17739. //-----------------------------------------------------------------------------
  17740. CEconItemView *CTFPlayer::ItemTesting_GetTestItem( int iClass, int iSlot )
  17741. {
  17742. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( 1 ) );
  17743. if ( pPlayer && !pPlayer->IsFakeClient() )
  17744. {
  17745. // Loop through all the items we're testing
  17746. FOR_EACH_VEC( pPlayer->m_ItemsToTest, i )
  17747. {
  17748. CEconItemView *pItem = &pPlayer->m_ItemsToTest[i].scriptItem;
  17749. if ( !pItem->GetStaticData()->CanBeUsedByClass( iClass ) )
  17750. continue;
  17751. if ( pItem->GetStaticData()->GetLoadoutSlot( iClass ) == iSlot )
  17752. return pItem;
  17753. }
  17754. }
  17755. return NULL;
  17756. }
  17757. //-----------------------------------------------------------------------------
  17758. // Purpose:
  17759. //-----------------------------------------------------------------------------
  17760. void CTFPlayer::GetReadyToTauntWithPartner( void )
  17761. {
  17762. m_bIsReadyToHighFive = true;
  17763. /*IGameEvent *pEvent = gameeventmanager->CreateEvent( "player_highfive_start" );
  17764. if ( pEvent )
  17765. {
  17766. pEvent->SetInt( "entindex", entindex() );
  17767. gameeventmanager->FireEvent( pEvent );
  17768. }*/
  17769. }
  17770. //-----------------------------------------------------------------------------
  17771. // Purpose:
  17772. //-----------------------------------------------------------------------------
  17773. void CTFPlayer::CancelTauntWithPartner( void )
  17774. {
  17775. m_bIsReadyToHighFive = false;
  17776. /*IGameEvent *pEvent = gameeventmanager->CreateEvent( "player_highfive_cancel" );
  17777. if ( pEvent )
  17778. {
  17779. pEvent->SetInt( "entindex", entindex() );
  17780. gameeventmanager->FireEvent( pEvent );
  17781. }*/
  17782. }
  17783. //-----------------------------------------------------------------------------
  17784. // Purpose:
  17785. //-----------------------------------------------------------------------------
  17786. void CTFPlayer::StopTauntSoundLoop()
  17787. {
  17788. if ( !m_strTauntSoundLoopName.IsEmpty() )
  17789. {
  17790. CReliableBroadcastRecipientFilter filter;
  17791. UserMessageBegin( filter, "PlayerTauntSoundLoopEnd" );
  17792. WRITE_BYTE( entindex() );
  17793. MessageEnd();
  17794. m_strTauntSoundLoopName = "";
  17795. }
  17796. }
  17797. //-----------------------------------------------------------------------------
  17798. // Purpose: Look for a nearby players who has started a
  17799. // high five and is waiting for a partner
  17800. //-----------------------------------------------------------------------------
  17801. CTFPlayer *CTFPlayer::FindPartnerTauntInitiator( void )
  17802. {
  17803. if ( tf_highfive_debug.GetBool() )
  17804. Msg( "%s looking for a partner taunt initiator.\n", GetPlayerName() );
  17805. CTFPlayer *pTargetInitiator = NULL;
  17806. float flDistSqrToTargetInitiator = FLT_MAX;
  17807. CUtlVector< CTFPlayer* > playerList;
  17808. CollectPlayers( &playerList, GetAllowedTauntPartnerTeam(), true );
  17809. for( int t=0; t<playerList.Count(); ++t )
  17810. {
  17811. CTFPlayer *pPlayer = playerList[t];
  17812. if ( pPlayer == this )
  17813. continue;
  17814. // don't allow bot to taunt with each other
  17815. if ( pPlayer->IsBot() && IsBot() )
  17816. continue;
  17817. if ( !pPlayer->IsReadyToTauntWithPartner() )
  17818. continue;
  17819. if ( tf_highfive_debug.GetBool() )
  17820. Msg( "%s is ready to %s.\n", pPlayer->GetPlayerName(), pPlayer->m_TauntEconItemView.GetStaticData()->GetDefinitionName() );
  17821. Vector toPartner = pPlayer->GetAbsOrigin() - GetAbsOrigin();
  17822. float flDistSqrToPlayer = toPartner.LengthSqr();
  17823. if ( flDistSqrToPlayer > Square( tf_highfive_max_range.GetFloat() ) )
  17824. {
  17825. if ( tf_highfive_debug.GetBool() )
  17826. Msg( " - but that player was too far away.\n" );
  17827. // too far away
  17828. continue;
  17829. }
  17830. // skip if this player is too far to be our initiator
  17831. if ( flDistSqrToPlayer >= flDistSqrToTargetInitiator )
  17832. {
  17833. if ( tf_highfive_debug.GetBool() )
  17834. {
  17835. Msg( " - is further than the current potential initiator.\n" );
  17836. }
  17837. continue;
  17838. }
  17839. toPartner.NormalizeInPlace();
  17840. Vector forward;
  17841. EyeVectors( &forward );
  17842. // check if I'm facing this player
  17843. if ( DotProduct( toPartner, forward ) < 0.6f )
  17844. {
  17845. if ( tf_highfive_debug.GetBool() )
  17846. Msg( " - but we are not looking at that player.\n" );
  17847. // we are not looking at this partner
  17848. continue;
  17849. }
  17850. bool bShouldCheckFacing = !pPlayer->m_bTauntMimic;
  17851. // check if the player is facing us
  17852. if ( bShouldCheckFacing )
  17853. {
  17854. Vector partnerForward = pPlayer->BodyDirection2D();
  17855. float toPartnerDotProduct = DotProduct( toPartner, partnerForward );
  17856. if ( tf_highfive_debug.GetBool() )
  17857. Msg( " - dot product to partner is %f\n", toPartnerDotProduct );
  17858. if ( toPartnerDotProduct > -0.6f )
  17859. {
  17860. if ( tf_highfive_debug.GetBool() )
  17861. Msg( " - but that player is not facing us.\n" );
  17862. // they are not facing us
  17863. continue;
  17864. }
  17865. }
  17866. // check if there's something between us
  17867. trace_t result;
  17868. CTraceFilterIgnoreTeammates filter( this, COLLISION_GROUP_NONE, GetAllowedTauntPartnerTeam() );
  17869. UTIL_TraceHull( GetAbsOrigin(), pPlayer->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result );
  17870. if ( result.DidHit() )
  17871. {
  17872. if ( tf_highfive_debug.GetBool() )
  17873. Msg( " - entity [%i %s %s] in between. tracing again with tolerance.\n",
  17874. result.GetEntityIndex(),
  17875. result.m_pEnt ? result.m_pEnt->GetClassname() : "NULL",
  17876. result.surface.name );
  17877. Vector offset( 0, 0, tf_highfive_height_tolerance.GetFloat() );
  17878. trace_t result2;
  17879. UTIL_TraceHull( GetAbsOrigin() + offset, pPlayer->GetAbsOrigin() + offset, VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result2 );
  17880. if ( result2.DidHit() )
  17881. {
  17882. if ( tf_highfive_debug.GetBool() )
  17883. Msg( " - entity [%i %s %s] in between.\n",
  17884. result2.GetEntityIndex(),
  17885. result2.m_pEnt ? result2.m_pEnt->GetClassname() : "NULL",
  17886. result2.surface.name );
  17887. // something is in between us
  17888. continue;
  17889. }
  17890. }
  17891. // Check to see if there's a spawn room visualizer between us and our partner
  17892. if( PointsCrossRespawnRoomVisualizer( WorldSpaceCenter(), pPlayer->WorldSpaceCenter() ) )
  17893. {
  17894. if ( tf_highfive_debug.GetBool() )
  17895. Msg( " - spawn room visualizer in between.\n" );
  17896. continue;
  17897. }
  17898. // update to closer target player
  17899. if ( flDistSqrToPlayer < flDistSqrToTargetInitiator )
  17900. {
  17901. // success!
  17902. if ( tf_highfive_debug.GetBool() )
  17903. Msg( " - is potentially the closest target player.\n" );
  17904. flDistSqrToTargetInitiator = flDistSqrToPlayer;
  17905. pTargetInitiator = pPlayer;
  17906. }
  17907. else if ( tf_highfive_debug.GetBool() )
  17908. {
  17909. Msg( " - is further than the current target player.\n" );
  17910. }
  17911. }
  17912. // pick the closest target player over the closest player
  17913. return pTargetInitiator;
  17914. }
  17915. //-----------------------------------------------------------------------------
  17916. // Purpose:
  17917. //-----------------------------------------------------------------------------
  17918. static bool SelectPartnerTaunt( const GameItemDefinition_t *pItemDef, CTFPlayer *initiator, CTFPlayer *receiver, const char **pszInitiatorScene, const char **pszReceiverScene )
  17919. {
  17920. static CSchemaItemDefHandle rpsTaunt( "RPS Taunt" );
  17921. CTFTauntInfo *pTauntData = pItemDef->GetTauntData();
  17922. if ( !pTauntData )
  17923. return false;
  17924. int iInitiatorClass = initiator->GetPlayerClass()->GetClassIndex();
  17925. int iReceiverClass = receiver->GetPlayerClass()->GetClassIndex();
  17926. // check if we have any scene
  17927. const int iInitiatorSceneCount = pTauntData->GetPartnerTauntInitiatorSceneCount( iInitiatorClass );
  17928. const int iReceiverSceneCount = pTauntData->GetPartnerTauntReceiverSceneCount( iReceiverClass );
  17929. if ( iInitiatorSceneCount == 0 ||
  17930. iReceiverSceneCount == 0 )
  17931. {
  17932. return false;
  17933. }
  17934. int iInitiator = 0;
  17935. int iReceiver = 0;
  17936. if ( pItemDef == rpsTaunt )
  17937. {
  17938. Assert( iInitiatorSceneCount == 6 && iReceiverSceneCount == 6 );
  17939. int iWinner = RandomInt( 0, 2 );
  17940. int iLoser = ( ( iWinner + 2 ) % 3 ) + 3;
  17941. /*static const char* s_pszRPS[3] = { "rock", "paper", "scissor" };
  17942. DevMsg( "%s beats %s\n", s_pszRPS[iWinner], s_pszRPS[iLoser%3] );*/
  17943. if ( RandomInt( 0, 1 ) )
  17944. {
  17945. iInitiator = iWinner;
  17946. iReceiver = iLoser;
  17947. }
  17948. else
  17949. {
  17950. iInitiator = iLoser;
  17951. iReceiver = iWinner;
  17952. }
  17953. initiator->SetRPSResult( iInitiator );
  17954. }
  17955. else
  17956. {
  17957. // randomly select a player to pick 0 (could be silent taunt)
  17958. // and other player select a different one if there's any
  17959. if ( RandomInt( 0, 1 ) == 0 )
  17960. {
  17961. iReceiver = iReceiverSceneCount > 1 ? RandomInt( 1, iReceiverSceneCount - 1 ) : 0;
  17962. }
  17963. else
  17964. {
  17965. iInitiator = iInitiatorSceneCount > 1 ? RandomInt( 1, iInitiatorSceneCount - 1 ) : 0;
  17966. }
  17967. }
  17968. *pszInitiatorScene = pTauntData->GetPartnerTauntInitiatorScene( iInitiatorClass, iInitiator );
  17969. *pszReceiverScene = pTauntData->GetPartnerTauntReceiverScene( iReceiverClass, iReceiver );
  17970. return true;
  17971. }
  17972. //-----------------------------------------------------------------------------
  17973. // Purpose:
  17974. //-----------------------------------------------------------------------------
  17975. void CTFPlayer::AcceptTauntWithPartner( CTFPlayer *initiator )
  17976. {
  17977. if ( !initiator )
  17978. {
  17979. return;
  17980. }
  17981. if ( tf_highfive_debug.GetBool() )
  17982. Msg( "%s doing %s with initiator %s.\n", GetPlayerName(), initiator->m_TauntEconItemView.GetStaticData()->GetDefinitionName(), initiator->GetPlayerName() );
  17983. // make sure this won't get us stuck
  17984. Vector newOrigin;
  17985. float flTolerance;
  17986. if ( !initiator->FindOpenTauntPartnerPosition( &initiator->m_TauntEconItemView, newOrigin, &flTolerance ))
  17987. {
  17988. if ( tf_highfive_debug.GetBool() )
  17989. Msg( " - but there is no open space for us.\n" );
  17990. return;
  17991. }
  17992. trace_t result;
  17993. CTraceFilterIgnoreTeammates filter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  17994. UTIL_TraceHull( newOrigin, newOrigin - Vector( 0, 0, flTolerance ), VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, &filter, &result );
  17995. if ( !result.DidHit() )
  17996. {
  17997. if ( tf_highfive_debug.GetBool() )
  17998. Msg( " - there's too much space underneath where we need to be.\n" );
  17999. return;
  18000. }
  18001. else
  18002. {
  18003. newOrigin = result.endpos;
  18004. }
  18005. trace_t stucktrace;
  18006. UTIL_TraceEntity( this, newOrigin, newOrigin, MASK_PLAYERSOLID, &filter, &stucktrace );
  18007. if ( stucktrace.startsolid != 0 )
  18008. {
  18009. if ( tf_highfive_debug.GetBool() )
  18010. Msg( " - but we'd get stuck on entity [%i %s %s] going in front of %s.\n",
  18011. stucktrace.GetEntityIndex(),
  18012. stucktrace.m_pEnt ? stucktrace.m_pEnt->GetClassname() : "NULL",
  18013. stucktrace.surface.name,
  18014. initiator->GetPlayerName() );
  18015. return;
  18016. }
  18017. // move us into facing position with initiator
  18018. SetAbsOrigin( newOrigin );
  18019. QAngle newAngles = initiator->GetAbsAngles();
  18020. // turn 180 degree to face the initiator
  18021. newAngles[YAW] = AngleNormalize( newAngles[YAW] - 180 );
  18022. SetAbsAngles( newAngles );
  18023. m_bIsReadyToHighFive = false;
  18024. initiator->m_bIsReadyToHighFive = false;
  18025. // note who our partner is so we can lock our facing toward them on the client
  18026. m_hHighFivePartner = initiator;
  18027. initiator->m_hHighFivePartner = this;
  18028. if ( initiator->m_hTauntScene.Get() )
  18029. {
  18030. StopScriptedScene( initiator, initiator->m_hTauntScene );
  18031. initiator->m_hTauntScene = NULL;
  18032. initiator->StopTauntSoundLoop();
  18033. }
  18034. const char *pszInitiatorScene = NULL;
  18035. const char *pszOurScene = NULL;
  18036. const GameItemDefinition_t *pItemDef = initiator->m_TauntEconItemView.GetItemDefinition();
  18037. if ( !SelectPartnerTaunt( pItemDef, initiator, this, &pszInitiatorScene, &pszOurScene ) )
  18038. {
  18039. if ( tf_highfive_debug.GetBool() )
  18040. {
  18041. Msg( "SpeakConceptIfAllowed failed on partner taunt initiator. Aborting taunt.\n" );
  18042. }
  18043. AssertMsg( false, "SpeakConceptIfAllowed failed on partner taunt initiator. Aborting taunt." );
  18044. initiator->m_flTauntRemoveTime = gpGlobals->curtime;
  18045. initiator->m_bAllowedToRemoveTaunt = true;
  18046. return;
  18047. }
  18048. m_TauntEconItemView = initiator->m_TauntEconItemView;
  18049. int initiatorConcept = MP_CONCEPT_HIGHFIVE_SUCCESS_FULL;
  18050. int ourConcept = MP_CONCEPT_HIGHFIVE_SUCCESS;
  18051. CMultiplayer_Expresser *pInitiatorExpresser = initiator->GetMultiplayerExpresser();
  18052. Assert( pInitiatorExpresser );
  18053. pInitiatorExpresser->AllowMultipleScenes();
  18054. // extend initiator's taunt duration to include actual high five
  18055. initiator->m_bInitTaunt = true;
  18056. initiator->PlayScene( pszInitiatorScene );
  18057. if ( tf_highfive_debug.GetBool() )
  18058. Msg( " concept %i started fine for initiator %s.\n", initiatorConcept, initiator->GetPlayerName() );
  18059. initiator->m_Shared.m_iTauntIndex = TAUNT_MISC_ITEM;
  18060. initiator->m_Shared.m_iTauntConcept.Set( initiatorConcept );
  18061. initiator->m_flTauntRemoveTime = gpGlobals->curtime + GetSceneDuration( pszInitiatorScene ) + 0.2f;
  18062. initiator->m_bAllowedToRemoveTaunt = true;
  18063. initiator->m_iTauntAttack = TAUNTATK_NONE;
  18064. initiator->m_flTauntAttackTime = 0.f;
  18065. static CSchemaAttributeDefHandle pAttrDef_TauntAttackName( "taunt attack name" );
  18066. const char* pszTauntAttackName = NULL;
  18067. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItemDef, pAttrDef_TauntAttackName, &pszTauntAttackName ) )
  18068. {
  18069. initiator->m_iTauntAttack = GetTauntAttackByName( pszTauntAttackName );
  18070. }
  18071. static CSchemaAttributeDefHandle pAttrDef_TauntAttackTime( "taunt attack time" );
  18072. float flTauntAttackTime = 0.f;
  18073. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItemDef, pAttrDef_TauntAttackTime, &flTauntAttackTime ) )
  18074. {
  18075. initiator->m_flTauntAttackTime = gpGlobals->curtime + flTauntAttackTime;
  18076. }
  18077. if ( GetActiveWeapon() )
  18078. {
  18079. m_iPreTauntWeaponSlot = GetActiveWeapon()->GetSlot();
  18080. }
  18081. PlayScene( pszOurScene );
  18082. OnTauntSucceeded( pszOurScene, TAUNT_MISC_ITEM, ourConcept );
  18083. const char *pszTauntSound = pItemDef->GetCustomSound( initiator->GetTeamNumber(), 0 );
  18084. if ( pszTauntSound )
  18085. {
  18086. // each participant hears the sound without PAS attenuation, but everyone else gets the PAS attenuation
  18087. EmitSound_t params;
  18088. params.m_pSoundName = pszTauntSound;
  18089. CSingleUserRecipientFilter soundFilterInitiator( initiator );
  18090. initiator->EmitSound( soundFilterInitiator, initiator->entindex(), params );
  18091. CSingleUserRecipientFilter soundFilter( this );
  18092. EmitSound( soundFilter, this->entindex(), params );
  18093. CPASAttenuationFilter attenuationFilter( this, params.m_pSoundName );
  18094. attenuationFilter.RemoveRecipient( this );
  18095. attenuationFilter.RemoveRecipient( initiator );
  18096. initiator->EmitSound( attenuationFilter, initiator->entindex(), params );
  18097. }
  18098. /*static CSchemaItemDefHandle highfiveTaunt( "High Five Taunt" );
  18099. if ( pItemDef == highfiveTaunt )
  18100. {
  18101. IGameEvent *pEvent = gameeventmanager->CreateEvent( "player_highfive_success" );
  18102. if ( pEvent )
  18103. {
  18104. pEvent->SetInt( "initiator_entindex", initiator->entindex() );
  18105. pEvent->SetInt( "partner_entindex", entindex() );
  18106. gameeventmanager->FireEvent( pEvent );
  18107. }
  18108. }*/
  18109. initiator->m_bInitTaunt = false;
  18110. pInitiatorExpresser->DisallowMultipleScenes();
  18111. // check for taunt achievements
  18112. if ( TFGameRules() && ( TFGameRules()->GetGameType() == TF_GAMETYPE_CP ) )
  18113. {
  18114. if ( !IsBot() && !initiator->IsBot() && ( GetTeamNumber() == initiator->GetTeamNumber() ) )
  18115. {
  18116. if ( IsCapturingPoint() && initiator->IsCapturingPoint() )
  18117. {
  18118. AwardAchievement( ACHIEVEMENT_TF_TAUNT_WHILE_CAPPING );
  18119. initiator->AwardAchievement( ACHIEVEMENT_TF_TAUNT_WHILE_CAPPING );
  18120. }
  18121. }
  18122. }
  18123. }
  18124. //-----------------------------------------------------------------------------
  18125. // Purpose:
  18126. //-----------------------------------------------------------------------------
  18127. void CTFPlayer::MimicTauntFromPartner( CTFPlayer *initiator )
  18128. {
  18129. Assert( initiator->m_bAllowMoveDuringTaunt );
  18130. if ( initiator->m_TauntEconItemView.IsValid() && initiator->m_TauntEconItemView.GetItemDefinition() != NULL )
  18131. {
  18132. PlayTauntSceneFromItem( &initiator->m_TauntEconItemView );
  18133. }
  18134. }
  18135. //-----------------------------------------------------------------------------
  18136. // Purpose:
  18137. //-----------------------------------------------------------------------------
  18138. extern ConVar tf_allow_all_team_partner_taunt;
  18139. int CTFPlayer::GetAllowedTauntPartnerTeam() const
  18140. {
  18141. return tf_allow_all_team_partner_taunt.GetBool() ? TEAM_ANY : GetTeamNumber();
  18142. }
  18143. //-----------------------------------------------------------------------------
  18144. // Purpose:
  18145. //-----------------------------------------------------------------------------
  18146. void CTFPlayer::IncrementKillCountSinceLastDeploy( const CTakeDamageInfo &info )
  18147. {
  18148. // track kills since last deploy, but only if our deployed weapon is the one we
  18149. // just killed someone with (this fixes the problem where you fire a rocket, switch
  18150. // weapons, and then get the kill tracked on the newly-deployed weapon)
  18151. CTFWeaponBase *pTFWeapon = dynamic_cast<CTFWeaponBase *>( info.GetWeapon() );
  18152. if ( pTFWeapon && ( pTFWeapon == GetActiveTFWeapon() ) )
  18153. {
  18154. m_Shared.m_iKillCountSinceLastDeploy++;
  18155. }
  18156. }
  18157. //-----------------------------------------------------------------------------
  18158. // Purpose: Return true if any enemy sentry has LOS and is facing me and is in range to attack
  18159. //-----------------------------------------------------------------------------
  18160. bool CTFPlayer::IsAnyEnemySentryAbleToAttackMe( void ) const
  18161. {
  18162. if ( m_Shared.InCond( TF_COND_DISGUISED ) ||
  18163. m_Shared.InCond( TF_COND_DISGUISING ) ||
  18164. m_Shared.IsStealthed() )
  18165. {
  18166. // I'm a disguised or cloaked Spy
  18167. return false;
  18168. }
  18169. for ( int i=0; i<IBaseObjectAutoList::AutoList().Count(); ++i )
  18170. {
  18171. CBaseObject* pObj = static_cast< CBaseObject* >( IBaseObjectAutoList::AutoList()[i] );
  18172. if ( pObj->ObjectType() != OBJ_SENTRYGUN )
  18173. continue;
  18174. if ( pObj->HasSapper() )
  18175. continue;
  18176. if ( pObj->IsPlasmaDisabled() )
  18177. continue;
  18178. if ( pObj->IsDisabled() )
  18179. continue;
  18180. if ( pObj->IsBuilding() )
  18181. continue;
  18182. if ( pObj->IsCarried() )
  18183. continue;
  18184. // are we in range?
  18185. if ( ( GetAbsOrigin() - pObj->GetAbsOrigin() ).IsLengthGreaterThan( SENTRY_MAX_RANGE ) )
  18186. continue;
  18187. // is the sentry aiming towards me?
  18188. if ( !IsThreatAimingTowardMe( pObj, 0.95f ) )
  18189. continue;
  18190. // does the sentry have clear line of fire?
  18191. if ( !IsLineOfSightClear( pObj, IGNORE_ACTORS ) )
  18192. continue;
  18193. // this sentry can attack me
  18194. return true;
  18195. }
  18196. return false;
  18197. }
  18198. //-----------------------------------------------------------------------------
  18199. // MVM Con Commands
  18200. //-----------------------------------------------------------------------------
  18201. CON_COMMAND_F( currency_give, "Have some in-game money.", FCVAR_CHEAT )
  18202. {
  18203. CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
  18204. if ( !pPlayer )
  18205. return;
  18206. int nAmount = atoi( args[1] );
  18207. #ifdef STAGING_ONLY
  18208. if ( TFGameRules() && TFGameRules()->GameModeUsesExperience() )
  18209. {
  18210. // This will give money, and calculate their level
  18211. pPlayer->AddExperiencePoints( nAmount, true );
  18212. return;
  18213. }
  18214. #endif // STAGING_ONLY
  18215. pPlayer->AddCurrency( nAmount );
  18216. }
  18217. //-----------------------------------------------------------------------------
  18218. // Purpose: Currency awarded directly will not be tracked by stats - see TFGameRules
  18219. //-----------------------------------------------------------------------------
  18220. void CTFPlayer::AddCurrency( int nAmount )
  18221. {
  18222. if ( nAmount + m_nCurrency > 30000 )
  18223. {
  18224. m_nCurrency = 30000;
  18225. }
  18226. else if ( nAmount + m_nCurrency < 0 )
  18227. {
  18228. m_nCurrency = 0;
  18229. }
  18230. else
  18231. {
  18232. m_nCurrency += nAmount;
  18233. }
  18234. }
  18235. //-----------------------------------------------------------------------------
  18236. // Purpose: Remove Currency from Display and track it as currency spent
  18237. //-----------------------------------------------------------------------------
  18238. void CTFPlayer::RemoveCurrency( int nAmount )
  18239. {
  18240. m_nCurrency = Max( m_nCurrency - nAmount, 0 );
  18241. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  18242. {
  18243. g_pPopulationManager->AddPlayerCurrencySpent( this, nAmount );
  18244. }
  18245. }
  18246. //-----------------------------------------------------------------------------
  18247. // Purpose: Ultra crude experience and level system
  18248. //-----------------------------------------------------------------------------
  18249. void CTFPlayer::AddExperiencePoints( int nAmount, bool bGiveCurrency /*= false*/, CTFPlayer *pSource /*= NULL*/ )
  18250. {
  18251. int nMyLevel = GetExperienceLevel();
  18252. // Adjust experience based on level difference of source player
  18253. if ( pSource )
  18254. {
  18255. int nLevelDiff = pSource->GetExperienceLevel() - nMyLevel;
  18256. if ( nLevelDiff <= -5 )
  18257. return;
  18258. if ( nLevelDiff > 0 )
  18259. {
  18260. nAmount *= ( nLevelDiff + 1 );
  18261. }
  18262. else if ( nLevelDiff < 0 )
  18263. {
  18264. nAmount /= ( abs( nLevelDiff ) + 1 );
  18265. }
  18266. }
  18267. m_nExperiencePoints += nAmount;
  18268. CalculateExperienceLevel();
  18269. // Money?
  18270. if ( bGiveCurrency && TFGameRules() )
  18271. {
  18272. TFGameRules()->DistributeCurrencyAmount( nAmount, this, false );
  18273. CTF_GameStats.Event_PlayerCollectedCurrency( this, nAmount );
  18274. EmitSound( "MVM.MoneyPickup" );
  18275. }
  18276. // DevMsg( "Exp: %d, Level: %d Perc: %d\n", GetExperiencePoints(), GetExperienceLevel(), m_nExperienceLevelProgress );
  18277. }
  18278. //-----------------------------------------------------------------------------
  18279. // Purpose:
  18280. //-----------------------------------------------------------------------------
  18281. void CTFPlayer::RefundExperiencePoints( void )
  18282. {
  18283. SetExperienceLevel( 1 );
  18284. int nAmount = 0;
  18285. PlayerStats_t *pPlayerStats = CTF_GameStats.FindPlayerStats( this );
  18286. if ( pPlayerStats )
  18287. {
  18288. nAmount = pPlayerStats->statsCurrentRound.m_iStat[TFSTAT_CURRENCY_COLLECTED];
  18289. }
  18290. if ( nAmount > 0 )
  18291. {
  18292. SetExperiencePoints(nAmount);
  18293. SetCurrency(nAmount);
  18294. }
  18295. CalculateExperienceLevel(false);
  18296. }
  18297. //-----------------------------------------------------------------------------
  18298. // Purpose:
  18299. //-----------------------------------------------------------------------------
  18300. void CTFPlayer::CalculateExperienceLevel( bool bAnnounce /*= true*/ )
  18301. {
  18302. int nMyLevel = GetExperienceLevel();
  18303. int nPrevLevel = nMyLevel;
  18304. float flLevel = ( (float)m_nExperiencePoints / 400.f ) + 1.f;
  18305. flLevel = Min( flLevel, 20.f );
  18306. // Ding?
  18307. if ( bAnnounce )
  18308. {
  18309. if ( flLevel > 1 && nPrevLevel != (int)flLevel )
  18310. {
  18311. const char *pszTeamName = GetTeamNumber() == TF_TEAM_RED ? "RED" : "BLU";
  18312. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#TF_PlayerLeveled", pszTeamName, GetPlayerName(), CFmtStr( "%d", (int)flLevel ) );
  18313. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#TF_PlayerLeveled", pszTeamName, GetPlayerName(), CFmtStr( "%d", (int)flLevel ) );
  18314. DispatchParticleEffect( "mvm_levelup1", PATTACH_POINT_FOLLOW, this, "head" );
  18315. EmitSound( "Achievement.Earned" );
  18316. }
  18317. }
  18318. flLevel = floor( flLevel );
  18319. SetExperienceLevel( Max( flLevel, 1.f ) );
  18320. // Update level progress percentage - networked
  18321. float flLevelPerc = ( flLevel - floor( flLevel ) ) * 100.f;
  18322. if ( m_nExperienceLevelProgress != flLevelPerc )
  18323. {
  18324. m_nExperienceLevelProgress.Set( (int)flLevelPerc );
  18325. }
  18326. }
  18327. //-----------------------------------------------------------------------------
  18328. // Purpose: Store this upgrade for restoring at a checkpoint
  18329. //-----------------------------------------------------------------------------
  18330. void CTFPlayer::RememberUpgrade( int iPlayerClass, CEconItemView *pItem, int iUpgrade, int nCost, bool bDowngrade )
  18331. {
  18332. if ( IsBot() )
  18333. return;
  18334. if ( !g_pPopulationManager )
  18335. {
  18336. Warning( "Remember Upgrade Error: Population Manager does not exist!\n" );
  18337. return;
  18338. }
  18339. if ( TFGameRules() == NULL || !TFGameRules()->GameModeUsesUpgrades() )
  18340. return;
  18341. item_definition_index_t iItemIndex = pItem ? pItem->GetItemDefIndex() : INVALID_ITEM_DEF_INDEX;
  18342. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  18343. if ( !bDowngrade )
  18344. {
  18345. CUpgradeInfo info;
  18346. info.m_iPlayerClass = iPlayerClass;
  18347. info.m_itemDefIndex = iItemIndex;
  18348. info.m_upgrade = iUpgrade;
  18349. info.m_nCost = nCost;
  18350. if ( upgrades )
  18351. {
  18352. upgrades->AddToTail( info );
  18353. }
  18354. m_RefundableUpgrades.AddToTail( info );
  18355. }
  18356. else
  18357. {
  18358. if ( upgrades )
  18359. {
  18360. for ( int i = 0; i < upgrades->Count(); ++i )
  18361. {
  18362. CUpgradeInfo pInfo = upgrades->Element(i);
  18363. if ( ( pInfo.m_itemDefIndex == iItemIndex ) && ( pInfo.m_upgrade == iUpgrade ) && ( pInfo.m_nCost == -nCost ) )
  18364. {
  18365. upgrades->Remove( i );
  18366. break;
  18367. }
  18368. }
  18369. }
  18370. // Subset of upgrades that can be sold back
  18371. for ( int i = 0; i < m_RefundableUpgrades.Count(); ++i )
  18372. {
  18373. CUpgradeInfo pInfo = m_RefundableUpgrades.Element( i );
  18374. if ( ( pInfo.m_itemDefIndex == iItemIndex ) && ( pInfo.m_upgrade == iUpgrade ) && ( pInfo.m_nCost == -nCost ) )
  18375. {
  18376. m_RefundableUpgrades.Remove( i );
  18377. break;
  18378. }
  18379. }
  18380. }
  18381. const char *upgradeName = g_hUpgradeEntity->GetUpgradeAttributeName( iUpgrade );
  18382. DevMsg( "%3.2f: %s: Player '%s', item '%s', upgrade '%s', cost '%d'\n",
  18383. gpGlobals->curtime,
  18384. bDowngrade ? "FORGET_UPGRADE" : "REMEMBER_UPGRADE",
  18385. GetPlayerName(),
  18386. pItem ? pItem->GetStaticData()->GetItemBaseName() : "<self>",
  18387. upgradeName ? upgradeName : "<NULL>",
  18388. nCost );
  18389. }
  18390. //-----------------------------------------------------------------------------
  18391. // Purpose: Erase the first upgrade stored for this item (for powerup bottles)
  18392. //-----------------------------------------------------------------------------
  18393. void CTFPlayer::ForgetFirstUpgradeForItem( CEconItemView *pItem )
  18394. {
  18395. if ( IsBot() )
  18396. return;
  18397. if ( TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  18398. return;
  18399. DevMsg( "%3.2f: FORGET_FIRST_UPGRADE_FOR_ITEM: Player '%s', item '%s'\n",
  18400. gpGlobals->curtime,
  18401. GetPlayerName(),
  18402. pItem ? pItem->GetStaticData()->GetItemBaseName() : "<self>" );
  18403. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  18404. if ( upgrades == NULL )
  18405. return;
  18406. for( int i = 0; i < upgrades->Count(); ++i )
  18407. {
  18408. if ( ( pItem == NULL && upgrades->Element( i ).m_itemDefIndex == INVALID_ITEM_DEF_INDEX ) || // self upgrade
  18409. upgrades->Element(i).m_itemDefIndex == pItem->GetItemDefIndex() ) // item upgrade
  18410. {
  18411. upgrades->Remove( i );
  18412. g_pPopulationManager->SendUpgradesToPlayer( this );
  18413. break;
  18414. }
  18415. }
  18416. }
  18417. //-----------------------------------------------------------------------------
  18418. // Purpose:
  18419. //-----------------------------------------------------------------------------
  18420. void CTFPlayer::ClearUpgradeHistory( void )
  18421. {
  18422. if( !g_pPopulationManager )
  18423. return;
  18424. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  18425. if ( upgrades != NULL )
  18426. upgrades->RemoveAll();
  18427. ResetAccumulatedSentryGunDamageDealt();
  18428. ResetAccumulatedSentryGunKillCount();
  18429. g_pPopulationManager->SendUpgradesToPlayer( this );
  18430. DevMsg( "%3.2f: CLEAR_UPGRADE_HISTORY: Player '%s'\n", gpGlobals->curtime, GetPlayerName() );
  18431. }
  18432. //-----------------------------------------------------------------------------
  18433. // Purpose:
  18434. //-----------------------------------------------------------------------------
  18435. void CTFPlayer::ReapplyItemUpgrades( CEconItemView *pItem )
  18436. {
  18437. if ( IsBot() || !g_pPopulationManager)
  18438. return;
  18439. int iClassIndex = GetPlayerClass()->GetClassIndex();
  18440. // Restore player Upgrades
  18441. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  18442. if ( upgrades == NULL )
  18443. return;
  18444. BeginPurchasableUpgrades();
  18445. for( int u = 0; u < upgrades->Count(); ++u )
  18446. {
  18447. // Player Upgrades for this class and item
  18448. const CUpgradeInfo& upgrade = upgrades->Element(u);
  18449. if ( iClassIndex == upgrade.m_iPlayerClass && pItem->GetItemDefIndex() == upgrade.m_itemDefIndex )
  18450. {
  18451. g_hUpgradeEntity->ApplyUpgradeToItem( this, pItem, upgrade.m_upgrade, upgrade.m_nCost );
  18452. }
  18453. }
  18454. EndPurchasableUpgrades();
  18455. }
  18456. //-----------------------------------------------------------------------------
  18457. // Purpose:
  18458. //-----------------------------------------------------------------------------
  18459. void CTFPlayer::ReapplyPlayerUpgrades( void )
  18460. {
  18461. if ( IsBot() || !g_pPopulationManager)
  18462. return;
  18463. int iClassIndex = GetPlayerClass()->GetClassIndex();
  18464. RemovePlayerAttributes( false );
  18465. CUtlVector< CUpgradeInfo > *upgrades = g_pPopulationManager->GetPlayerUpgradeHistory( this );
  18466. if ( upgrades == NULL )
  18467. return;
  18468. BeginPurchasableUpgrades();
  18469. // Restore player Upgrades
  18470. for( int u = 0; u < upgrades->Count(); ++u )
  18471. {
  18472. // Player Upgrades for this class
  18473. if ( iClassIndex == upgrades->Element(u).m_iPlayerClass)
  18474. {
  18475. // Upgrades applied to player
  18476. if ( upgrades->Element(u).m_itemDefIndex == INVALID_ITEM_DEF_INDEX )
  18477. {
  18478. g_hUpgradeEntity->ApplyUpgradeToItem( this, NULL, upgrades->Element(u).m_upgrade, upgrades->Element(u).m_nCost );
  18479. }
  18480. }
  18481. }
  18482. EndPurchasableUpgrades();
  18483. }
  18484. //-----------------------------------------------------------------------------
  18485. // Purpose:
  18486. //-----------------------------------------------------------------------------
  18487. void CTFPlayer::BeginPurchasableUpgrades( void )
  18488. {
  18489. m_nCanPurchaseUpgradesCount++;
  18490. if ( TFObjectiveResource()->GetMannVsMachineWaveCount() > 1 )
  18491. {
  18492. m_RefundableUpgrades.RemoveAll();
  18493. }
  18494. }
  18495. //-----------------------------------------------------------------------------
  18496. // Purpose:
  18497. //-----------------------------------------------------------------------------
  18498. void CTFPlayer::EndPurchasableUpgrades( void )
  18499. {
  18500. AssertMsg( m_nCanPurchaseUpgradesCount > 0, "EndPurchasableUpgrades called when m_nCanPurchaseUpgradesCount <= 0" );
  18501. if ( m_nCanPurchaseUpgradesCount <= 0 )
  18502. return;
  18503. m_nCanPurchaseUpgradesCount--;
  18504. if ( TFObjectiveResource()->GetMannVsMachineWaveCount() > 1 )
  18505. {
  18506. m_RefundableUpgrades.RemoveAll();
  18507. }
  18508. // report all upgrades
  18509. if ( g_pPopulationManager )
  18510. {
  18511. g_pPopulationManager->SendUpgradesToPlayer( this );
  18512. }
  18513. }
  18514. //-----------------------------------------------------------------------------
  18515. // Purpose:
  18516. //-----------------------------------------------------------------------------
  18517. void CTFPlayer::PlayReadySound( void )
  18518. {
  18519. if ( m_flLastReadySoundTime < gpGlobals->curtime )
  18520. {
  18521. if ( TFGameRules() )
  18522. {
  18523. int iTeam = GetTeamNumber();
  18524. const char *pszFormat = "%s.Ready";
  18525. if ( TFGameRules()->IsMannVsMachineMode() )
  18526. {
  18527. pszFormat = "%s.ReadyMvM";
  18528. }
  18529. else if ( TFGameRules()->IsCompetitiveMode() )
  18530. {
  18531. pszFormat = "%s.ReadyComp";
  18532. }
  18533. CFmtStr goYell( pszFormat, g_aPlayerClassNames_NonLocalized[ m_Shared.GetDesiredPlayerClassIndex() ] );
  18534. TFGameRules()->BroadcastSound( iTeam, goYell );
  18535. TFGameRules()->BroadcastSound( TEAM_SPECTATOR, goYell ); // spectators hear the ready sounds, too
  18536. m_flLastReadySoundTime = gpGlobals->curtime + 4.f;
  18537. }
  18538. }
  18539. }
  18540. //-----------------------------------------------------------------------------
  18541. // Purpose:
  18542. //-----------------------------------------------------------------------------
  18543. float CTFPlayer::GetDesiredHeadScale() const
  18544. {
  18545. float flDesiredHeadScale = 1.f;
  18546. CALL_ATTRIB_HOOK_FLOAT( flDesiredHeadScale, head_scale );
  18547. return flDesiredHeadScale;
  18548. }
  18549. //-----------------------------------------------------------------------------
  18550. // Purpose:
  18551. //-----------------------------------------------------------------------------
  18552. float CTFPlayer::GetHeadScaleSpeed() const
  18553. {
  18554. // change size now
  18555. if (
  18556. m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) ||
  18557. m_Shared.InCond( TF_COND_MELEE_ONLY ) ||
  18558. m_Shared.InCond( TF_COND_HALLOWEEN_KART ) ||
  18559. m_Shared.InCond( TF_COND_BALLOON_HEAD )
  18560. )
  18561. {
  18562. return GetDesiredHeadScale();
  18563. }
  18564. return gpGlobals->frametime;
  18565. }
  18566. //-----------------------------------------------------------------------------
  18567. // Purpose:
  18568. //-----------------------------------------------------------------------------
  18569. float CTFPlayer::GetDesiredTorsoScale() const
  18570. {
  18571. float flDesiredTorsoScale = 1.f;
  18572. CALL_ATTRIB_HOOK_FLOAT( flDesiredTorsoScale, torso_scale );
  18573. return flDesiredTorsoScale;
  18574. }
  18575. //-----------------------------------------------------------------------------
  18576. // Purpose:
  18577. //-----------------------------------------------------------------------------
  18578. float CTFPlayer::GetTorsoScaleSpeed() const
  18579. {
  18580. return gpGlobals->frametime;
  18581. }
  18582. //-----------------------------------------------------------------------------
  18583. // Purpose:
  18584. //-----------------------------------------------------------------------------
  18585. float CTFPlayer::GetDesiredHandScale() const
  18586. {
  18587. float flDesiredHandScale = 1.f;
  18588. CALL_ATTRIB_HOOK_FLOAT( flDesiredHandScale, hand_scale );
  18589. return flDesiredHandScale;
  18590. }
  18591. //-----------------------------------------------------------------------------
  18592. // Purpose:
  18593. //-----------------------------------------------------------------------------
  18594. float CTFPlayer::GetHandScaleSpeed() const
  18595. {
  18596. if ( m_Shared.InCond( TF_COND_MELEE_ONLY ) )
  18597. {
  18598. return GetDesiredHandScale();
  18599. }
  18600. return gpGlobals->frametime;
  18601. }
  18602. //-----------------------------------------------------------------------------
  18603. // Purpose:
  18604. //-----------------------------------------------------------------------------
  18605. void CTFPlayer::SetBombHeadTimestamp()
  18606. {
  18607. m_fLastBombHeadTimestamp = gpGlobals->curtime;
  18608. }
  18609. //-----------------------------------------------------------------------------
  18610. // Purpose:
  18611. //-----------------------------------------------------------------------------
  18612. float CTFPlayer::GetTimeSinceWasBombHead() const
  18613. {
  18614. return gpGlobals->curtime - m_fLastBombHeadTimestamp;
  18615. }
  18616. //-----------------------------------------------------------------------------
  18617. // Purpose: Can the player breathe under water?
  18618. //-----------------------------------------------------------------------------
  18619. bool CTFPlayer::CanBreatheUnderwater() const
  18620. {
  18621. if ( m_Shared.InCond( TF_COND_SWIMMING_CURSE ) )
  18622. return true;
  18623. int iCanBreatheUnderWater = 0;
  18624. CALL_ATTRIB_HOOK_INT( iCanBreatheUnderWater, can_breathe_under_water );
  18625. if ( iCanBreatheUnderWater != 0 )
  18626. return true;
  18627. return false;
  18628. }
  18629. //-----------------------------------------------------------------------------
  18630. // Purpose:
  18631. //-----------------------------------------------------------------------------
  18632. void CTFPlayer::InputSpeakResponseConcept( inputdata_t &inputdata )
  18633. {
  18634. const char *pInputString = STRING(inputdata.value.StringID());
  18635. // if no params, early out
  18636. if (!pInputString || *pInputString == 0)
  18637. {
  18638. Warning( "empty SpeakResponse input from %s to %s\n", inputdata.pCaller->GetDebugName(), GetDebugName() );
  18639. return;
  18640. }
  18641. char buf[512] = {0}; // temporary for tokenizing
  18642. char outputmodifiers[512] = {0}; // eventual output to speak
  18643. int outWritten = 0;
  18644. V_strncpy(buf, pInputString, 510);
  18645. buf[511] = 0; // just in case the last character is a comma -- enforce that the
  18646. // last character in the buffer is always a terminator.
  18647. // special syntax allowing designers to submit inputs with contexts like
  18648. // "concept,context1:value1,context2:value2,context3:value3"
  18649. // except that entity i/o seems to eat commas these days (didn't used to be the case)
  18650. // so instead of commas we have to use spaces in the entity IO,
  18651. // and turn them into commas here. AWESOME.
  18652. char *pModifiers = const_cast<char *>(V_strnchr(buf, ' ', 510));
  18653. if ( pModifiers )
  18654. {
  18655. *pModifiers = 0;
  18656. ++pModifiers;
  18657. // tokenize on spaces
  18658. char *token = strtok(pModifiers, " ");
  18659. while (token)
  18660. {
  18661. // find the start characters for the key and value
  18662. // (seperated by a : which we replace with null)
  18663. char * RESTRICT key = token;
  18664. char * RESTRICT colon = const_cast<char *>(V_strnchr(key, ':', 510));
  18665. char * RESTRICT value;
  18666. if (!colon)
  18667. {
  18668. Warning( "faulty context k:v pair in entity io %s\n", pInputString );
  18669. break;
  18670. }
  18671. // write the key and colon to the output string
  18672. int toWrite = colon - key + 1;
  18673. if ( outWritten + toWrite >= 512 )
  18674. {
  18675. Warning( "Speak input to %s had overlong parameter %s", GetDebugName(), pInputString );
  18676. return;
  18677. }
  18678. memcpy(outputmodifiers + outWritten, key, toWrite);
  18679. outWritten += toWrite;
  18680. *colon = 0;
  18681. value = colon + 1;
  18682. // determine if the value is actually a procedural name
  18683. CBaseEntity *pProcedural = gEntList.FindEntityProcedural( value, this, inputdata.pActivator, inputdata.pCaller );
  18684. // write the value to the output -- if it's a procedural name, replace appropriately;
  18685. // if not, just copy over.
  18686. const char *valString;
  18687. if (pProcedural)
  18688. {
  18689. valString = STRING(pProcedural->GetEntityName());
  18690. }
  18691. else
  18692. {
  18693. valString = value;
  18694. }
  18695. toWrite = strlen(valString);
  18696. toWrite = MIN( 511-outWritten, toWrite );
  18697. V_strncpy( outputmodifiers + outWritten, valString, toWrite+1 );
  18698. outWritten += toWrite;
  18699. // get the next token
  18700. token = strtok(NULL, " ");
  18701. if (token)
  18702. {
  18703. // if there is a next token, write in a comma
  18704. if (outWritten < 511)
  18705. {
  18706. outputmodifiers[outWritten++]=',';
  18707. }
  18708. }
  18709. }
  18710. }
  18711. // null terminate just in case
  18712. outputmodifiers[outWritten <= 511 ? outWritten : 511] = 0;
  18713. SpeakConceptIfAllowed( GetMPConceptIndexFromString( buf ), outputmodifiers[0] ? outputmodifiers : NULL );
  18714. }
  18715. //-----------------------------------------------------------------------------
  18716. // Purpose:
  18717. //-----------------------------------------------------------------------------
  18718. void CTFPlayer::CreateDisguiseWeaponList( CTFPlayer *pDisguiseTarget )
  18719. {
  18720. ClearDisguiseWeaponList();
  18721. // copy disguise target weapons
  18722. if ( pDisguiseTarget )
  18723. {
  18724. for ( int i=0; i<TF_PLAYER_WEAPON_COUNT; ++i )
  18725. {
  18726. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>( pDisguiseTarget->GetWeapon( i ) );
  18727. if ( !pWeapon )
  18728. continue;
  18729. CEconItemView *pItem = NULL;
  18730. // We are copying a generated, non-base item.
  18731. CAttributeContainer *pContainer = pWeapon->GetAttributeContainer();
  18732. if ( pContainer )
  18733. {
  18734. pItem = pContainer->GetItem();
  18735. }
  18736. int iSubType = 0;
  18737. CTFWeaponBase *pCopyWeapon = dynamic_cast<CTFWeaponBase*>( GiveNamedItem( pWeapon->GetClassname(), iSubType, pItem, true ) );
  18738. if ( pCopyWeapon )
  18739. {
  18740. pCopyWeapon->AddEffects( EF_NODRAW | EF_NOSHADOW );
  18741. m_hDisguiseWeaponList.AddToTail( pCopyWeapon );
  18742. }
  18743. }
  18744. }
  18745. }
  18746. //-----------------------------------------------------------------------------
  18747. // Purpose:
  18748. //-----------------------------------------------------------------------------
  18749. void CTFPlayer::ClearDisguiseWeaponList()
  18750. {
  18751. FOR_EACH_VEC( m_hDisguiseWeaponList, i )
  18752. {
  18753. if ( m_hDisguiseWeaponList[i] )
  18754. {
  18755. m_hDisguiseWeaponList[i]->Drop( vec3_origin );
  18756. }
  18757. }
  18758. m_hDisguiseWeaponList.RemoveAll();
  18759. }
  18760. //-----------------------------------------------------------------------------
  18761. // Purpose:
  18762. //-----------------------------------------------------------------------------
  18763. bool CTFPlayer::CanScorePointForPD( void ) const
  18764. {
  18765. // These conditions block being able to score in PD
  18766. ETFCond blockingConds[] = { TF_COND_STEALTHED // Invis spies
  18767. , TF_COND_STEALTHED_BLINK
  18768. , TF_COND_DISGUISING // Disguised spies
  18769. , TF_COND_DISGUISED
  18770. , TF_COND_INVULNERABLE // Uber
  18771. , TF_COND_PHASE }; // Bonked Scouts
  18772. // Check for blocking conditions
  18773. for( int i=0; i<ARRAYSIZE(blockingConds); ++i )
  18774. {
  18775. if ( m_Shared.InCond( blockingConds[i] ) )
  18776. {
  18777. return false;
  18778. }
  18779. }
  18780. // More aggressively deny invis than the code above
  18781. if ( m_Shared.GetPercentInvisible() > 0.f )
  18782. {
  18783. return false;
  18784. }
  18785. // Rate limit
  18786. return ( ( m_flNextScorePointForPD < 0 ) || ( m_flNextScorePointForPD < gpGlobals->curtime ) );
  18787. }
  18788. //-----------------------------------------------------------------------------
  18789. // Purpose:
  18790. //-----------------------------------------------------------------------------
  18791. bool CTFPlayer::PickupWeaponFromOther( CTFDroppedWeapon *pDroppedWeapon )
  18792. {
  18793. const CEconItemView *pItem = pDroppedWeapon->GetItem();
  18794. if ( !pItem )
  18795. return false;
  18796. if ( pItem->IsValid() )
  18797. {
  18798. int iClass = GetPlayerClass()->GetClassIndex();
  18799. int iItemSlot = pItem->GetStaticData()->GetLoadoutSlot( iClass );
  18800. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( GetEntityForLoadoutSlot( iItemSlot ) );
  18801. if ( !pWeapon )
  18802. {
  18803. AssertMsg( false, "No weapon to put down when picking up a dropped weapon!" );
  18804. return false;
  18805. }
  18806. // we need to force translating the name here.
  18807. // GiveNamedItem will not translate if we force creating the item
  18808. const char *pTranslatedWeaponName = TranslateWeaponEntForClass( pItem->GetStaticData()->GetItemClass(), iClass );
  18809. CTFWeaponBase *pNewItem = dynamic_cast<CTFWeaponBase*>( GiveNamedItem( pTranslatedWeaponName, 0, pItem, true ));
  18810. Assert( pNewItem );
  18811. if ( pNewItem )
  18812. {
  18813. CTFWeaponBuilder *pBuilder = dynamic_cast<CTFWeaponBuilder*>( (CBaseEntity*)pNewItem );
  18814. if ( pBuilder )
  18815. {
  18816. pBuilder->SetSubType( GetPlayerClass()->GetData()->m_aBuildable[0] );
  18817. }
  18818. // make sure we removed our current weapon
  18819. if ( pWeapon )
  18820. {
  18821. // drop current weapon
  18822. Vector vecPackOrigin;
  18823. QAngle vecPackAngles;
  18824. CalculateAmmoPackPositionAndAngles( pWeapon, vecPackOrigin, vecPackAngles );
  18825. bool bShouldThrowHeldWeapon = true;
  18826. // When in the spawn room, you won't throw down your held weapon if you own that weapon.
  18827. // This is to prevent folks from standing near a supply closet and spawning their items
  18828. // over and over and over.
  18829. if ( PointInRespawnRoom( this, WorldSpaceCenter() ) )
  18830. {
  18831. CSteamID playerSteamID;
  18832. GetSteamID( &playerSteamID );
  18833. uint32 nItemAccountID = pWeapon->GetAttributeContainer()->GetItem()->GetAccountID();
  18834. // Stock weapons have accountID 0
  18835. if ( playerSteamID.GetAccountID() == nItemAccountID || nItemAccountID == 0 )
  18836. {
  18837. bShouldThrowHeldWeapon = false;
  18838. }
  18839. }
  18840. if ( bShouldThrowHeldWeapon )
  18841. {
  18842. CTFDroppedWeapon *pNewDroppedWeapon = CTFDroppedWeapon::Create( this, vecPackOrigin, vecPackAngles, pWeapon->GetWorldModel(), pWeapon->GetAttributeContainer()->GetItem() );
  18843. if ( pNewDroppedWeapon )
  18844. {
  18845. pNewDroppedWeapon->InitDroppedWeapon( this, pWeapon, true );
  18846. }
  18847. }
  18848. Weapon_Detach( pWeapon );
  18849. UTIL_Remove( pWeapon );
  18850. }
  18851. CBaseCombatWeapon *pLastWeapon = GetLastWeapon();
  18852. pNewItem->MarkAttachedEntityAsValidated();
  18853. pNewItem->GiveTo( this );
  18854. Weapon_SetLast( pLastWeapon );
  18855. pDroppedWeapon->InitPickedUpWeapon( this, pNewItem );
  18856. // can't use the weapon we just picked up?
  18857. if ( !Weapon_CanSwitchTo( pNewItem ) )
  18858. {
  18859. // try next best thing we can use
  18860. SwitchToNextBestWeapon( pNewItem );
  18861. }
  18862. // delay pickup weapon message
  18863. m_flSendPickupWeaponMessageTime = gpGlobals->curtime + 0.1f;
  18864. return true;
  18865. }
  18866. }
  18867. return false;
  18868. }
  18869. //-----------------------------------------------------------------------------
  18870. // Purpose:
  18871. //-----------------------------------------------------------------------------
  18872. bool CTFPlayer::TryToPickupDroppedWeapon()
  18873. {
  18874. if ( !CanAttack() )
  18875. return false;
  18876. if ( GetActiveWeapon() && ( GetActiveWeapon()->m_flNextPrimaryAttack > gpGlobals->curtime ) )
  18877. return false;
  18878. CTFDroppedWeapon *pDroppedWeapon = GetDroppedWeaponInRange();
  18879. if ( pDroppedWeapon && !pDroppedWeapon->IsMarkedForDeletion() )
  18880. {
  18881. if ( PickupWeaponFromOther( pDroppedWeapon ) )
  18882. {
  18883. UTIL_Remove( pDroppedWeapon );
  18884. return true;
  18885. }
  18886. }
  18887. return false;
  18888. }
  18889. //-----------------------------------------------------------------------------
  18890. // Purpose:
  18891. //-----------------------------------------------------------------------------
  18892. void CTFPlayer::AddCustomAttribute( const char *pszAttributeName, float flVal, float flDuration /*= -1.f*/ )
  18893. {
  18894. float flExpireTime = flDuration > 0 ? gpGlobals->curtime + flDuration : flDuration;
  18895. int iIndex = m_mapCustomAttributes.Find( pszAttributeName );
  18896. if ( iIndex == m_mapCustomAttributes.InvalidIndex() )
  18897. {
  18898. m_mapCustomAttributes.Insert( pszAttributeName, flExpireTime );
  18899. }
  18900. else
  18901. {
  18902. // stomp the previous expire time for now
  18903. m_mapCustomAttributes[iIndex] = flExpireTime;
  18904. }
  18905. // just stomp the value
  18906. m_Shared.ApplyAttributeToPlayer( pszAttributeName, flVal );
  18907. }
  18908. //-----------------------------------------------------------------------------
  18909. // Purpose:
  18910. //-----------------------------------------------------------------------------
  18911. void CTFPlayer::RemoveCustomAttribute( const char *pszAttributeName )
  18912. {
  18913. int iIndex = m_mapCustomAttributes.Find( pszAttributeName );
  18914. if ( iIndex != m_mapCustomAttributes.InvalidIndex() )
  18915. {
  18916. m_Shared.RemoveAttributeFromPlayer( pszAttributeName );
  18917. m_mapCustomAttributes.RemoveAt( iIndex );
  18918. }
  18919. }
  18920. //-----------------------------------------------------------------------------
  18921. // Purpose:
  18922. //-----------------------------------------------------------------------------
  18923. void CTFPlayer::UpdateCustomAttributes()
  18924. {
  18925. // check if we should remove custom attributes from player
  18926. bool bShouldCheckCustomAttributes = m_mapCustomAttributes.Count() > 0;
  18927. while ( bShouldCheckCustomAttributes )
  18928. {
  18929. bShouldCheckCustomAttributes = false;
  18930. FOR_EACH_MAP_FAST( m_mapCustomAttributes, i )
  18931. {
  18932. float flExpireTime = m_mapCustomAttributes[i];
  18933. if ( flExpireTime > 0 && gpGlobals->curtime > flExpireTime )
  18934. {
  18935. const char *pszAttributeName = m_mapCustomAttributes.Key( i );
  18936. m_Shared.RemoveAttributeFromPlayer( pszAttributeName );
  18937. m_mapCustomAttributes.RemoveAt( i );
  18938. bShouldCheckCustomAttributes = true;
  18939. break;
  18940. }
  18941. }
  18942. }
  18943. }
  18944. //-----------------------------------------------------------------------------
  18945. // Purpose:
  18946. //-----------------------------------------------------------------------------
  18947. void CTFPlayer::RemoveAllCustomAttributes()
  18948. {
  18949. FOR_EACH_MAP_FAST( m_mapCustomAttributes, i )
  18950. {
  18951. const char *pszAttributeName = m_mapCustomAttributes.Key( i );
  18952. m_Shared.RemoveAttributeFromPlayer( pszAttributeName );
  18953. }
  18954. m_mapCustomAttributes.RemoveAll();
  18955. }
  18956. //-----------------------------------------------------------------------------
  18957. // Purpose:
  18958. //-----------------------------------------------------------------------------
  18959. bool CTFPlayer::ShouldForceTransmitsForTeam( int iTeam )
  18960. {
  18961. return ( ( GetTeamNumber() == TEAM_SPECTATOR ) ||
  18962. ( ( GetTeamNumber() == iTeam ) && ( m_Shared.InCond( TF_COND_TEAM_GLOWS ) || !IsAlive() ) ) );
  18963. }
  18964. //-----------------------------------------------------------------------------
  18965. // Purpose:
  18966. //-----------------------------------------------------------------------------
  18967. bool CTFPlayer::ShouldGetBonusPointsForExtinguishEvent( int userID )
  18968. {
  18969. int iIndex = m_PlayersExtinguished.Find( userID );
  18970. if ( iIndex != m_PlayersExtinguished.InvalidIndex() )
  18971. {
  18972. if ( ( gpGlobals->curtime - m_PlayersExtinguished[iIndex] ) < 20.f )
  18973. return false;
  18974. m_PlayersExtinguished[iIndex] = gpGlobals->curtime;
  18975. }
  18976. else
  18977. {
  18978. m_PlayersExtinguished.Insert( userID, gpGlobals->curtime );
  18979. }
  18980. return true;
  18981. }
  18982. //-----------------------------------------------------------------------------
  18983. // Purpose:
  18984. //-----------------------------------------------------------------------------
  18985. bool CTFPlayer::IsTruceValidForEnt( void ) const
  18986. {
  18987. if ( PointInRespawnRoom( this, WorldSpaceCenter(), true ) )
  18988. return false;
  18989. return true;
  18990. }