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.

14162 lines
413 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_gamerules.h"
  8. #include "tf_player_shared.h"
  9. #include "takedamageinfo.h"
  10. #include "tf_weaponbase.h"
  11. #include "effect_dispatch_data.h"
  12. #include "tf_item.h"
  13. #include "entity_capture_flag.h"
  14. #include "tf_weapon_medigun.h"
  15. #include "tf_weapon_pipebomblauncher.h"
  16. #include "tf_weapon_invis.h"
  17. #include "tf_weapon_sniperrifle.h"
  18. #include "tf_weapon_shovel.h"
  19. #include "tf_weapon_sword.h"
  20. #include "tf_weapon_shotgun.h"
  21. #include "in_buttons.h"
  22. #include "tf_weapon_lunchbox.h"
  23. #include "tf_weapon_flaregun.h"
  24. #include "tf_weapon_wrench.h"
  25. #include "econ_wearable.h"
  26. #include "econ_item_system.h"
  27. #include "tf_weapon_knife.h"
  28. #include "tf_weapon_syringegun.h"
  29. #include "tf_weapon_flamethrower.h"
  30. #include "econ_entity_creation.h"
  31. #include "tf_mapinfo.h"
  32. #include "tf_dropped_weapon.h"
  33. #include "tf_weapon_passtime_gun.h"
  34. // Client specific.
  35. #ifdef CLIENT_DLL
  36. #include "c_tf_player.h"
  37. #include "c_te_effect_dispatch.h"
  38. #include "c_tf_fx.h"
  39. #include "soundenvelope.h"
  40. #include "c_tf_playerclass.h"
  41. #include "iviewrender.h"
  42. #include "prediction.h"
  43. #include "achievementmgr.h"
  44. #include "baseachievement.h"
  45. #include "achievements_tf.h"
  46. #include "c_tf_weapon_builder.h"
  47. #include "dt_utlvector_recv.h"
  48. #include "recvproxy.h"
  49. #include "c_tf_weapon_builder.h"
  50. #include "c_func_capture_zone.h"
  51. #include "tf_hud_target_id.h"
  52. #include "tempent.h"
  53. #include "cam_thirdperson.h"
  54. #include "vgui/IInput.h"
  55. #define CTFPlayerClass C_TFPlayerClass
  56. #define CCaptureZone C_CaptureZone
  57. #define CRecipientFilter C_RecipientFilter
  58. #include "c_tf_objective_resource.h"
  59. #include "tf_weapon_buff_item.h"
  60. #include "c_tf_passtime_logic.h"
  61. // Server specific.
  62. #else
  63. #include "tf_player.h"
  64. #include "te_effect_dispatch.h"
  65. #include "tf_fx.h"
  66. #include "util.h"
  67. #include "tf_team.h"
  68. #include "tf_gamestats.h"
  69. #include "tf_playerclass.h"
  70. #include "SpriteTrail.h"
  71. #include "tf_weapon_builder.h"
  72. #include "nav_mesh/tf_nav_area.h"
  73. #include "nav_pathfind.h"
  74. #include "tf_obj_dispenser.h"
  75. #include "dt_utlvector_send.h"
  76. #include "tf_item_wearable.h"
  77. #include "NextBotManager.h"
  78. #include "tf_weapon_builder.h"
  79. #include "func_capture_zone.h"
  80. #include "hl2orange.spa.h"
  81. #include "bot/tf_bot.h"
  82. #include "tf_objective_resource.h"
  83. #include "halloween/tf_weapon_spellbook.h"
  84. #include "tf_weapon_buff_item.h"
  85. #include "tf_passtime_logic.h"
  86. #include "tf_weapon_passtime_gun.h"
  87. #include "entity_healthkit.h"
  88. #include "halloween/merasmus/merasmus.h"
  89. #include "tf_weapon_grapplinghook.h"
  90. #include "tf_wearable_levelable_item.h"
  91. #include "tf_obj_sentrygun.h"
  92. #endif
  93. #include "tf_wearable_item_demoshield.h"
  94. #include "tf_weapon_bonesaw.h"
  95. static ConVar tf_demoman_charge_frametime_scaling( "tf_demoman_charge_frametime_scaling", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "When enabled, scale yaw limiting based on client performance (frametime)." );
  96. static const float YAW_CAP_SCALE_MIN = 0.2f;
  97. static const float YAW_CAP_SCALE_MAX = 2.f;
  98. ConVar tf_halloween_kart_boost_recharge( "tf_halloween_kart_boost_recharge", "5.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
  99. ConVar tf_halloween_kart_boost_duration( "tf_halloween_kart_boost_duration", "1.5f", FCVAR_REPLICATED | FCVAR_CHEAT );
  100. ConVar tf_scout_air_dash_count( "tf_scout_air_dash_count", "1", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  101. ConVar tf_spy_invis_time( "tf_spy_invis_time", "1.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Transition time in and out of spy invisibility", true, 0.1, true, 5.0 );
  102. ConVar tf_spy_invis_unstealth_time( "tf_spy_invis_unstealth_time", "2.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Transition time in and out of spy invisibility", true, 0.1, true, 5.0 );
  103. ConVar tf_spy_max_cloaked_speed( "tf_spy_max_cloaked_speed", "999", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED ); // no cap
  104. ConVar tf_whip_speed_increase( "tf_whip_speed_increase", "105", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  105. ConVar tf_max_health_boost( "tf_max_health_boost", "1.5", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Max health factor that players can be boosted to by healers.", true, 1.0, false, 0 );
  106. ConVar tf_invuln_time( "tf_invuln_time", "1.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Time it takes for invulnerability to wear off." );
  107. ConVar tf_player_movement_stun_time( "tf_player_movement_stun_time", "0.5", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  108. extern ConVar tf_player_movement_restart_freeze;
  109. extern ConVar mp_tournament_readymode_countdown;
  110. extern ConVar tf_max_charge_speed;
  111. ConVar tf_always_loser( "tf_always_loser", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "Force loserstate to true." );
  112. ConVar tf_mvm_bot_flag_carrier_movement_penalty( "tf_mvm_bot_flag_carrier_movement_penalty", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT );
  113. //ConVar tf_scout_dodge_move_penalty_duration( "tf_scout_dodge_move_penalty_duration", "3.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  114. //ConVar tf_scout_dodge_move_penalty( "tf_scout_dodge_move_penalty", "0.5", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  115. #ifdef GAME_DLL
  116. ConVar tf_boost_drain_time( "tf_boost_drain_time", "15.0", FCVAR_DEVELOPMENTONLY, "Time is takes for a full health boost to drain away from a player.", true, 0.1, false, 0 );
  117. #ifdef _DEBUG
  118. CON_COMMAND_F( tf_add_bombhead, "Add Merasmus Bomb Head Condition", 0 )
  119. {
  120. CUtlVector< CTFPlayer * > playerVector;
  121. CollectPlayers( &playerVector, TF_TEAM_RED, true );
  122. FOR_EACH_VEC ( playerVector, i )
  123. {
  124. float flBuffDuration = 7.0f;
  125. playerVector[i]->m_Shared.StunPlayer( flBuffDuration, 0.f, TF_STUN_LOSER_STATE );
  126. playerVector[i]->m_Shared.AddCond( TF_COND_HALLOWEEN_BOMB_HEAD, flBuffDuration );
  127. playerVector[i]->m_Shared.AddCond( TF_COND_SPEED_BOOST, flBuffDuration );
  128. //playerVector[i]->m_Shared.AddCond( TF_COND_HALLOWEEN_BOMB_HEAD, 7 );
  129. }
  130. }
  131. ConVar tf_debug_bullets( "tf_debug_bullets", "0", FCVAR_DEVELOPMENTONLY, "Visualize bullet traces." );
  132. #endif // _DEBUG
  133. ConVar tf_damage_events_track_for( "tf_damage_events_track_for", "30", FCVAR_DEVELOPMENTONLY );
  134. extern ConVar tf_halloween_giant_health_scale;
  135. ConVar tf_allow_sliding_taunt( "tf_allow_sliding_taunt", "0", FCVAR_NONE, "1 - Allow player to slide for a bit after taunting" );
  136. #endif // GAME_DLL
  137. #ifdef STAGING_ONLY
  138. ConVar tf_force_allow_move_during_taunt( "tf_force_allow_move_during_taunt", "0", FCVAR_REPLICATED );
  139. #endif // STAGING_ONLY
  140. ConVar tf_useparticletracers( "tf_useparticletracers", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use particle tracers instead of old style ones." );
  141. ConVar tf_spy_cloak_consume_rate( "tf_spy_cloak_consume_rate", "10.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "cloak to use per second while cloaked, from 100 max )" ); // 10 seconds of invis
  142. ConVar tf_spy_cloak_regen_rate( "tf_spy_cloak_regen_rate", "3.3", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "cloak to regen per second, up to 100 max" ); // 30 seconds to full charge
  143. ConVar tf_spy_cloak_no_attack_time( "tf_spy_cloak_no_attack_time", "2.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "time after uncloaking that the spy is prohibited from attacking" );
  144. ConVar tf_tournament_hide_domination_icons( "tf_tournament_hide_domination_icons", "0", FCVAR_REPLICATED, "Tournament mode server convar that forces clients to not display the domination icons above players dominating them." );
  145. ConVar tf_damage_disablespread( "tf_damage_disablespread", "1", FCVAR_REPLICATED | FCVAR_NOTIFY, "Toggles the random damage spread applied to all player damage." );
  146. ConVar tf_scout_energydrink_regen_rate( "tf_scout_energydrink_regen_rate", "3.3", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "energy drink regen per second, up to 100 max" );
  147. ConVar tf_scout_energydrink_consume_rate( "tf_scout_energydrink_consume_rate", "12.5", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "energy drink to use per second while boosted, from 100 max" );
  148. ConVar tf_scout_energydrink_activation( "tf_scout_energydrink_activation", "0.0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "how long it takes for the energy buff to become active" );
  149. ConVar tf_demoman_charge_regen_rate( "tf_demoman_charge_regen_rate", "8.3", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "" );
  150. ConVar tf_demoman_charge_drain_time( "tf_demoman_charge_drain_time", "1.5", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "" );
  151. // STAGING_SPY
  152. ConVar tf_feign_death_duration( "tf_feign_death_duration", "3.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Time that feign death buffs last." );
  153. ConVar tf_feign_death_speed_duration( "tf_feign_death_speed_duration", "3.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Time that feign death speed boost last." );
  154. ConVar tf_allow_taunt_switch( "tf_allow_taunt_switch", "0", FCVAR_REPLICATED, "0 - players are not allowed to switch weapons while taunting, 1 - players can switch weapons at the start of a taunt (old bug behavior), 2 - players can switch weapons at any time during a taunt." );
  155. ConVar tf_allow_all_team_partner_taunt( "tf_allow_all_team_partner_taunt", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  156. #ifdef STAGING_ONLY
  157. ConVar tf_random_item_min( "tf_random_item_min", "-1", FCVAR_REPLICATED, "Min Itemdef for random cosmetics" );
  158. ConVar tf_random_item_max( "tf_random_item_max", "-1", FCVAR_REPLICATED, "Max Itemdef for random cosmetics" );
  159. ConVar tf_killstreak_eyeglow( "tf_killstreak_eyeglow", "0", FCVAR_REPLICATED, "Force Kill Streak effect index for Eye Glows. -1 to disable regardless of equipped items. Stats at 2002" );
  160. ConVar tf_killstreak_color( "tf_killstreak_color", "0", FCVAR_REPLICATED, "Force Kill Streak color." );
  161. ConVar tf_eyeglow_wip( "tf_eyeglow_wip", "0", FCVAR_REPLICATED, "Activate to Always have specific wip eyeglow (DEV)" );
  162. #endif // Staging only
  163. #ifdef CLIENT_DLL
  164. ConVar tf_colorblindassist( "tf_colorblindassist", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Setting this to 1 turns on colorblind mode." );
  165. extern ConVar cam_idealdist;
  166. extern ConVar cam_idealdistright;
  167. #endif // CLIENT_DLL
  168. extern ConVar tf_flamethrower_flametime;
  169. extern ConVar weapon_medigun_chargerelease_rate;
  170. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  171. extern ConVar mp_developer;
  172. #endif // _DEBUG || STAGING_ONLY
  173. //ConVar tf_spy_stealth_blink_time( "tf_spy_stealth_blink_time", "0.3", FCVAR_DEVELOPMENTONLY, "time after being hit the spy blinks into view" );
  174. //ConVar tf_spy_stealth_blink_scale( "tf_spy_stealth_blink_scale", "0.85", FCVAR_DEVELOPMENTONLY, "percentage visible scalar after being hit the spy blinks into view" );
  175. #define TF_SPY_STEALTH_BLINKTIME 0.3f
  176. #define TF_SPY_STEALTH_BLINKSCALE 0.85f
  177. #define TF_BUILDING_PICKUP_RANGE 150
  178. #define TF_BUILDING_RESCUE_MIN_RANGE_SQ 62500 //250 * 250
  179. #define TF_BUILDING_RESCUE_MAX_RANGE 5500
  180. #define TF_PLAYER_CONDITION_CONTEXT "TFPlayerConditionContext"
  181. #define TF_SCREEN_OVERLAY_MATERIAL_BURNING "effects/imcookin"
  182. #define TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED "effects/invuln_overlay_red"
  183. #define TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE "effects/invuln_overlay_blue"
  184. #define TF_SCREEN_OVERLAY_MATERIAL_MILK "effects/milk_screen"
  185. #define TF_SCREEN_OVERLAY_MATERIAL_URINE "effects/jarate_overlay"
  186. #define TF_SCREEN_OVERLAY_MATERIAL_BLEED "effects/bleed_overlay"
  187. #define TF_SCREEN_OVERLAY_MATERIAL_STEALTH "effects/stealth_overlay"
  188. #define TF_SCREEN_OVERLAY_MATERIAL_SWIMMING_CURSE "effects/jarate_overlay"
  189. #define TF_SCREEN_OVERLAY_MATERIAL_PHASE "effects/dodge_overlay"
  190. #define MAX_DAMAGE_EVENTS 128
  191. const char *g_pszBDayGibs[22] =
  192. {
  193. "models/effects/bday_gib01.mdl",
  194. "models/effects/bday_gib02.mdl",
  195. "models/effects/bday_gib03.mdl",
  196. "models/effects/bday_gib04.mdl",
  197. "models/player/gibs/gibs_balloon.mdl",
  198. "models/player/gibs/gibs_burger.mdl",
  199. "models/player/gibs/gibs_boot.mdl",
  200. "models/player/gibs/gibs_bolt.mdl",
  201. "models/player/gibs/gibs_can.mdl",
  202. "models/player/gibs/gibs_clock.mdl",
  203. "models/player/gibs/gibs_fish.mdl",
  204. "models/player/gibs/gibs_gear1.mdl",
  205. "models/player/gibs/gibs_gear2.mdl",
  206. "models/player/gibs/gibs_gear3.mdl",
  207. "models/player/gibs/gibs_gear4.mdl",
  208. "models/player/gibs/gibs_gear5.mdl",
  209. "models/player/gibs/gibs_hubcap.mdl",
  210. "models/player/gibs/gibs_licenseplate.mdl",
  211. "models/player/gibs/gibs_spring1.mdl",
  212. "models/player/gibs/gibs_spring2.mdl",
  213. "models/player/gibs/gibs_teeth.mdl",
  214. "models/player/gibs/gibs_tire.mdl"
  215. };
  216. ETFCond g_SoldierBuffAttributeIDToConditionMap[kSoldierBuffCount + 1] =
  217. {
  218. TF_COND_LAST, // dummy entry to deal with attribute value of "1" being the lowest value we store in the attribute itself
  219. TF_COND_OFFENSEBUFF,
  220. TF_COND_DEFENSEBUFF,
  221. TF_COND_REGENONDAMAGEBUFF,
  222. TF_COND_NOHEALINGDAMAGEBUFF,
  223. TF_COND_CRITBOOSTED_RAGE_BUFF,
  224. TF_COND_SNIPERCHARGE_RAGE_BUFF
  225. };
  226. #ifdef CLIENT_DLL
  227. //-----------------------------------------------------------------------------
  228. // Purpose:
  229. //-----------------------------------------------------------------------------
  230. void RecvProxy_BuildablesListChanged( const CRecvProxyData *pData, void *pStruct, void *pOut )
  231. {
  232. RecvProxy_Int32ToInt32( pData, pStruct, pOut );
  233. C_TFPlayer* pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  234. if ( !pLocalPlayer || pLocalPlayer->entindex() != pData->m_ObjectID )
  235. return;
  236. int index = pData->m_pRecvProp->GetOffset() / sizeof(int);
  237. int object = pData->m_Value.m_Int;
  238. IGameEvent *event = gameeventmanager->CreateEvent( "update_status_item" );
  239. if ( event )
  240. {
  241. event->SetInt( "index", index );
  242. event->SetInt( "object", object );
  243. gameeventmanager->FireEventClientSide( event );
  244. }
  245. }
  246. #endif
  247. //=============================================================================
  248. //
  249. // Tables.
  250. //
  251. // Client specific.
  252. #ifdef CLIENT_DLL
  253. BEGIN_RECV_TABLE_NOBASE( localplayerscoring_t, DT_TFPlayerScoringDataExclusive )
  254. RecvPropInt( RECVINFO( m_iCaptures ) ),
  255. RecvPropInt( RECVINFO( m_iDefenses ) ),
  256. RecvPropInt( RECVINFO( m_iKills ) ),
  257. RecvPropInt( RECVINFO( m_iDeaths ) ),
  258. RecvPropInt( RECVINFO( m_iSuicides ) ),
  259. RecvPropInt( RECVINFO( m_iDominations ) ),
  260. RecvPropInt( RECVINFO( m_iRevenge ) ),
  261. RecvPropInt( RECVINFO( m_iBuildingsBuilt ) ),
  262. RecvPropInt( RECVINFO( m_iBuildingsDestroyed ) ),
  263. RecvPropInt( RECVINFO( m_iHeadshots ) ),
  264. RecvPropInt( RECVINFO( m_iBackstabs ) ),
  265. RecvPropInt( RECVINFO( m_iHealPoints ) ),
  266. RecvPropInt( RECVINFO( m_iInvulns ) ),
  267. RecvPropInt( RECVINFO( m_iTeleports ) ),
  268. RecvPropInt( RECVINFO( m_iResupplyPoints ) ),
  269. RecvPropInt( RECVINFO( m_iKillAssists ) ),
  270. RecvPropInt( RECVINFO( m_iPoints ) ),
  271. RecvPropInt( RECVINFO( m_iBonusPoints ) ),
  272. RecvPropInt( RECVINFO( m_iDamageDone ) ),
  273. RecvPropInt( RECVINFO( m_iCrits ) ),
  274. END_RECV_TABLE()
  275. EXTERN_RECV_TABLE(DT_TFPlayerConditionListExclusive);
  276. BEGIN_RECV_TABLE_NOBASE( CTFPlayerShared, DT_TFPlayerSharedLocal )
  277. RecvPropInt( RECVINFO( m_nDesiredDisguiseTeam ) ),
  278. RecvPropInt( RECVINFO( m_nDesiredDisguiseClass ) ),
  279. RecvPropTime( RECVINFO( m_flStealthNoAttackExpire ) ),
  280. RecvPropTime( RECVINFO( m_flStealthNextChangeTime ) ),
  281. RecvPropBool( RECVINFO( m_bLastDisguisedAsOwnTeam ) ),
  282. RecvPropFloat( RECVINFO( m_flRageMeter ) ),
  283. RecvPropBool( RECVINFO( m_bRageDraining ) ),
  284. RecvPropTime( RECVINFO( m_flNextRageEarnTime ) ),
  285. RecvPropBool( RECVINFO( m_bInUpgradeZone ) ),
  286. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ),
  287. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) ),
  288. RecvPropDataTable( RECVINFO_DT(m_ScoreData),0, &REFERENCE_RECV_TABLE(DT_TFPlayerScoringDataExclusive) ),
  289. RecvPropDataTable( RECVINFO_DT(m_RoundScoreData),0, &REFERENCE_RECV_TABLE(DT_TFPlayerScoringDataExclusive) ),
  290. END_RECV_TABLE()
  291. BEGIN_RECV_TABLE_NOBASE( condition_source_t, DT_TFPlayerConditionSource )
  292. //RecvPropInt( RECVINFO( m_nPreventedDamageFromCondition ) ),
  293. //RecvPropFloat( RECVINFO( m_flExpireTime ) ),
  294. RecvPropEHandle( RECVINFO( m_pProvider ) ),
  295. //RecvPropBool( RECVINFO( m_bPrevActive ) ),
  296. END_RECV_TABLE()
  297. BEGIN_RECV_TABLE_NOBASE( CTFPlayerShared, DT_TFPlayerShared )
  298. RecvPropInt( RECVINFO( m_nPlayerCond ) ),
  299. RecvPropInt( RECVINFO( m_bJumping) ),
  300. RecvPropInt( RECVINFO( m_nNumHealers ) ),
  301. RecvPropInt( RECVINFO( m_iCritMult ) ),
  302. RecvPropInt( RECVINFO( m_iAirDash ) ),
  303. RecvPropInt( RECVINFO( m_nAirDucked ) ),
  304. RecvPropFloat( RECVINFO( m_flDuckTimer ) ),
  305. RecvPropInt( RECVINFO( m_nPlayerState ) ),
  306. RecvPropInt( RECVINFO( m_iDesiredPlayerClass ) ),
  307. RecvPropFloat( RECVINFO( m_flMovementStunTime ) ),
  308. RecvPropInt( RECVINFO( m_iMovementStunAmount ) ),
  309. RecvPropInt( RECVINFO( m_iMovementStunParity ) ),
  310. RecvPropEHandle( RECVINFO( m_hStunner ) ),
  311. RecvPropInt( RECVINFO( m_iStunFlags ) ),
  312. RecvPropInt( RECVINFO( m_nArenaNumChanges ) ),
  313. RecvPropBool( RECVINFO( m_bArenaFirstBloodBoost ) ),
  314. RecvPropInt( RECVINFO( m_iWeaponKnockbackID ) ),
  315. RecvPropBool( RECVINFO( m_bLoadoutUnavailable ) ),
  316. RecvPropInt( RECVINFO( m_iItemFindBonus ) ),
  317. RecvPropBool( RECVINFO( m_bShieldEquipped ) ),
  318. RecvPropBool( RECVINFO( m_bParachuteEquipped ) ),
  319. RecvPropInt( RECVINFO( m_iNextMeleeCrit ) ),
  320. RecvPropInt( RECVINFO( m_iDecapitations ) ),
  321. RecvPropInt( RECVINFO( m_iRevengeCrits ) ),
  322. RecvPropInt( RECVINFO( m_iDisguiseBody ) ),
  323. RecvPropEHandle( RECVINFO( m_hCarriedObject ) ),
  324. RecvPropBool( RECVINFO( m_bCarryingObject ) ),
  325. RecvPropFloat( RECVINFO( m_flNextNoiseMakerTime ) ),
  326. RecvPropInt( RECVINFO( m_iSpawnRoomTouchCount ) ),
  327. RecvPropInt( RECVINFO( m_iKillCountSinceLastDeploy ) ),
  328. RecvPropFloat( RECVINFO( m_flFirstPrimaryAttack ) ),
  329. //Scout
  330. RecvPropFloat( RECVINFO( m_flEnergyDrinkMeter) ),
  331. RecvPropFloat( RECVINFO( m_flHypeMeter) ),
  332. // Demoman
  333. RecvPropFloat( RECVINFO( m_flChargeMeter) ),
  334. // Spy.
  335. RecvPropTime( RECVINFO( m_flInvisChangeCompleteTime ) ),
  336. RecvPropInt( RECVINFO( m_nDisguiseTeam ) ),
  337. RecvPropInt( RECVINFO( m_nDisguiseClass ) ),
  338. RecvPropInt( RECVINFO( m_nDisguiseSkinOverride ) ),
  339. RecvPropInt( RECVINFO( m_nMaskClass ) ),
  340. RecvPropInt( RECVINFO( m_iDisguiseTargetIndex ) ),
  341. RecvPropInt( RECVINFO( m_iDisguiseHealth ) ),
  342. RecvPropBool( RECVINFO( m_bFeignDeathReady ) ),
  343. RecvPropEHandle( RECVINFO( m_hDisguiseWeapon ) ),
  344. RecvPropInt( RECVINFO( m_nTeamTeleporterUsed ) ),
  345. RecvPropFloat( RECVINFO( m_flCloakMeter ) ),
  346. RecvPropFloat( RECVINFO( m_flSpyTranqBuffDuration ) ),
  347. // Local Data.
  348. RecvPropDataTable( "tfsharedlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_TFPlayerSharedLocal) ),
  349. RecvPropDataTable( RECVINFO_DT(m_ConditionList),0, &REFERENCE_RECV_TABLE(DT_TFPlayerConditionListExclusive) ),
  350. RecvPropInt( RECVINFO( m_iTauntIndex ) ),
  351. RecvPropInt( RECVINFO( m_iTauntConcept ) ),
  352. RecvPropInt( RECVINFO( m_nPlayerCondEx ) ),
  353. RecvPropInt( RECVINFO( m_iStunIndex ) ),
  354. RecvPropInt( RECVINFO( m_nHalloweenBombHeadStage ) ),
  355. RecvPropInt( RECVINFO( m_nPlayerCondEx2 ) ),
  356. RecvPropInt( RECVINFO( m_nPlayerCondEx3 ) ),
  357. RecvPropArray3( RECVINFO_ARRAY( m_nStreaks ), RecvPropInt( RECVINFO( m_nStreaks[0] ) ) ),
  358. RecvPropInt( RECVINFO( m_unTauntSourceItemID_Low ) ),
  359. RecvPropInt( RECVINFO( m_unTauntSourceItemID_High ) ),
  360. RecvPropFloat( RECVINFO( m_flRuneCharge ) ),
  361. #ifdef STAGING_ONLY
  362. RecvPropFloat( RECVINFO( m_flSpaceJumpCharge ) ),
  363. #endif
  364. RecvPropBool( RECVINFO( m_bHasPasstimeBall ) ),
  365. RecvPropBool( RECVINFO( m_bIsTargetedForPasstimePass ) ),
  366. RecvPropEHandle( RECVINFO( m_hPasstimePassTarget ) ),
  367. RecvPropFloat( RECVINFO( m_askForBallTime ) ),
  368. RecvPropBool( RECVINFO( m_bKingRuneBuffActive ) ),
  369. RecvPropUtlVectorDataTable( m_ConditionData, TF_COND_LAST, DT_TFPlayerConditionSource ),
  370. END_RECV_TABLE()
  371. BEGIN_PREDICTION_DATA_NO_BASE( CTFPlayerShared )
  372. DEFINE_PRED_FIELD( m_nPlayerState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  373. DEFINE_PRED_FIELD( m_nPlayerCond, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  374. DEFINE_PRED_FIELD( m_flCloakMeter, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  375. DEFINE_PRED_FIELD( m_flRageMeter, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  376. DEFINE_PRED_FIELD( m_bRageDraining, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  377. DEFINE_PRED_FIELD( m_flNextRageEarnTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  378. DEFINE_PRED_FIELD( m_flEnergyDrinkMeter, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  379. DEFINE_PRED_FIELD( m_flHypeMeter, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  380. DEFINE_PRED_FIELD( m_flChargeMeter, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  381. DEFINE_PRED_FIELD( m_bJumping, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  382. DEFINE_PRED_FIELD( m_iAirDash, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  383. DEFINE_PRED_FIELD( m_nAirDucked, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  384. DEFINE_PRED_FIELD( m_flDuckTimer, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  385. DEFINE_PRED_FIELD( m_flInvisChangeCompleteTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  386. DEFINE_PRED_FIELD( m_nDisguiseTeam, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  387. DEFINE_PRED_FIELD( m_nDisguiseClass, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  388. DEFINE_PRED_FIELD( m_nDisguiseSkinOverride, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  389. DEFINE_PRED_FIELD( m_nMaskClass, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  390. DEFINE_PRED_FIELD( m_nDesiredDisguiseTeam, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  391. DEFINE_PRED_FIELD( m_nDesiredDisguiseClass, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  392. DEFINE_PRED_FIELD( m_bLastDisguisedAsOwnTeam, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  393. DEFINE_PRED_FIELD( m_bFeignDeathReady, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  394. DEFINE_PRED_FIELD( m_nPlayerCondEx, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  395. DEFINE_PRED_FIELD( m_nPlayerCondEx2, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  396. DEFINE_PRED_FIELD( m_nPlayerCondEx3, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  397. // DEFINE_PRED_FIELD( m_hDisguiseWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
  398. DEFINE_FIELD( m_flDisguiseCompleteTime, FIELD_FLOAT ),
  399. DEFINE_PRED_FIELD( m_bHasPasstimeBall, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  400. DEFINE_PRED_FIELD( m_bIsTargetedForPasstimePass, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), // does this belong here?
  401. DEFINE_PRED_FIELD( m_askForBallTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  402. END_PREDICTION_DATA()
  403. // Server specific.
  404. #else
  405. BEGIN_SEND_TABLE_NOBASE( localplayerscoring_t, DT_TFPlayerScoringDataExclusive )
  406. SendPropInt( SENDINFO( m_iCaptures ), 10, SPROP_UNSIGNED ),
  407. SendPropInt( SENDINFO( m_iDefenses ), 10, SPROP_UNSIGNED ),
  408. SendPropInt( SENDINFO( m_iKills ), 10, SPROP_UNSIGNED ),
  409. SendPropInt( SENDINFO( m_iDeaths ), 10, SPROP_UNSIGNED ),
  410. SendPropInt( SENDINFO( m_iSuicides ), 10, SPROP_UNSIGNED ),
  411. SendPropInt( SENDINFO( m_iDominations ), 10, SPROP_UNSIGNED ),
  412. SendPropInt( SENDINFO( m_iRevenge ), 10, SPROP_UNSIGNED ),
  413. SendPropInt( SENDINFO( m_iBuildingsBuilt ), 10, SPROP_UNSIGNED ),
  414. SendPropInt( SENDINFO( m_iBuildingsDestroyed ), 10, SPROP_UNSIGNED ),
  415. SendPropInt( SENDINFO( m_iHeadshots ), 10, SPROP_UNSIGNED ),
  416. SendPropInt( SENDINFO( m_iBackstabs ), 10, SPROP_UNSIGNED ),
  417. SendPropInt( SENDINFO( m_iHealPoints ), 20, SPROP_UNSIGNED ),
  418. SendPropInt( SENDINFO( m_iInvulns ), 10, SPROP_UNSIGNED ),
  419. SendPropInt( SENDINFO( m_iTeleports ), 10, SPROP_UNSIGNED ),
  420. SendPropInt( SENDINFO( m_iDamageDone ), 20, SPROP_UNSIGNED ),
  421. SendPropInt( SENDINFO( m_iCrits ), 10, SPROP_UNSIGNED ),
  422. SendPropInt( SENDINFO( m_iResupplyPoints ), 10, SPROP_UNSIGNED ),
  423. SendPropInt( SENDINFO( m_iKillAssists ), 12, SPROP_UNSIGNED ),
  424. SendPropInt( SENDINFO( m_iBonusPoints ), 10, SPROP_UNSIGNED ),
  425. SendPropInt( SENDINFO( m_iPoints ), 10, SPROP_UNSIGNED ),
  426. END_SEND_TABLE()
  427. EXTERN_SEND_TABLE(DT_TFPlayerConditionListExclusive);
  428. BEGIN_SEND_TABLE_NOBASE( CTFPlayerShared, DT_TFPlayerSharedLocal )
  429. SendPropInt( SENDINFO( m_nDesiredDisguiseTeam ), 3, SPROP_UNSIGNED ),
  430. SendPropInt( SENDINFO( m_nDesiredDisguiseClass ), 4, SPROP_UNSIGNED ),
  431. SendPropBool( SENDINFO( m_bLastDisguisedAsOwnTeam ) ),
  432. SendPropTime( SENDINFO( m_flStealthNoAttackExpire ) ),
  433. SendPropTime( SENDINFO( m_flStealthNextChangeTime ) ),
  434. SendPropFloat( SENDINFO( m_flRageMeter ), 0, SPROP_NOSCALE, 0.0, 100.0 ),
  435. SendPropBool( SENDINFO( m_bRageDraining ) ),
  436. SendPropTime( SENDINFO( m_flNextRageEarnTime ) ),
  437. SendPropBool( SENDINFO( m_bInUpgradeZone ) ),
  438. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ),
  439. SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ),
  440. SendPropDataTable( SENDINFO_DT(m_ScoreData), &REFERENCE_SEND_TABLE(DT_TFPlayerScoringDataExclusive) ),
  441. SendPropDataTable( SENDINFO_DT(m_RoundScoreData), &REFERENCE_SEND_TABLE(DT_TFPlayerScoringDataExclusive) ),
  442. END_SEND_TABLE()
  443. BEGIN_SEND_TABLE_NOBASE( condition_source_t, DT_TFPlayerConditionSource )
  444. //SendPropInt( SENDINFO( m_nPreventedDamageFromCondition ) ),
  445. //SendPropFloat( SENDINFO( m_flExpireTime ) ),
  446. SendPropEHandle( SENDINFO( m_pProvider ) ),
  447. //SendPropBool( SENDINFO( m_bPrevActive ) ),
  448. END_SEND_TABLE()
  449. BEGIN_SEND_TABLE_NOBASE( CTFPlayerShared, DT_TFPlayerShared )
  450. SendPropInt( SENDINFO( m_nPlayerCond ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  451. SendPropInt( SENDINFO( m_bJumping ), 1, SPROP_UNSIGNED ),
  452. SendPropInt( SENDINFO( m_nNumHealers ), 5, SPROP_UNSIGNED ),
  453. SendPropInt( SENDINFO( m_iCritMult ), 8, SPROP_UNSIGNED ),
  454. SendPropInt( SENDINFO( m_iAirDash ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  455. SendPropInt( SENDINFO( m_nAirDucked ), 2, SPROP_UNSIGNED ),
  456. SendPropFloat( SENDINFO( m_flDuckTimer ) ),
  457. SendPropInt( SENDINFO( m_nPlayerState ), Q_log2( TF_STATE_COUNT )+1, SPROP_UNSIGNED ),
  458. SendPropInt( SENDINFO( m_iDesiredPlayerClass ), Q_log2( TF_CLASS_COUNT_ALL )+1, SPROP_UNSIGNED ),
  459. SendPropFloat( SENDINFO( m_flMovementStunTime ) ),
  460. SendPropInt( SENDINFO( m_iMovementStunAmount ), 8, SPROP_UNSIGNED ),
  461. SendPropInt( SENDINFO( m_iMovementStunParity ), MOVEMENTSTUN_PARITY_BITS, SPROP_UNSIGNED ),
  462. SendPropEHandle( SENDINFO( m_hStunner ) ),
  463. SendPropInt( SENDINFO( m_iStunFlags ), 12, SPROP_UNSIGNED ),
  464. SendPropInt( SENDINFO( m_nArenaNumChanges ), 5, SPROP_UNSIGNED ),
  465. SendPropBool( SENDINFO( m_bArenaFirstBloodBoost ) ),
  466. SendPropInt( SENDINFO( m_iWeaponKnockbackID ) ),
  467. SendPropBool( SENDINFO( m_bLoadoutUnavailable ) ),
  468. SendPropInt( SENDINFO( m_iItemFindBonus ) ),
  469. SendPropBool( SENDINFO( m_bShieldEquipped ) ),
  470. SendPropBool( SENDINFO( m_bParachuteEquipped ) ),
  471. SendPropInt( SENDINFO( m_iNextMeleeCrit ) ),
  472. SendPropInt( SENDINFO( m_iDecapitations ), 8, SPROP_UNSIGNED ),
  473. SendPropInt( SENDINFO( m_iRevengeCrits ), 7, SPROP_UNSIGNED ),
  474. SendPropInt( SENDINFO( m_iDisguiseBody ) ),
  475. SendPropEHandle( SENDINFO( m_hCarriedObject ) ),
  476. SendPropBool( SENDINFO( m_bCarryingObject ) ),
  477. SendPropFloat( SENDINFO( m_flNextNoiseMakerTime ) ),
  478. SendPropInt( SENDINFO( m_iSpawnRoomTouchCount ) ),
  479. SendPropInt( SENDINFO( m_iKillCountSinceLastDeploy ) ),
  480. SendPropFloat( SENDINFO( m_flFirstPrimaryAttack ) ),
  481. //Scout
  482. SendPropFloat( SENDINFO( m_flEnergyDrinkMeter ), 0, SPROP_NOSCALE, 0.0, 100.0 ),
  483. SendPropFloat( SENDINFO( m_flHypeMeter ), 0, SPROP_NOSCALE, 0.0, 100.0 ),
  484. // Demoman
  485. SendPropFloat( SENDINFO( m_flChargeMeter ), 8, SPROP_NOSCALE, 0.0, 100.0 ),
  486. // Spy
  487. SendPropTime( SENDINFO( m_flInvisChangeCompleteTime ) ),
  488. SendPropInt( SENDINFO( m_nDisguiseTeam ), 3, SPROP_UNSIGNED ),
  489. SendPropInt( SENDINFO( m_nDisguiseClass ), 4, SPROP_UNSIGNED ),
  490. SendPropInt( SENDINFO( m_nDisguiseSkinOverride ), 1, SPROP_UNSIGNED ),
  491. SendPropInt( SENDINFO( m_nMaskClass ), 4, SPROP_UNSIGNED ),
  492. SendPropInt( SENDINFO( m_iDisguiseTargetIndex ), 7, SPROP_UNSIGNED ),
  493. SendPropInt( SENDINFO( m_iDisguiseHealth ), -1, SPROP_VARINT ),
  494. SendPropBool( SENDINFO( m_bFeignDeathReady ) ),
  495. SendPropEHandle( SENDINFO( m_hDisguiseWeapon ) ),
  496. SendPropInt( SENDINFO( m_nTeamTeleporterUsed ), 3, SPROP_UNSIGNED ),
  497. SendPropFloat( SENDINFO( m_flCloakMeter ), 16, SPROP_NOSCALE, 0.0, 100.0 ),
  498. SendPropFloat( SENDINFO( m_flSpyTranqBuffDuration ), 16, SPROP_NOSCALE, 0.0, 100.0 ),
  499. // Local Data.
  500. SendPropDataTable( "tfsharedlocaldata", 0, &REFERENCE_SEND_TABLE( DT_TFPlayerSharedLocal ), SendProxy_SendLocalDataTable ),
  501. SendPropDataTable( SENDINFO_DT(m_ConditionList), &REFERENCE_SEND_TABLE(DT_TFPlayerConditionListExclusive) ),
  502. SendPropInt( SENDINFO( m_iTauntIndex ), 8, SPROP_UNSIGNED ),
  503. SendPropInt( SENDINFO( m_iTauntConcept ), 8, SPROP_UNSIGNED ),
  504. SendPropInt( SENDINFO( m_nPlayerCondEx ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  505. SendPropInt( SENDINFO( m_iStunIndex ), 8 ),
  506. SendPropInt( SENDINFO( m_nHalloweenBombHeadStage ), 2, SPROP_UNSIGNED ),
  507. SendPropInt( SENDINFO( m_nPlayerCondEx2 ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  508. SendPropInt( SENDINFO( m_nPlayerCondEx3 ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  509. SendPropArray3( SENDINFO_ARRAY3( m_nStreaks ), SendPropInt( SENDINFO_ARRAY( m_nStreaks ) ) ),
  510. SendPropInt( SENDINFO( m_unTauntSourceItemID_Low ), -1, SPROP_UNSIGNED ),
  511. SendPropInt( SENDINFO( m_unTauntSourceItemID_High ), -1, SPROP_UNSIGNED ),
  512. SendPropFloat( SENDINFO( m_flRuneCharge ), 8, 0, 0.0, 100.0 ),
  513. #ifdef STAGING_ONLY
  514. SendPropFloat( SENDINFO( m_flSpaceJumpCharge ), 8, 0, 0.0, 100.0 ),
  515. #endif
  516. SendPropBool( SENDINFO( m_bHasPasstimeBall ) ),
  517. SendPropBool( SENDINFO( m_bIsTargetedForPasstimePass ) ),
  518. SendPropEHandle( SENDINFO( m_hPasstimePassTarget ) ),
  519. SendPropFloat( SENDINFO( m_askForBallTime ) ),
  520. SendPropBool( SENDINFO( m_bKingRuneBuffActive ) ),
  521. SendPropUtlVectorDataTable( m_ConditionData, TF_COND_LAST, DT_TFPlayerConditionSource ),
  522. END_SEND_TABLE()
  523. #endif
  524. extern void HandleRageGain( CTFPlayer *pPlayer, unsigned int iRequiredBuffFlags, float flDamage, float fInverseRageGainScale );
  525. CTFWearableDemoShield* GetEquippedDemoShield( CTFPlayer * pPlayer )
  526. {
  527. // Loop through our wearables in search of a shield
  528. for ( int i=0; i<pPlayer->GetNumWearables(); ++i )
  529. {
  530. CTFWearableDemoShield *pWearableShield = dynamic_cast<CTFWearableDemoShield*>( pPlayer->GetWearable( i ) );
  531. if ( pWearableShield )
  532. {
  533. return pWearableShield;
  534. }
  535. }
  536. return NULL;
  537. }
  538. CTFPlayer *GetRuneCarrier( RuneTypes_t type, int iTeam = TEAM_ANY )
  539. {
  540. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  541. {
  542. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  543. if ( !pPlayer )
  544. continue;
  545. if ( iTeam != TEAM_ANY && pPlayer->GetTeamNumber() != iTeam )
  546. continue;
  547. if ( pPlayer->m_Shared.GetCarryingRuneType() == type )
  548. {
  549. return pPlayer;
  550. }
  551. }
  552. return NULL;
  553. }
  554. // --------------------------------------------------------------------------------------------------- //
  555. // Shared CTFPlayer implementation.
  556. // --------------------------------------------------------------------------------------------------- //
  557. //-----------------------------------------------------------------------------
  558. // Purpose:
  559. //-----------------------------------------------------------------------------
  560. bool CTFPlayer::HasCampaignMedal( int iMedal )
  561. {
  562. return ( ( m_iCampaignMedals & iMedal ) != 0 );
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Purpose:
  566. //-----------------------------------------------------------------------------
  567. bool CTFPlayer::IsAllowedToTaunt( void )
  568. {
  569. if ( !IsAlive() )
  570. return false;
  571. // Check to see if we can taunt again!
  572. if ( m_Shared.InCond( TF_COND_TAUNTING ) )
  573. return false;
  574. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  575. return false;
  576. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  577. return false;
  578. // Can't taunt while charging.
  579. if ( m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  580. return false;
  581. if ( m_Shared.InCond( TF_COND_COMPETITIVE_LOSER ) )
  582. return false;
  583. if ( IsLerpingFOV() )
  584. return false;
  585. // Check for things that prevent taunting
  586. if ( ShouldStopTaunting() )
  587. return false;
  588. // Check to see if we are on the ground.
  589. if ( GetGroundEntity() == NULL && !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  590. return false;
  591. CTFWeaponBase *pActiveWeapon = m_Shared.GetActiveTFWeapon();
  592. if ( pActiveWeapon )
  593. {
  594. if ( !pActiveWeapon->OwnerCanTaunt() )
  595. return false;
  596. // ignore taunt key if one of these if active weapon
  597. if ( pActiveWeapon->GetWeaponID() == TF_WEAPON_PDA_ENGINEER_BUILD
  598. || pActiveWeapon->GetWeaponID() == TF_WEAPON_PDA_ENGINEER_DESTROY )
  599. return false;
  600. }
  601. // can't taunt while carrying an object
  602. if ( m_Shared.IsCarryingObject() )
  603. return false;
  604. // Can't taunt if hooked into a player
  605. if ( m_Shared.InCond( TF_COND_GRAPPLED_TO_PLAYER ) )
  606. return false;
  607. if ( IsPlayerClass( TF_CLASS_SCOUT ) )
  608. {
  609. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  610. {
  611. //Scouts can't drink while they're already phasing.
  612. if ( m_Shared.InCond( TF_COND_ENERGY_BUFF ) || m_Shared.InCond( TF_COND_PHASE ) )
  613. return false;
  614. // Or if their energy drink meter isn't refilled
  615. if ( m_Shared.GetScoutEnergyDrinkMeter() < 100 )
  616. return false;
  617. //They can't drink the default (phase) item while carrying a flag
  618. pActiveWeapon = m_Shared.GetActiveTFWeapon();
  619. if ( pActiveWeapon && pActiveWeapon->GetWeaponID() == TF_WEAPON_LUNCHBOX )
  620. {
  621. CTFLunchBox *pLunchbox = (CTFLunchBox*)pActiveWeapon;
  622. if ( ( pLunchbox->GetLunchboxType() == LUNCHBOX_STANDARD ) || ( pLunchbox->GetLunchboxType() == LUNCHBOX_STANDARD_ROBO ) )
  623. {
  624. if ( !TFGameRules()->IsMannVsMachineMode() && HasItem() )
  625. return false;
  626. }
  627. }
  628. }
  629. }
  630. if ( IsPlayerClass( TF_CLASS_SPY ) )
  631. {
  632. if ( m_Shared.IsStealthed() || m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ||
  633. m_Shared.InCond( TF_COND_DISGUISED ) || m_Shared.InCond( TF_COND_DISGUISING ) )
  634. {
  635. return false;
  636. }
  637. }
  638. return true;
  639. }
  640. // --------------------------------------------------------------------------------------------------- //
  641. // CTFPlayerShared implementation.
  642. // --------------------------------------------------------------------------------------------------- //
  643. CTFPlayerShared::CTFPlayerShared()
  644. {
  645. // If you hit this assert, CONGRATULATIONS! You've added a condition that has gone
  646. // beyond the amount of bits we network for conditions. Take a look at the pattern
  647. // of m_nPlayerCond, m_nPlayerCondEx, m_nPlayerCondEx2, and m_nPlayerCondEx3 to get more bits.
  648. // This pattern is as such to preserve replays.
  649. // Don't forget to add an m_nOldCond* and m_nForceCond*
  650. COMPILE_TIME_ASSERT( TF_COND_LAST < (32 + 32 + 32 + 32) );
  651. m_nPlayerState.Set( TF_STATE_WELCOME );
  652. m_bJumping = false;
  653. m_iAirDash = 0;
  654. m_nAirDucked = 0;
  655. m_flDuckTimer = 0.0f;
  656. m_flStealthNoAttackExpire = 0.0f;
  657. m_flStealthNextChangeTime = 0.0f;
  658. m_iCritMult = 0;
  659. m_flInvisibility = 0.0f;
  660. m_flPrevInvisibility = 0.f;
  661. m_flTmpDamageBonusAmount = 1.0f;
  662. m_bFeignDeathReady = false;
  663. m_fCloakConsumeRate = tf_spy_cloak_consume_rate.GetFloat();
  664. m_fCloakRegenRate = tf_spy_cloak_regen_rate.GetFloat();
  665. m_fEnergyDrinkConsumeRate = tf_scout_energydrink_consume_rate.GetFloat();
  666. m_fEnergyDrinkRegenRate = tf_scout_energydrink_regen_rate.GetFloat();
  667. m_bMotionCloak = false;
  668. m_hStunner = NULL;
  669. m_iStunFlags = 0;
  670. m_hAssist = NULL;
  671. m_bLastDisguisedAsOwnTeam = false;
  672. m_bRageDraining = false;
  673. m_bInUpgradeZone = false;
  674. m_bPhaseFXOn = false;
  675. ResetRageBuffs();
  676. m_iPhaseDamage = 0;
  677. Q_memset(m_pPhaseTrail, 0, sizeof(m_pPhaseTrail));
  678. m_iWeaponKnockbackID = -1;
  679. m_bLoadoutUnavailable = false;
  680. m_nMaskClass = 0;
  681. m_iItemFindBonus = 0;
  682. m_nTeamTeleporterUsed = TEAM_UNASSIGNED;
  683. m_bShieldEquipped = false;
  684. m_bPostShieldCharge = false;
  685. m_iNextMeleeCrit = 0;
  686. m_bParachuteEquipped = false;
  687. m_iDecapitations = m_iOldDecapitations = 0;
  688. m_iOldKillStreak = 0;
  689. m_iOldKillStreakWepSlot = 0;
  690. m_flNextNoiseMakerTime = 0;
  691. m_iSpawnRoomTouchCount = 0;
  692. m_iKillCountSinceLastDeploy = 0;
  693. m_flFirstPrimaryAttack = 0.0f;
  694. #ifdef GAME_DLL
  695. m_flBestOverhealDecayMult = -1;
  696. m_hPeeAttacker = NULL;
  697. m_flHealedPerSecondTimer = -1000;
  698. m_bPulseRadiusHeal = false;
  699. m_flRadiusCurrencyCollectionTime = 0;
  700. m_flRadiusSpyScanTime = 0;
  701. m_flCloakStartTime = -1.0f;
  702. ListenForGameEvent( "player_disconnect" );
  703. #else
  704. m_pWheelEffect = NULL;
  705. m_angVehicleMoveAngles = QAngle( 0.f, 0.f, 0.f );
  706. m_angVehicleMovePitchLast = 0.0f;
  707. // Save Prediction value
  708. m_bPreKartPredictionState = cl_predict->GetBool();
  709. m_hKartParachuteEntity = NULL;
  710. #endif
  711. m_nForceConditions = 0;
  712. m_nForceConditionsEx = 0;
  713. m_nForceConditionsEx2 = 0;
  714. m_nForceConditionsEx3 = 0;
  715. m_flChargeEndTime = -1000;
  716. m_flLastChargeTime = -1000;
  717. m_flLastNoChargeTime = 0;
  718. m_bChargeGlowing = false;
  719. m_bChargeOffSounded = false;
  720. m_bBiteEffectWasApplied = false;
  721. m_flLastMovementStunChange = 0;
  722. m_bStunNeedsFadeOut = false;
  723. m_flChargeMeter = 100;
  724. m_flEnergyDrinkMeter = 0;
  725. m_flHypeMeter = 0;
  726. m_bCarryingObject = false;
  727. m_hCarriedObject = NULL;
  728. m_iStunIndex = -1;
  729. m_flLastNoMovementTime = -1.f;
  730. m_flRuneCharge = 0.f;
  731. #ifdef STAGING_ONLY
  732. m_flSpaceJumpCharge = 100.0f;
  733. m_flSpyTranqBuffDuration = 0.0f;
  734. #endif
  735. m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_NONE;
  736. m_bHasPasstimeBall = false;
  737. m_bIsTargetedForPasstimePass = false;
  738. m_askForBallTime = 0.0f;
  739. // make sure we have all conditions in the list
  740. m_ConditionData.EnsureCount( TF_COND_LAST );
  741. }
  742. void CTFPlayerShared::Init( CTFPlayer *pPlayer )
  743. {
  744. m_pOuter = pPlayer;
  745. m_flNextBurningSound = 0;
  746. m_bArenaFirstBloodBoost = false;
  747. m_iStunAnimState = STUN_ANIM_NONE;
  748. m_iPhaseDamage = 0;
  749. m_iWeaponKnockbackID = -1;
  750. m_hStunner = NULL;
  751. m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_NONE;
  752. m_bHasPasstimeBall = false;
  753. m_bIsTargetedForPasstimePass = false;
  754. m_askForBallTime = 0.0f;
  755. m_bMotionCloak = false;
  756. m_bShieldEquipped = false;
  757. m_bPostShieldCharge = false;
  758. m_iNextMeleeCrit = 0;
  759. m_bParachuteEquipped = false;
  760. m_iDecapitations = m_iOldDecapitations = 0;
  761. m_iOldKillStreak = 0;
  762. m_iOldKillStreakWepSlot = 0;
  763. SetJumping( false );
  764. SetAssist( NULL );
  765. m_flInvulnerabilityRemoveTime = -1;
  766. SetNextMeleeCrit( MELEE_NOCRIT );
  767. Spawn();
  768. }
  769. void CTFPlayerShared::ResetRageBuffs( void )
  770. {
  771. for ( int i = 0; i < kBuffSlot_MAX; i++ )
  772. {
  773. m_RageBuffSlots[i].m_iBuffTypeActive = 0;
  774. m_RageBuffSlots[i].m_iBuffPulseCount = 0;
  775. m_RageBuffSlots[i].m_flNextBuffPulseTime = 0.0f;
  776. }
  777. }
  778. void CTFPlayerShared::Spawn( void )
  779. {
  780. #ifdef GAME_DLL
  781. m_hPeeAttacker = NULL;
  782. if ( m_bCarryingObject )
  783. {
  784. CBaseObject* pObj = GetCarriedObject();
  785. if ( pObj )
  786. {
  787. pObj->DetonateObject();
  788. }
  789. }
  790. m_bCarryingObject = false;
  791. m_hCarriedObject = NULL;
  792. m_flRadiusHealCheckTime = 0;
  793. m_flKingRuneBuffCheckTime = 0.f;
  794. m_bBiteEffectWasApplied = false;
  795. m_flNextRocketPackTime = 0.f;
  796. m_iSpawnRoomTouchCount = 0;
  797. SetRevengeCrits( 0 );
  798. m_PlayerStuns.RemoveAll();
  799. m_iStunIndex = -1;
  800. m_iPasstimeThrowAnimState = PASSTIME_THROW_ANIM_NONE;
  801. m_bHasPasstimeBall = false;
  802. m_bIsTargetedForPasstimePass = false;
  803. m_askForBallTime = 0.0f;
  804. #else
  805. m_bSyncingConditions = false;
  806. #endif
  807. m_bKingRuneBuffActive = false;
  808. // Reset our assist here incase something happens before we get killed
  809. // again that checks this (getting slapped with a fish)
  810. SetAssist( NULL );
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. //-----------------------------------------------------------------------------
  815. template < typename tIntType >
  816. class CConditionVars
  817. {
  818. public:
  819. CConditionVars( tIntType& nPlayerCond, tIntType& nPlayerCondEx, tIntType& nPlayerCondEx2, tIntType& nPlayerCondEx3, ETFCond eCond )
  820. {
  821. if ( eCond >= 96 )
  822. {
  823. Assert( eCond < 96 + 32 );
  824. m_pnCondVar = &nPlayerCondEx3;
  825. m_nCondBit = eCond - 96;
  826. }
  827. else if( eCond >= 64 )
  828. {
  829. Assert( eCond < (64 + 32) );
  830. m_pnCondVar = &nPlayerCondEx2;
  831. m_nCondBit = eCond - 64;
  832. }
  833. else if ( eCond >= 32 )
  834. {
  835. Assert( eCond < (32 + 32) );
  836. m_pnCondVar = &nPlayerCondEx;
  837. m_nCondBit = eCond - 32;
  838. }
  839. else
  840. {
  841. m_pnCondVar = &nPlayerCond;
  842. m_nCondBit = eCond;
  843. }
  844. }
  845. tIntType& CondVar() const
  846. {
  847. return *m_pnCondVar;
  848. }
  849. int CondBit() const
  850. {
  851. return 1 << m_nCondBit;
  852. }
  853. private:
  854. tIntType *m_pnCondVar;
  855. int m_nCondBit;
  856. };
  857. //-----------------------------------------------------------------------------
  858. // Purpose: Add a condition and duration
  859. // duration of PERMANENT_CONDITION means infinite duration
  860. //-----------------------------------------------------------------------------
  861. void CTFPlayerShared::AddCond( ETFCond eCond, float flDuration /* = PERMANENT_CONDITION */, CBaseEntity *pProvider /*= NULL */)
  862. {
  863. Assert( eCond >= 0 && eCond < TF_COND_LAST );
  864. Assert( eCond < m_ConditionData.Count() );
  865. // If we're dead, don't take on any new conditions
  866. if( !m_pOuter || !m_pOuter->IsAlive() )
  867. {
  868. return;
  869. }
  870. #ifdef CLEINT_DLL
  871. if ( m_pOuter->IsDormant() )
  872. {
  873. return;
  874. }
  875. #endif
  876. // sanity check to prevent servers from adding these conditions when they shouldn't
  877. if ( ( eCond == TF_COND_COMPETITIVE_WINNER ) || ( eCond == TF_COND_COMPETITIVE_LOSER ) )
  878. {
  879. if ( TFGameRules() && !TFGameRules()->ShowMatchSummary() )
  880. return;
  881. }
  882. // Which bitfield are we tracking this condition variable in? Which bit within
  883. // that variable will we track it as?
  884. CConditionVars<int> cPlayerCond( m_nPlayerCond.m_Value, m_nPlayerCondEx.m_Value, m_nPlayerCondEx2.m_Value, m_nPlayerCondEx3.m_Value, eCond );
  885. // See if there is an object representation of the condition.
  886. bool bAddedToExternalConditionList = m_ConditionList.Add( eCond, flDuration, m_pOuter, pProvider );
  887. if ( !bAddedToExternalConditionList )
  888. {
  889. // Set the condition bit for this condition.
  890. cPlayerCond.CondVar() |= cPlayerCond.CondBit();
  891. // Flag for gamecode to query
  892. m_ConditionData[eCond].m_bPrevActive = ( m_ConditionData[eCond].m_flExpireTime != 0.f ) ? true : false;
  893. if ( flDuration != PERMANENT_CONDITION )
  894. {
  895. // if our current condition is permanent or we're trying to set a new
  896. // time that's less our current time remaining, use our current time instead
  897. if ( ( m_ConditionData[eCond].m_flExpireTime == PERMANENT_CONDITION ) ||
  898. ( flDuration < m_ConditionData[eCond].m_flExpireTime ) )
  899. {
  900. flDuration = m_ConditionData[eCond].m_flExpireTime;
  901. }
  902. }
  903. m_ConditionData[eCond].m_flExpireTime = flDuration;
  904. m_ConditionData[eCond].m_pProvider = pProvider;
  905. m_ConditionData[ eCond ].m_nPreventedDamageFromCondition = 0;
  906. OnConditionAdded( eCond );
  907. }
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Purpose: Forcibly remove a condition
  911. //-----------------------------------------------------------------------------
  912. void CTFPlayerShared::RemoveCond( ETFCond eCond, bool ignore_duration )
  913. {
  914. Assert( eCond >= 0 && eCond < TF_COND_LAST );
  915. Assert( eCond < m_ConditionData.Count() );
  916. if ( !InCond( eCond ) )
  917. return;
  918. CConditionVars<int> cPlayerCond( m_nPlayerCond.m_Value, m_nPlayerCondEx.m_Value, m_nPlayerCondEx2.m_Value, m_nPlayerCondEx3.m_Value, eCond );
  919. // If this variable is handled by the condition list, abort before doing the
  920. // work for the condition flags.
  921. if ( m_ConditionList.Remove( eCond, ignore_duration ) )
  922. return;
  923. cPlayerCond.CondVar() &= ~cPlayerCond.CondBit();
  924. OnConditionRemoved( eCond );
  925. if ( m_ConditionData[ eCond ].m_nPreventedDamageFromCondition )
  926. {
  927. IGameEvent *pEvent = gameeventmanager->CreateEvent( "damage_prevented" );
  928. if ( pEvent )
  929. {
  930. pEvent->SetInt( "preventor", m_ConditionData[eCond].m_pProvider ? m_ConditionData[eCond].m_pProvider->entindex() : m_pOuter->entindex() );
  931. pEvent->SetInt( "victim", m_pOuter->entindex() );
  932. pEvent->SetInt( "amount", m_ConditionData[ eCond ].m_nPreventedDamageFromCondition );
  933. pEvent->SetInt( "condition", eCond );
  934. gameeventmanager->FireEvent( pEvent, true );
  935. }
  936. m_ConditionData[ eCond ].m_nPreventedDamageFromCondition = 0;
  937. }
  938. m_ConditionData[eCond].m_flExpireTime = 0;
  939. m_ConditionData[eCond].m_pProvider = NULL;
  940. m_ConditionData[eCond].m_bPrevActive = false;
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Purpose:
  944. //-----------------------------------------------------------------------------
  945. bool CTFPlayerShared::InCond( ETFCond eCond ) const
  946. {
  947. Assert( eCond >= 0 && eCond < TF_COND_LAST );
  948. // Old condition system, only used for the first 32 conditions
  949. if ( eCond < 32 && m_ConditionList.InCond( eCond ) )
  950. return true;
  951. CConditionVars<const int> cPlayerCond( m_nPlayerCond.m_Value, m_nPlayerCondEx.m_Value, m_nPlayerCondEx2.m_Value, m_nPlayerCondEx3.m_Value, eCond );
  952. return (cPlayerCond.CondVar() & cPlayerCond.CondBit()) != 0;
  953. }
  954. //-----------------------------------------------------------------------------
  955. // Purpose: Return whether or not we were in this condition before.
  956. //-----------------------------------------------------------------------------
  957. bool CTFPlayerShared::WasInCond( ETFCond eCond ) const
  958. {
  959. // I don't know if this actually works for conditions < 32, because we definitely cannot peak into m_ConditionList (back in time).
  960. // But others think that m_ConditionList is propogated into m_nOldConditions, so just check if you hit the assert. (And then remove the
  961. // assert. And this comment).
  962. Assert( eCond >= 32 && eCond < TF_COND_LAST );
  963. CConditionVars<const int> cPlayerCond( m_nOldConditions, m_nOldConditionsEx, m_nOldConditionsEx2, m_nOldConditionsEx3, eCond );
  964. return (cPlayerCond.CondVar() & cPlayerCond.CondBit()) != 0;
  965. }
  966. //-----------------------------------------------------------------------------
  967. // Purpose: Set a bit to force this condition off and then back on next time we sync bits from the server.
  968. //-----------------------------------------------------------------------------
  969. void CTFPlayerShared::ForceRecondNextSync( ETFCond eCond )
  970. {
  971. // I don't know if this actually works for conditions < 32. We may need to set this bit in m_ConditionList, too.
  972. // Please check if you hit the assert. (And then remove the assert. And this comment).
  973. Assert(eCond >= 32 && eCond < TF_COND_LAST);
  974. CConditionVars<int> playerCond( m_nForceConditions, m_nForceConditionsEx, m_nForceConditionsEx2, m_nForceConditionsEx3, eCond );
  975. playerCond.CondVar() |= playerCond.CondBit();
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose:
  979. //-----------------------------------------------------------------------------
  980. float CTFPlayerShared::GetConditionDuration( ETFCond eCond ) const
  981. {
  982. Assert( eCond >= 0 && eCond < TF_COND_LAST );
  983. Assert( eCond < m_ConditionData.Count() );
  984. if ( InCond( eCond ) )
  985. {
  986. return m_ConditionData[eCond].m_flExpireTime;
  987. }
  988. return 0.0f;
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Purpose: Returns the entity that provided the passed in condition
  992. //-----------------------------------------------------------------------------
  993. CBaseEntity *CTFPlayerShared::GetConditionProvider( ETFCond eCond ) const
  994. {
  995. Assert( eCond >= 0 && eCond < TF_COND_LAST );
  996. Assert( eCond < m_ConditionData.Count() );
  997. CBaseEntity *pProvider = NULL;
  998. if ( InCond( eCond ) )
  999. {
  1000. if ( eCond == TF_COND_CRITBOOSTED )
  1001. {
  1002. pProvider = m_ConditionList.GetProvider( eCond );
  1003. }
  1004. else
  1005. {
  1006. pProvider = m_ConditionData[eCond].m_pProvider;
  1007. }
  1008. }
  1009. return pProvider;
  1010. }
  1011. //-----------------------------------------------------------------------------
  1012. // Purpose: Returns the entity that applied this condition to us - for granting an assist when we die
  1013. //-----------------------------------------------------------------------------
  1014. CBaseEntity *CTFPlayerShared::GetConditionAssistFromVictim( void )
  1015. {
  1016. // We only give an assist to one person. That means this list is order
  1017. // sensitive, so consider how "powerful" an effect is when adding it here.
  1018. static const ETFCond nTrackedConditions[] =
  1019. {
  1020. TF_COND_URINE,
  1021. TF_COND_MAD_MILK,
  1022. TF_COND_MARKEDFORDEATH,
  1023. };
  1024. CBaseEntity *pProvider = NULL;
  1025. for ( int i = 0; i < ARRAYSIZE( nTrackedConditions ); i++ )
  1026. {
  1027. if ( InCond( nTrackedConditions[i] ) )
  1028. {
  1029. pProvider = GetConditionProvider( nTrackedConditions[i] );
  1030. break;
  1031. }
  1032. }
  1033. return pProvider;
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Purpose: Returns the entity that applied this condition to us - for granting an assist when we kill someone
  1037. //-----------------------------------------------------------------------------
  1038. CBaseEntity *CTFPlayerShared::GetConditionAssistFromAttacker( void )
  1039. {
  1040. // We only give an assist to one person. That means this list is order
  1041. // sensitive, so consider how "powerful" an effect is when adding it here.
  1042. static const ETFCond nTrackedConditions[] =
  1043. {
  1044. TF_COND_OFFENSEBUFF, // Highest priority
  1045. TF_COND_DEFENSEBUFF,
  1046. TF_COND_REGENONDAMAGEBUFF,
  1047. TF_COND_NOHEALINGDAMAGEBUFF, // Lowest priority
  1048. };
  1049. CBaseEntity *pProvider = NULL;
  1050. for ( int i = 0; i < ARRAYSIZE( nTrackedConditions ); i++ )
  1051. {
  1052. if ( InCond( nTrackedConditions[i] ) )
  1053. {
  1054. CBaseEntity* pPotentialProvider = GetConditionProvider( nTrackedConditions[i] );
  1055. // Check to make sure we're not providing the condition to ourselves
  1056. if( pPotentialProvider != m_pOuter )
  1057. {
  1058. pProvider = pPotentialProvider;
  1059. break;
  1060. }
  1061. }
  1062. }
  1063. return pProvider;
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. // Purpose:
  1067. //-----------------------------------------------------------------------------
  1068. void CTFPlayerShared::DebugPrintConditions( void )
  1069. {
  1070. #ifndef CLIENT_DLL
  1071. const char *szDll = "Server";
  1072. #else
  1073. const char *szDll = "Client";
  1074. #endif
  1075. Msg( "( %s ) Conditions for player ( %d )\n", szDll, m_pOuter->entindex() );
  1076. int i;
  1077. int iNumFound = 0;
  1078. for ( i=0;i<TF_COND_LAST;i++ )
  1079. {
  1080. if ( InCond( (ETFCond)i ) )
  1081. {
  1082. if ( m_ConditionData[i].m_flExpireTime == PERMANENT_CONDITION )
  1083. {
  1084. Msg( "( %s ) Condition %d - ( permanent cond )\n", szDll, i );
  1085. }
  1086. else
  1087. {
  1088. Msg( "( %s ) Condition %d - ( %.1f left )\n", szDll, i, m_ConditionData[i].m_flExpireTime );
  1089. }
  1090. iNumFound++;
  1091. }
  1092. }
  1093. if ( iNumFound == 0 )
  1094. {
  1095. Msg( "( %s ) No active conditions\n", szDll );
  1096. }
  1097. }
  1098. void CTFPlayerShared::InstantlySniperUnzoom( void )
  1099. {
  1100. // Unzoom if we are a sniper zoomed!
  1101. if ( m_pOuter->GetPlayerClass()->GetClassIndex() == TF_CLASS_SNIPER )
  1102. {
  1103. CTFWeaponBase *pWpn = m_pOuter->GetActiveTFWeapon();
  1104. if ( pWpn && WeaponID_IsSniperRifle( pWpn->GetWeaponID() ) )
  1105. {
  1106. CTFSniperRifle *pRifle = static_cast<CTFSniperRifle*>( pWpn );
  1107. if ( pRifle->IsZoomed() )
  1108. {
  1109. // Let the rifle clean up conditions and state
  1110. pRifle->ToggleZoom();
  1111. // Slam the FOV right now
  1112. m_pOuter->SetFOV( m_pOuter, 0, 0.0f );
  1113. }
  1114. }
  1115. }
  1116. }
  1117. #ifdef CLIENT_DLL
  1118. //-----------------------------------------------------------------------------
  1119. // Purpose:
  1120. //-----------------------------------------------------------------------------
  1121. void CTFPlayerShared::OnPreDataChanged( void )
  1122. {
  1123. m_ConditionList.OnPreDataChanged();
  1124. m_nOldConditions = m_nPlayerCond;
  1125. m_nOldConditionsEx = m_nPlayerCondEx;
  1126. m_nOldConditionsEx2 = m_nPlayerCondEx2;
  1127. m_nOldConditionsEx3 = m_nPlayerCondEx3;
  1128. m_nOldDisguiseClass = GetDisguiseClass();
  1129. m_nOldDisguiseTeam = GetDisguiseTeam();
  1130. m_iOldMovementStunParity = m_iMovementStunParity;
  1131. InvisibilityThink();
  1132. ConditionThink();
  1133. }
  1134. //-----------------------------------------------------------------------------
  1135. // Purpose:
  1136. //-----------------------------------------------------------------------------
  1137. void CTFPlayerShared::OnDataChanged( void )
  1138. {
  1139. m_ConditionList.OnDataChanged( m_pOuter );
  1140. if ( m_iOldMovementStunParity != m_iMovementStunParity )
  1141. {
  1142. m_flStunFade = gpGlobals->curtime + m_flMovementStunTime;
  1143. m_flStunEnd = m_flStunFade;
  1144. if ( IsControlStunned() && (m_iStunAnimState == STUN_ANIM_NONE) )
  1145. {
  1146. m_flStunEnd += CONTROL_STUN_ANIM_TIME;
  1147. }
  1148. UpdateLegacyStunSystem();
  1149. }
  1150. // Update conditions from last network change
  1151. SyncConditions( m_nOldConditions, m_nPlayerCond, m_nForceConditions, 0 );
  1152. SyncConditions( m_nOldConditionsEx, m_nPlayerCondEx, m_nForceConditionsEx, 32 );
  1153. SyncConditions( m_nOldConditionsEx2, m_nPlayerCondEx2, m_nForceConditionsEx2, 64 );
  1154. SyncConditions( m_nOldConditionsEx3, m_nPlayerCondEx3, m_nForceConditionsEx3, 96 );
  1155. // Make sure these items are present
  1156. m_nPlayerCond |= m_nForceConditions;
  1157. m_nPlayerCondEx |= m_nForceConditionsEx;
  1158. m_nPlayerCondEx2 |= m_nForceConditionsEx2;
  1159. m_nPlayerCondEx3 |= m_nForceConditionsEx3;
  1160. // Clear our force bits now that we've used them.
  1161. m_nForceConditions = 0;
  1162. m_nForceConditionsEx = 0;
  1163. m_nForceConditionsEx2 = 0;
  1164. m_nForceConditionsEx3 = 0;
  1165. if ( m_nOldDisguiseClass != GetDisguiseClass() || m_nOldDisguiseTeam != GetDisguiseTeam() )
  1166. {
  1167. OnDisguiseChanged();
  1168. }
  1169. if ( m_hDisguiseWeapon )
  1170. {
  1171. m_hDisguiseWeapon->UpdateVisibility();
  1172. m_hDisguiseWeapon->UpdateParticleSystems();
  1173. }
  1174. if ( ( IsLoser() || InCond( TF_COND_COMPETITIVE_LOSER ) ) && GetActiveTFWeapon() && !GetActiveTFWeapon()->IsEffectActive( EF_NODRAW ) )
  1175. {
  1176. GetActiveTFWeapon()->SetWeaponVisible( false );
  1177. }
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: check the newly networked conditions for changes
  1181. //-----------------------------------------------------------------------------
  1182. void CTFPlayerShared::SyncConditions( int nPreviousConditions, int nNewConditions, int nForceConditions, int nBaseCondBit )
  1183. {
  1184. if ( nPreviousConditions == nNewConditions )
  1185. return;
  1186. int nCondChanged = nNewConditions ^ nPreviousConditions;
  1187. int nCondAdded = nCondChanged & nNewConditions;
  1188. int nCondRemoved = nCondChanged & nPreviousConditions;
  1189. m_bSyncingConditions = true;
  1190. for ( int i=0;i<32;i++ )
  1191. {
  1192. const int testBit = 1<<i;
  1193. if ( nForceConditions & testBit )
  1194. {
  1195. if ( nPreviousConditions & testBit )
  1196. {
  1197. OnConditionRemoved((ETFCond)(nBaseCondBit + i));
  1198. }
  1199. OnConditionAdded((ETFCond)(nBaseCondBit + i));
  1200. }
  1201. else
  1202. {
  1203. if ( nCondAdded & testBit )
  1204. {
  1205. OnConditionAdded( (ETFCond)(nBaseCondBit + i) );
  1206. }
  1207. else if ( nCondRemoved & testBit )
  1208. {
  1209. OnConditionRemoved( (ETFCond)(nBaseCondBit + i) );
  1210. }
  1211. }
  1212. }
  1213. m_bSyncingConditions = false;
  1214. }
  1215. #endif // CLIENT_DLL
  1216. //-----------------------------------------------------------------------------
  1217. // Purpose: Remove any conditions affecting players
  1218. //-----------------------------------------------------------------------------
  1219. void CTFPlayerShared::RemoveAllCond()
  1220. {
  1221. m_ConditionList.RemoveAll();
  1222. int i;
  1223. for ( i=0;i<TF_COND_LAST;i++ )
  1224. {
  1225. if ( InCond( (ETFCond)i ) )
  1226. {
  1227. RemoveCond( (ETFCond)i );
  1228. }
  1229. }
  1230. // Now remove all the rest
  1231. m_nPlayerCond = 0;
  1232. m_nPlayerCondEx = 0;
  1233. m_nPlayerCondEx2 = 0;
  1234. m_nPlayerCondEx3 = 0;
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Purpose: Called on both client and server. Server when we add the bit,
  1238. // and client when it receives the new cond bits and finds one added
  1239. //-----------------------------------------------------------------------------
  1240. void CTFPlayerShared::OnConditionAdded( ETFCond eCond )
  1241. {
  1242. switch( eCond )
  1243. {
  1244. case TF_COND_ZOOMED:
  1245. OnAddZoomed();
  1246. break;
  1247. case TF_COND_HEALTH_BUFF:
  1248. #ifdef GAME_DLL
  1249. m_flHealFraction = 0;
  1250. m_flDisguiseHealFraction = 0;
  1251. m_flHealedPerSecondTimer = gpGlobals->curtime + 1.0f;
  1252. #endif
  1253. break;
  1254. case TF_COND_HEALTH_OVERHEALED:
  1255. OnAddOverhealed();
  1256. break;
  1257. case TF_COND_FEIGN_DEATH:
  1258. OnAddFeignDeath();
  1259. break;
  1260. case TF_COND_STEALTHED:
  1261. case TF_COND_STEALTHED_USER_BUFF:
  1262. OnAddStealthed();
  1263. break;
  1264. case TF_COND_INVULNERABLE:
  1265. case TF_COND_INVULNERABLE_USER_BUFF:
  1266. case TF_COND_INVULNERABLE_CARD_EFFECT:
  1267. OnAddInvulnerable();
  1268. break;
  1269. case TF_COND_TELEPORTED:
  1270. OnAddTeleported();
  1271. break;
  1272. case TF_COND_BURNING:
  1273. OnAddBurning();
  1274. break;
  1275. case TF_COND_CRITBOOSTED:
  1276. Assert( !"TF_COND_CRITBOOSTED should be handled by the condition list!" );
  1277. break;
  1278. case TF_COND_CRITBOOSTED_DEMO_CHARGE:
  1279. OnAddDemoCharge();
  1280. break;
  1281. // First blood falls through on purpose.
  1282. case TF_COND_CRITBOOSTED_FIRST_BLOOD:
  1283. SetFirstBloodBoosted( true );
  1284. case TF_COND_CRITBOOSTED_PUMPKIN:
  1285. case TF_COND_CRITBOOSTED_USER_BUFF:
  1286. case TF_COND_CRITBOOSTED_BONUS_TIME:
  1287. case TF_COND_CRITBOOSTED_CTF_CAPTURE:
  1288. case TF_COND_CRITBOOSTED_ON_KILL:
  1289. case TF_COND_CRITBOOSTED_RAGE_BUFF:
  1290. case TF_COND_SNIPERCHARGE_RAGE_BUFF:
  1291. case TF_COND_CRITBOOSTED_CARD_EFFECT:
  1292. case TF_COND_CRITBOOSTED_RUNE_TEMP:
  1293. OnAddCritBoost();
  1294. break;
  1295. case TF_COND_SODAPOPPER_HYPE:
  1296. OnAddSodaPopperHype();
  1297. break;
  1298. case TF_COND_DISGUISING:
  1299. OnAddDisguising();
  1300. break;
  1301. case TF_COND_DISGUISED:
  1302. OnAddDisguised();
  1303. break;
  1304. case TF_COND_URINE:
  1305. OnAddUrine();
  1306. break;
  1307. case TF_COND_MARKEDFORDEATH:
  1308. OnAddMarkedForDeath();
  1309. break;
  1310. case TF_COND_BLEEDING:
  1311. OnAddBleeding();
  1312. break;
  1313. case TF_COND_TAUNTING:
  1314. OnAddTaunting();
  1315. break;
  1316. case TF_COND_STUNNED:
  1317. OnAddStunned();
  1318. break;
  1319. case TF_COND_PHASE:
  1320. OnAddPhase();
  1321. break;
  1322. case TF_COND_OFFENSEBUFF:
  1323. OnAddOffenseBuff();
  1324. break;
  1325. case TF_COND_DEFENSEBUFF:
  1326. case TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK:
  1327. case TF_COND_DEFENSEBUFF_HIGH:
  1328. OnAddDefenseBuff();
  1329. break;
  1330. case TF_COND_REGENONDAMAGEBUFF:
  1331. OnAddOffenseHealthRegenBuff();
  1332. break;
  1333. case TF_COND_NOHEALINGDAMAGEBUFF:
  1334. OnAddNoHealingDamageBuff();
  1335. break;
  1336. case TF_COND_SHIELD_CHARGE:
  1337. OnAddShieldCharge();
  1338. break;
  1339. case TF_COND_DEMO_BUFF:
  1340. OnAddDemoBuff();
  1341. break;
  1342. case TF_COND_ENERGY_BUFF:
  1343. OnAddEnergyDrinkBuff();
  1344. break;
  1345. case TF_COND_RADIUSHEAL:
  1346. OnAddRadiusHeal();
  1347. break;
  1348. case TF_COND_MEGAHEAL:
  1349. OnAddMegaHeal();
  1350. break;
  1351. case TF_COND_MAD_MILK:
  1352. OnAddMadMilk();
  1353. break;
  1354. case TF_COND_SPEED_BOOST: OnAddSpeedBoost( false ); break;
  1355. #ifdef STAGING_ONLY //STAGING_ENGY
  1356. case TF_COND_NO_COMBAT_SPEED_BOOST: OnAddSpeedBoost( true ); break;
  1357. #endif
  1358. case TF_COND_SAPPED:
  1359. OnAddSapped();
  1360. break;
  1361. case TF_COND_REPROGRAMMED:
  1362. OnAddReprogrammed();
  1363. break;
  1364. case TF_COND_PASSTIME_PENALTY_DEBUFF:
  1365. case TF_COND_MARKEDFORDEATH_SILENT:
  1366. OnAddMarkedForDeathSilent();
  1367. break;
  1368. case TF_COND_DISGUISED_AS_DISPENSER:
  1369. OnAddDisguisedAsDispenser();
  1370. break;
  1371. case TF_COND_HALLOWEEN_BOMB_HEAD:
  1372. OnAddHalloweenBombHead();
  1373. break;
  1374. case TF_COND_HALLOWEEN_THRILLER:
  1375. OnAddHalloweenThriller();
  1376. break;
  1377. case TF_COND_RADIUSHEAL_ON_DAMAGE:
  1378. OnAddRadiusHealOnDamage();
  1379. break;
  1380. case TF_COND_MEDIGUN_UBER_BULLET_RESIST:
  1381. OnAddMedEffectUberBulletResist();
  1382. break;
  1383. case TF_COND_MEDIGUN_UBER_BLAST_RESIST:
  1384. OnAddMedEffectUberBlastResist();
  1385. break;
  1386. case TF_COND_MEDIGUN_UBER_FIRE_RESIST:
  1387. OnAddMedEffectUberFireResist();
  1388. break;
  1389. case TF_COND_MEDIGUN_SMALL_BULLET_RESIST:
  1390. OnAddMedEffectSmallBulletResist();
  1391. break;
  1392. case TF_COND_MEDIGUN_SMALL_BLAST_RESIST:
  1393. OnAddMedEffectSmallBlastResist();
  1394. break;
  1395. case TF_COND_MEDIGUN_SMALL_FIRE_RESIST:
  1396. OnAddMedEffectSmallFireResist();
  1397. break;
  1398. case TF_COND_STEALTHED_USER_BUFF_FADING:
  1399. OnAddStealthedUserBuffFade();
  1400. break;
  1401. case TF_COND_BULLET_IMMUNE:
  1402. OnAddBulletImmune();
  1403. break;
  1404. case TF_COND_BLAST_IMMUNE:
  1405. OnAddBlastImmune();
  1406. break;
  1407. case TF_COND_FIRE_IMMUNE:
  1408. OnAddFireImmune();
  1409. break;
  1410. case TF_COND_MVM_BOT_STUN_RADIOWAVE:
  1411. OnAddMVMBotRadiowave();
  1412. break;
  1413. case TF_COND_HALLOWEEN_SPEED_BOOST:
  1414. OnAddHalloweenSpeedBoost();
  1415. break;
  1416. case TF_COND_HALLOWEEN_QUICK_HEAL:
  1417. OnAddHalloweenQuickHeal();
  1418. break;
  1419. case TF_COND_HALLOWEEN_GIANT:
  1420. OnAddHalloweenGiant();
  1421. break;
  1422. case TF_COND_HALLOWEEN_TINY:
  1423. OnAddHalloweenTiny();
  1424. break;
  1425. case TF_COND_HALLOWEEN_GHOST_MODE:
  1426. OnAddHalloweenGhostMode();
  1427. break;
  1428. case TF_COND_PARACHUTE_DEPLOYED:
  1429. OnAddCondParachute();
  1430. break;
  1431. case TF_COND_HALLOWEEN_KART_DASH:
  1432. OnAddHalloweenKartDash();
  1433. break;
  1434. case TF_COND_HALLOWEEN_KART:
  1435. OnAddHalloweenKart();
  1436. break;
  1437. case TF_COND_BALLOON_HEAD:
  1438. OnAddBalloonHead();
  1439. break;
  1440. case TF_COND_MELEE_ONLY:
  1441. OnAddMeleeOnly();
  1442. break;
  1443. case TF_COND_SWIMMING_CURSE:
  1444. OnAddSwimmingCurse();
  1445. break;
  1446. case TF_COND_HALLOWEEN_KART_CAGE:
  1447. OnAddHalloweenKartCage();
  1448. break;
  1449. case TF_COND_RUNE_RESIST:
  1450. OnAddRuneResist();
  1451. break;
  1452. case TF_COND_GRAPPLINGHOOK_LATCHED:
  1453. OnAddGrapplingHookLatched();
  1454. break;
  1455. case TF_COND_PASSTIME_INTERCEPTION:
  1456. OnAddPasstimeInterception();
  1457. break;
  1458. case TF_COND_RUNE_PLAGUE:
  1459. OnAddRunePlague();
  1460. break;
  1461. case TF_COND_PLAGUE:
  1462. OnAddPlague();
  1463. break;
  1464. case TF_COND_PURGATORY:
  1465. OnAddInPurgatory();
  1466. break;
  1467. case TF_COND_COMPETITIVE_WINNER:
  1468. OnAddCompetitiveWinner();
  1469. break;
  1470. case TF_COND_COMPETITIVE_LOSER:
  1471. OnAddCompetitiveLoser();
  1472. break;
  1473. #ifdef STAGING_ONLY
  1474. case TF_COND_SPY_CLASS_STEAL:
  1475. OnAddCondSpyClassSteal();
  1476. break;
  1477. case TF_COND_TRANQ_MARKED:
  1478. OnAddTranqMark();
  1479. break;
  1480. /*
  1481. case TF_COND_SPACE_GRAVITY:
  1482. OnAddSpaceGravity();
  1483. break;
  1484. case TF_COND_SELF_CONC:
  1485. OnAddSelfConc();
  1486. break;
  1487. */
  1488. case TF_COND_ROCKETPACK:
  1489. OnAddRocketPack();
  1490. break;
  1491. case TF_COND_STEALTHED_PHASE:
  1492. OnAddStealthedPhase();
  1493. break;
  1494. case TF_COND_CLIP_OVERLOAD:
  1495. OnAddClipOverload();
  1496. break;
  1497. case TF_COND_KING_BUFFED:
  1498. OnAddKingBuff();
  1499. break;
  1500. #endif // STAGING_ONLY
  1501. default:
  1502. break;
  1503. }
  1504. }
  1505. //-----------------------------------------------------------------------------
  1506. // Purpose: Called on both client and server. Server when we remove the bit,
  1507. // and client when it receives the new cond bits and finds one removed
  1508. //-----------------------------------------------------------------------------
  1509. void CTFPlayerShared::OnConditionRemoved( ETFCond eCond )
  1510. {
  1511. switch( eCond )
  1512. {
  1513. case TF_COND_ZOOMED:
  1514. OnRemoveZoomed();
  1515. break;
  1516. case TF_COND_BURNING:
  1517. OnRemoveBurning();
  1518. break;
  1519. case TF_COND_CRITBOOSTED:
  1520. Assert( !"TF_COND_CRITBOOSTED should be handled by the condition list!" );
  1521. break;
  1522. case TF_COND_CRITBOOSTED_DEMO_CHARGE:
  1523. OnRemoveDemoCharge();
  1524. break;
  1525. // First blood falls through on purpose.
  1526. case TF_COND_CRITBOOSTED_FIRST_BLOOD:
  1527. SetFirstBloodBoosted( false );
  1528. case TF_COND_CRITBOOSTED_PUMPKIN:
  1529. case TF_COND_CRITBOOSTED_USER_BUFF:
  1530. case TF_COND_CRITBOOSTED_BONUS_TIME:
  1531. case TF_COND_CRITBOOSTED_CTF_CAPTURE:
  1532. case TF_COND_CRITBOOSTED_ON_KILL:
  1533. case TF_COND_CRITBOOSTED_RAGE_BUFF:
  1534. case TF_COND_SNIPERCHARGE_RAGE_BUFF:
  1535. case TF_COND_CRITBOOSTED_CARD_EFFECT:
  1536. case TF_COND_CRITBOOSTED_RUNE_TEMP:
  1537. OnRemoveCritBoost();
  1538. break;
  1539. case TF_COND_SODAPOPPER_HYPE:
  1540. OnRemoveSodaPopperHype();
  1541. break;
  1542. case TF_COND_TMPDAMAGEBONUS:
  1543. OnRemoveTmpDamageBonus();
  1544. break;
  1545. case TF_COND_HEALTH_BUFF:
  1546. #ifdef GAME_DLL
  1547. m_flHealFraction = 0;
  1548. m_flDisguiseHealFraction = 0;
  1549. #endif
  1550. break;
  1551. case TF_COND_HEALTH_OVERHEALED:
  1552. OnRemoveOverhealed();
  1553. break;
  1554. case TF_COND_FEIGN_DEATH:
  1555. OnRemoveFeignDeath();
  1556. break;
  1557. case TF_COND_STEALTHED:
  1558. case TF_COND_STEALTHED_USER_BUFF:
  1559. OnRemoveStealthed();
  1560. break;
  1561. case TF_COND_DISGUISED:
  1562. OnRemoveDisguised();
  1563. break;
  1564. case TF_COND_DISGUISING:
  1565. OnRemoveDisguising();
  1566. break;
  1567. case TF_COND_INVULNERABLE:
  1568. case TF_COND_INVULNERABLE_USER_BUFF:
  1569. case TF_COND_INVULNERABLE_CARD_EFFECT:
  1570. OnRemoveInvulnerable();
  1571. break;
  1572. case TF_COND_TELEPORTED:
  1573. OnRemoveTeleported();
  1574. break;
  1575. case TF_COND_STUNNED:
  1576. OnRemoveStunned();
  1577. break;
  1578. case TF_COND_PHASE:
  1579. OnRemovePhase();
  1580. break;
  1581. case TF_COND_URINE:
  1582. OnRemoveUrine();
  1583. break;
  1584. case TF_COND_MARKEDFORDEATH:
  1585. OnRemoveMarkedForDeath();
  1586. break;
  1587. case TF_COND_BLEEDING:
  1588. OnRemoveBleeding();
  1589. break;
  1590. case TF_COND_INVULNERABLE_WEARINGOFF:
  1591. OnRemoveInvulnerableWearingOff();
  1592. break;
  1593. case TF_COND_OFFENSEBUFF:
  1594. OnRemoveOffenseBuff();
  1595. break;
  1596. case TF_COND_DEFENSEBUFF:
  1597. case TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK:
  1598. case TF_COND_DEFENSEBUFF_HIGH:
  1599. OnRemoveDefenseBuff();
  1600. break;
  1601. case TF_COND_REGENONDAMAGEBUFF:
  1602. OnRemoveOffenseHealthRegenBuff();
  1603. break;
  1604. case TF_COND_NOHEALINGDAMAGEBUFF:
  1605. OnRemoveNoHealingDamageBuff();
  1606. break;
  1607. case TF_COND_SHIELD_CHARGE:
  1608. OnRemoveShieldCharge();
  1609. break;
  1610. case TF_COND_DEMO_BUFF:
  1611. OnRemoveDemoBuff();
  1612. break;
  1613. case TF_COND_ENERGY_BUFF:
  1614. OnRemoveEnergyDrinkBuff();
  1615. break;
  1616. case TF_COND_RADIUSHEAL:
  1617. OnRemoveRadiusHeal();
  1618. break;
  1619. case TF_COND_MEGAHEAL:
  1620. OnRemoveMegaHeal();
  1621. break;
  1622. case TF_COND_MAD_MILK:
  1623. OnRemoveMadMilk();
  1624. break;
  1625. case TF_COND_TAUNTING:
  1626. OnRemoveTaunting();
  1627. break;
  1628. case TF_COND_SPEED_BOOST: OnRemoveSpeedBoost( false ); break;
  1629. #ifdef STAGING_ONLY
  1630. case TF_COND_NO_COMBAT_SPEED_BOOST: OnRemoveSpeedBoost( true ); break;
  1631. #endif
  1632. case TF_COND_SAPPED:
  1633. OnRemoveSapped();
  1634. break;
  1635. case TF_COND_REPROGRAMMED:
  1636. OnRemoveReprogrammed();
  1637. break;
  1638. case TF_COND_PASSTIME_PENALTY_DEBUFF:
  1639. case TF_COND_MARKEDFORDEATH_SILENT:
  1640. OnRemoveMarkedForDeathSilent();
  1641. break;
  1642. case TF_COND_DISGUISED_AS_DISPENSER:
  1643. OnRemoveDisguisedAsDispenser();
  1644. break;
  1645. case TF_COND_HALLOWEEN_BOMB_HEAD:
  1646. OnRemoveHalloweenBombHead();
  1647. break;
  1648. case TF_COND_HALLOWEEN_THRILLER:
  1649. OnRemoveHalloweenThriller();
  1650. break;
  1651. case TF_COND_RADIUSHEAL_ON_DAMAGE:
  1652. OnRemoveRadiusHealOnDamage();
  1653. break;
  1654. case TF_COND_MEDIGUN_UBER_BULLET_RESIST:
  1655. OnRemoveMedEffectUberBulletResist();
  1656. break;
  1657. case TF_COND_MEDIGUN_UBER_BLAST_RESIST:
  1658. OnRemoveMedEffectUberBlastResist();
  1659. break;
  1660. case TF_COND_MEDIGUN_UBER_FIRE_RESIST:
  1661. OnRemoveMedEffectUberFireResist();
  1662. break;
  1663. case TF_COND_MEDIGUN_SMALL_BULLET_RESIST:
  1664. OnRemoveMedEffectSmallBulletResist();
  1665. break;
  1666. case TF_COND_MEDIGUN_SMALL_BLAST_RESIST:
  1667. OnRemoveMedEffectSmallBlastResist();
  1668. break;
  1669. case TF_COND_MEDIGUN_SMALL_FIRE_RESIST:
  1670. OnRemoveMedEffectSmallFireResist();
  1671. break;
  1672. case TF_COND_STEALTHED_USER_BUFF_FADING:
  1673. OnRemoveStealthedUserBuffFade();
  1674. break;
  1675. case TF_COND_BULLET_IMMUNE:
  1676. OnRemoveBulletImmune();
  1677. break;
  1678. case TF_COND_BLAST_IMMUNE:
  1679. OnRemoveBlastImmune();
  1680. break;
  1681. case TF_COND_FIRE_IMMUNE:
  1682. OnRemoveFireImmune();
  1683. break;
  1684. case TF_COND_MVM_BOT_STUN_RADIOWAVE:
  1685. OnRemoveMVMBotRadiowave();
  1686. break;
  1687. case TF_COND_HALLOWEEN_SPEED_BOOST:
  1688. OnRemoveHalloweenSpeedBoost();
  1689. break;
  1690. case TF_COND_HALLOWEEN_QUICK_HEAL:
  1691. OnRemoveHalloweenQuickHeal();
  1692. break;
  1693. case TF_COND_HALLOWEEN_GIANT:
  1694. OnRemoveHalloweenGiant();
  1695. break;
  1696. case TF_COND_HALLOWEEN_TINY:
  1697. OnRemoveHalloweenTiny();
  1698. break;
  1699. case TF_COND_HALLOWEEN_GHOST_MODE:
  1700. OnRemoveHalloweenGhostMode();
  1701. break;
  1702. case TF_COND_PARACHUTE_DEPLOYED:
  1703. OnRemoveCondParachute();
  1704. break;
  1705. case TF_COND_HALLOWEEN_KART_DASH:
  1706. OnRemoveHalloweenKartDash();
  1707. break;
  1708. case TF_COND_HALLOWEEN_KART:
  1709. OnRemoveHalloweenKart();
  1710. break;
  1711. case TF_COND_BALLOON_HEAD:
  1712. OnRemoveBalloonHead();
  1713. break;
  1714. case TF_COND_MELEE_ONLY:
  1715. OnRemoveMeleeOnly();
  1716. break;
  1717. case TF_COND_SWIMMING_CURSE:
  1718. OnRemoveSwimmingCurse();
  1719. case TF_COND_HALLOWEEN_KART_CAGE:
  1720. OnRemoveHalloweenKartCage();
  1721. break;
  1722. case TF_COND_RUNE_RESIST:
  1723. OnRemoveRuneResist();
  1724. break;
  1725. case TF_COND_GRAPPLINGHOOK_LATCHED:
  1726. OnRemoveGrapplingHookLatched();
  1727. break;
  1728. case TF_COND_PASSTIME_INTERCEPTION:
  1729. OnRemovePasstimeInterception();
  1730. break;
  1731. case TF_COND_RUNE_PLAGUE:
  1732. OnRemoveRunePlague();
  1733. break;
  1734. case TF_COND_PLAGUE:
  1735. OnRemovePlague();
  1736. break;
  1737. case TF_COND_PURGATORY:
  1738. OnRemoveInPurgatory();
  1739. break;
  1740. case TF_COND_RUNE_KING:
  1741. OnRemoveRuneKing();
  1742. break;
  1743. case TF_COND_KING_BUFFED:
  1744. OnRemoveKingBuff();
  1745. break;
  1746. case TF_COND_RUNE_SUPERNOVA:
  1747. OnRemoveRuneSupernova();
  1748. break;
  1749. case TF_COND_COMPETITIVE_WINNER:
  1750. OnRemoveCompetitiveWinner();
  1751. break;
  1752. case TF_COND_COMPETITIVE_LOSER:
  1753. OnRemoveCompetitiveLoser();
  1754. break;
  1755. #ifdef STAGING_ONLY
  1756. case TF_COND_SPY_CLASS_STEAL:
  1757. OnRemoveCondSpyClassSteal();
  1758. break;
  1759. case TF_COND_TRANQ_MARKED:
  1760. OnRemoveTranqMark();
  1761. break;
  1762. /*
  1763. case TF_COND_SPACE_GRAVITY:
  1764. OnRemoveSpaceGravity();
  1765. break;
  1766. case TF_COND_SELF_CONC:
  1767. OnRemoveSelfConc();
  1768. break;
  1769. */
  1770. case TF_COND_ROCKETPACK:
  1771. OnRemoveRocketPack();
  1772. break;
  1773. case TF_COND_STEALTHED_PHASE:
  1774. OnRemoveStealthedPhase();
  1775. break;
  1776. case TF_COND_CLIP_OVERLOAD:
  1777. OnRemoveClipOverload();
  1778. break;
  1779. #endif // STAGING_ONLY
  1780. default:
  1781. break;
  1782. }
  1783. }
  1784. //-----------------------------------------------------------------------------
  1785. // Purpose: Returns the overheal bonus the specified healer is capable of buffing to
  1786. //-----------------------------------------------------------------------------
  1787. int CTFPlayerShared::GetMaxBuffedHealth( bool bIgnoreAttributes /*= false*/, bool bIgnoreHealthOverMax /*= false*/ )
  1788. {
  1789. // Find the healer we have who's providing the most overheal
  1790. float flBoostMax = m_pOuter->GetMaxHealthForBuffing() * tf_max_health_boost.GetFloat();
  1791. #ifdef GAME_DLL
  1792. if ( !bIgnoreAttributes )
  1793. {
  1794. for ( int i = 0; i < m_aHealers.Count(); i++ )
  1795. {
  1796. float flOverheal = m_pOuter->GetMaxHealthForBuffing() * m_aHealers[i].flOverhealBonus;
  1797. if ( flOverheal > flBoostMax )
  1798. {
  1799. flBoostMax = flOverheal;
  1800. }
  1801. }
  1802. }
  1803. #endif
  1804. int iRoundDown = floor( flBoostMax / 5 );
  1805. iRoundDown = iRoundDown * 5;
  1806. if ( !bIgnoreHealthOverMax )
  1807. {
  1808. // Don't allow overheal total to be less than the buffable + unbuffable max health or the current health
  1809. int nBoostMin = MAX( m_pOuter->GetMaxHealth(), m_pOuter->GetHealth() );
  1810. if ( iRoundDown < nBoostMin )
  1811. {
  1812. iRoundDown = nBoostMin;
  1813. }
  1814. }
  1815. return iRoundDown;
  1816. }
  1817. //-----------------------------------------------------------------------------
  1818. // Purpose:
  1819. //-----------------------------------------------------------------------------
  1820. int CTFPlayerShared::GetDisguiseMaxBuffedHealth( bool bIgnoreAttributes /*= false*/, bool bIgnoreHealthOverMax /*= false*/ )
  1821. {
  1822. // Find the healer we have who's providing the most overheal
  1823. float flBoostMax = GetDisguiseMaxHealth() * tf_max_health_boost.GetFloat();
  1824. #ifdef GAME_DLL
  1825. if ( !bIgnoreAttributes )
  1826. {
  1827. for ( int i = 0; i < m_aHealers.Count(); i++ )
  1828. {
  1829. float flOverheal = GetDisguiseMaxHealth() * m_aHealers[i].flOverhealBonus;
  1830. if ( flOverheal > flBoostMax )
  1831. {
  1832. flBoostMax = flOverheal;
  1833. }
  1834. }
  1835. }
  1836. #endif
  1837. int iRoundDown = floor( flBoostMax / 5 );
  1838. iRoundDown = iRoundDown * 5;
  1839. if ( !bIgnoreHealthOverMax )
  1840. {
  1841. // Don't allow overheal total to be less than the buffable + unbuffable max health or the current health
  1842. int nBoostMin = MAX(GetDisguiseMaxHealth(), GetDisguiseHealth() );
  1843. if ( iRoundDown < nBoostMin )
  1844. {
  1845. iRoundDown = nBoostMin;
  1846. }
  1847. }
  1848. return iRoundDown;
  1849. }
  1850. //-----------------------------------------------------------------------------
  1851. bool ShouldRemoveConditionOnTimeout( ETFCond eCond )
  1852. {
  1853. switch( eCond )
  1854. {
  1855. case TF_COND_HALLOWEEN_GHOST_MODE:
  1856. case TF_COND_HALLOWEEN_IN_HELL:
  1857. case TF_COND_HALLOWEEN_KART:
  1858. break;
  1859. return !TFGameRules()->ArePlayersInHell();
  1860. }
  1861. return true;
  1862. }
  1863. //-----------------------------------------------------------------------------
  1864. // Purpose: Runs SERVER SIDE only Condition Think
  1865. // If a player needs something to be updated no matter what do it here (invul, etc).
  1866. //-----------------------------------------------------------------------------
  1867. void CTFPlayerShared::ConditionGameRulesThink( void )
  1868. {
  1869. #ifdef GAME_DLL
  1870. m_ConditionList.ServerThink();
  1871. if ( m_flNextCritUpdate < gpGlobals->curtime )
  1872. {
  1873. UpdateCritMult();
  1874. m_flNextCritUpdate = gpGlobals->curtime + 0.5;
  1875. }
  1876. for ( int i=0; i < TF_COND_LAST; ++i )
  1877. {
  1878. // if we're in this condition and it's not already being handled by the condition list
  1879. if ( InCond( (ETFCond)i ) && ((i >= 32) || !m_ConditionList.InCond( (ETFCond)i )) )
  1880. {
  1881. // Ignore permanent conditions
  1882. if ( m_ConditionData[i].m_flExpireTime != PERMANENT_CONDITION )
  1883. {
  1884. float flReduction = gpGlobals->frametime;
  1885. // If we're being healed, we reduce bad conditions faster
  1886. if ( ConditionExpiresFast( (ETFCond)i) && m_aHealers.Count() > 0 )
  1887. {
  1888. if ( i == TF_COND_URINE )
  1889. {
  1890. flReduction += (m_aHealers.Count() * flReduction);
  1891. }
  1892. else
  1893. {
  1894. flReduction += (m_aHealers.Count() * flReduction * 4);
  1895. }
  1896. }
  1897. m_ConditionData[i].m_flExpireTime = MAX( m_ConditionData[i].m_flExpireTime - flReduction, 0 );
  1898. if ( m_ConditionData[i].m_flExpireTime == 0 )
  1899. {
  1900. RemoveCond( (ETFCond)i );
  1901. }
  1902. }
  1903. else
  1904. {
  1905. #if !defined( DEBUG )
  1906. // Prevent hacked usercommand exploits
  1907. if ( m_pOuter->GetTimeSinceLastUserCommand() > 5.f || m_pOuter->GetTimeSinceLastThink() > 5.f )
  1908. {
  1909. ETFCond eCond = (ETFCond)i;
  1910. if ( GetCarryingRuneType() != RUNE_NONE )
  1911. {
  1912. m_pOuter->DropRune();
  1913. }
  1914. if ( ShouldRemoveConditionOnTimeout( eCond ) )
  1915. {
  1916. RemoveCond( eCond );
  1917. // Reset active weapon to prevent stale-state bugs
  1918. CTFWeaponBase *pTFWeapon = m_pOuter->GetActiveTFWeapon();
  1919. if ( pTFWeapon )
  1920. {
  1921. pTFWeapon->WeaponReset();
  1922. }
  1923. m_pOuter->TeamFortress_SetSpeed();
  1924. }
  1925. }
  1926. #endif
  1927. }
  1928. }
  1929. }
  1930. // Our health will only decay ( from being medic buffed ) if we are not being healed by a medic
  1931. // Dispensers can give us the TF_COND_HEALTH_BUFF, but will not maintain or give us health above 100%s
  1932. bool bDecayHealth = true;
  1933. bool bDecayDisguiseHealth = true;
  1934. // If we're being healed, heal ourselves
  1935. if ( InCond( TF_COND_HEALTH_BUFF ) )
  1936. {
  1937. // Heal faster if we haven't been in combat for a while
  1938. float flTimeSinceDamage = gpGlobals->curtime - m_pOuter->GetLastDamageReceivedTime();
  1939. float flScale = RemapValClamped( flTimeSinceDamage, 10, 15, 1.0, 3.0 );
  1940. float flAttribModScale = 1.0;
  1941. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flAttribModScale, mult_health_fromhealers );
  1942. float flCurOverheal = (float)m_pOuter->GetHealth() / (float)m_pOuter->GetMaxHealth();
  1943. if ( flCurOverheal > 1.0f )
  1944. {
  1945. // If they're over their max health the overheal calculation is relative to the max buffable amount scale
  1946. float flMaxHealthForBuffing = m_pOuter->GetMaxHealthForBuffing();
  1947. float flBuffableRangeHealth = m_pOuter->GetHealth() - ( m_pOuter->GetMaxHealth() - flMaxHealthForBuffing );
  1948. flCurOverheal = flBuffableRangeHealth / flMaxHealthForBuffing;
  1949. }
  1950. float flCurDisguiseOverheal = ( GetDisguiseMaxHealth() != 0 ) ? ( (float)GetDisguiseHealth() / (float)GetDisguiseMaxHealth() ) : ( flCurOverheal );
  1951. float fTotalHealAmount = 0.0f;
  1952. for ( int i = 0; i < m_aHealers.Count(); i++ )
  1953. {
  1954. Assert( m_aHealers[i].pHealer );
  1955. float flPerHealerAttribModScale = 1.f;
  1956. // Check if the healer has an attribute that modifies their overheal rate
  1957. if( flCurOverheal > 1.f && !m_aHealers[i].bDispenserHeal )
  1958. {
  1959. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_aHealers[i].pHealer, flPerHealerAttribModScale, overheal_fill_rate );
  1960. }
  1961. bool bHealDisguise = InCond( TF_COND_DISGUISED );
  1962. bool bHealActual = true;
  1963. // dispensers heal cloak
  1964. if ( m_aHealers[i].bDispenserHeal )
  1965. {
  1966. AddToSpyCloakMeter( gpGlobals->frametime * m_aHealers[i].flAmount );
  1967. }
  1968. // Don't heal over the healer's overheal bonus
  1969. if ( flCurOverheal >= m_aHealers[i].flOverhealBonus )
  1970. {
  1971. bHealActual = false;
  1972. }
  1973. // Same overheal check, but for fake health
  1974. if ( InCond( TF_COND_DISGUISED ) && flCurDisguiseOverheal >= m_aHealers[i].flOverhealBonus )
  1975. {
  1976. // Fake over-heal
  1977. bHealDisguise = false;
  1978. }
  1979. CTFPlayer *pTFHealer = ToTFPlayer( m_aHealers[i].pHealer );
  1980. if ( !bHealActual && !bHealDisguise )
  1981. {
  1982. if ( pTFHealer )
  1983. {
  1984. // Quick fix never lets health decay, even when they're at or above max overheal
  1985. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pTFHealer->GetActiveTFWeapon() );
  1986. if ( pMedigun && pMedigun->GetMedigunType() == MEDIGUN_QUICKFIX )
  1987. {
  1988. bDecayHealth = false;
  1989. bDecayDisguiseHealth = false;
  1990. }
  1991. }
  1992. continue;
  1993. }
  1994. // Being healed by a medigun, don't decay our health
  1995. if ( bHealActual )
  1996. {
  1997. bDecayHealth = false;
  1998. }
  1999. if ( bHealDisguise )
  2000. {
  2001. bDecayDisguiseHealth = false;
  2002. }
  2003. // What we multiply the heal amount by (can be changed by conditions or items).
  2004. float flHealAmountMult = 1.0f;
  2005. // Quick-Fix uber
  2006. if ( InCond( TF_COND_MEGAHEAL ) )
  2007. {
  2008. flHealAmountMult = 3.0f;
  2009. }
  2010. flScale *= flHealAmountMult;
  2011. // Dispensers heal at a constant rate
  2012. if ( m_aHealers[i].bDispenserHeal )
  2013. {
  2014. // Dispensers heal at a slower rate, but ignore flScale
  2015. if ( bHealActual )
  2016. {
  2017. float flDispenserFraction = gpGlobals->frametime * m_aHealers[i].flAmount * flAttribModScale;
  2018. m_flHealFraction += flDispenserFraction;
  2019. // track how much this healer has actually done so far
  2020. m_aHealers[i].flHealAccum += clamp( flDispenserFraction, 0.f, (float) GetMaxBuffedHealth() - m_pOuter->GetHealth() );
  2021. }
  2022. if ( bHealDisguise )
  2023. {
  2024. m_flDisguiseHealFraction += gpGlobals->frametime * m_aHealers[i].flAmount * flAttribModScale;
  2025. }
  2026. }
  2027. else // player heals are affected by the last damage time
  2028. {
  2029. if ( bHealActual )
  2030. {
  2031. // Scale this if needed
  2032. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flScale, mult_healing_from_medics );
  2033. m_flHealFraction += gpGlobals->frametime * m_aHealers[i].flAmount * flScale * flAttribModScale * flPerHealerAttribModScale;
  2034. }
  2035. if ( bHealDisguise )
  2036. {
  2037. m_flDisguiseHealFraction += gpGlobals->frametime * m_aHealers[i].flAmount * flScale * flAttribModScale * flPerHealerAttribModScale;
  2038. }
  2039. }
  2040. fTotalHealAmount += m_aHealers[i].flAmount;
  2041. // Keep our decay multiplier uptodate
  2042. if ( m_flBestOverhealDecayMult == -1 || m_aHealers[i].flOverhealDecayMult < m_flBestOverhealDecayMult )
  2043. {
  2044. m_flBestOverhealDecayMult = m_aHealers[i].flOverhealDecayMult;
  2045. }
  2046. }
  2047. if ( InCond( TF_COND_HEALING_DEBUFF ) )
  2048. {
  2049. m_flHealFraction *= 0.75f;
  2050. }
  2051. int nHealthToAdd = (int)m_flHealFraction;
  2052. int nDisguiseHealthToAdd = (int)m_flDisguiseHealFraction;
  2053. if ( nHealthToAdd > 0 || nDisguiseHealthToAdd > 0 )
  2054. {
  2055. if ( nHealthToAdd > 0 )
  2056. {
  2057. m_flHealFraction -= nHealthToAdd;
  2058. }
  2059. if ( nDisguiseHealthToAdd > 0 )
  2060. {
  2061. m_flDisguiseHealFraction -= nDisguiseHealthToAdd;
  2062. }
  2063. int iBoostMax = GetMaxBuffedHealth();
  2064. if ( InCond( TF_COND_DISGUISED ) )
  2065. {
  2066. // Separate cap for disguised health
  2067. int nFakeHealthToAdd = clamp( nDisguiseHealthToAdd, 0, GetDisguiseMaxBuffedHealth() - m_iDisguiseHealth );
  2068. m_iDisguiseHealth += nFakeHealthToAdd;
  2069. }
  2070. // Track health prior to healing
  2071. int nPrevHealth = m_pOuter->GetHealth();
  2072. // Cap it to the max we'll boost a player's health
  2073. nHealthToAdd = clamp( nHealthToAdd, 0, iBoostMax - m_pOuter->GetHealth() );
  2074. m_pOuter->TakeHealth( nHealthToAdd, DMG_IGNORE_MAXHEALTH | DMG_IGNORE_DEBUFFS );
  2075. m_pOuter->AdjustDrownDmg( -1.0 * nHealthToAdd ); // subtract this from the drowndmg in case they're drowning and being healed at the same time
  2076. // split up total healing based on the amount each healer contributes
  2077. if ( fTotalHealAmount > 0 )
  2078. {
  2079. for ( int i = 0; i < m_aHealers.Count(); i++ )
  2080. {
  2081. Assert( m_aHealers[i].pHealScorer );
  2082. Assert( m_aHealers[i].pHealer );
  2083. if ( m_aHealers[i].pHealScorer.IsValid() && m_aHealers[i].pHealer.IsValid() )
  2084. {
  2085. CBaseEntity *pHealer = m_aHealers[i].pHealer;
  2086. float flHealAmount = nHealthToAdd * ( m_aHealers[i].flAmount / fTotalHealAmount );
  2087. if ( pHealer && IsAlly( pHealer ) )
  2088. {
  2089. CTFPlayer *pHealScorer = ToTFPlayer( m_aHealers[i].pHealScorer );
  2090. if ( pHealScorer )
  2091. {
  2092. // Don't report healing when we're close to the buff cap and haven't taken damage recently.
  2093. // This avoids sending bogus heal stats while maintaining our max overheal. Ideally we
  2094. // wouldn't decay in this scenario, but that would be a risky change.
  2095. if ( iBoostMax - nPrevHealth > 1 || gpGlobals->curtime - m_pOuter->GetLastDamageReceivedTime() <= 1.f )
  2096. {
  2097. CTF_GameStats.Event_PlayerHealedOther( pHealScorer, flHealAmount );
  2098. }
  2099. // Add this to the one-second-healing counter
  2100. m_aHealers[i].flHealedLastSecond += flHealAmount;
  2101. HandleRageGain( m_pOuter, kRageBuffFlag_OnMedicHealingReceived, flHealAmount / 2.f, 1.0f );
  2102. float flRage = flHealAmount;
  2103. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() &&
  2104. TFObjectiveResource() && TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() )
  2105. {
  2106. flRage = Max( flHealAmount, 10.f );
  2107. }
  2108. HandleRageGain( pHealScorer, kRageBuffFlag_OnHeal, flRage, 1.0f );
  2109. // If it's been one second, or we know healing beyond this point will be overheal, generate an event
  2110. if ( ( m_flHealedPerSecondTimer <= gpGlobals->curtime || m_pOuter->GetHealth() >= m_pOuter->GetMaxHealth() )
  2111. && m_aHealers[i].flHealedLastSecond > 1 )
  2112. {
  2113. // Make sure this isn't pure overheal
  2114. if ( m_pOuter->GetHealth() - m_aHealers[i].flHealedLastSecond < m_pOuter->GetMaxHealth() )
  2115. {
  2116. float flOverHeal = m_pOuter->GetHealth() - m_pOuter->GetMaxHealth();
  2117. if ( flOverHeal > 0 )
  2118. {
  2119. m_aHealers[i].flHealedLastSecond -= flOverHeal;
  2120. }
  2121. // TEST THIS
  2122. // Give the medic some uber if it is from their (AoE heal) which has no overheal
  2123. if ( m_aHealers[i].flOverhealBonus <= 1.0f )
  2124. {
  2125. // Give a litte bit of uber based on actual healing
  2126. // Give them a little bit of Uber
  2127. CWeaponMedigun *pMedigun = static_cast<CWeaponMedigun *>( pHealScorer->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN ) );
  2128. if ( pMedigun )
  2129. {
  2130. // On Mediguns, per frame, the amount of uber added is based on
  2131. // Default heal rate is 24per second, we scale based on that and frametime
  2132. pMedigun->AddCharge( ( m_aHealers[i].flHealedLastSecond / 24.0f ) * gpGlobals->frametime * 0.33f );
  2133. }
  2134. }
  2135. IGameEvent * event = gameeventmanager->CreateEvent( "player_healed" );
  2136. if ( event )
  2137. {
  2138. // HLTV event priority, not transmitted
  2139. event->SetInt( "priority", 1 );
  2140. // Healed by another player.
  2141. event->SetInt( "patient", m_pOuter->GetUserID() );
  2142. event->SetInt( "healer", pHealScorer->GetUserID() );
  2143. event->SetInt( "amount", m_aHealers[i].flHealedLastSecond );
  2144. gameeventmanager->FireEvent( event );
  2145. }
  2146. // Can we figure out which item is doing this healing?
  2147. if ( pHealScorer )
  2148. {
  2149. // Can be Mediguns or anything that gives off 'heal' buff like amputator aoe heal
  2150. EconEntity_OnOwnerKillEaterEvent_Batched( pHealScorer->GetActiveTFWeapon(), pHealScorer, m_pOuter, kKillEaterEvent_AllyHealingDone, m_aHealers[i].flHealedLastSecond );
  2151. }
  2152. }
  2153. m_aHealers[i].flHealedLastSecond = 0;
  2154. m_flHealedPerSecondTimer = gpGlobals->curtime + 1.0f;
  2155. }
  2156. }
  2157. }
  2158. else
  2159. {
  2160. CTF_GameStats.Event_PlayerLeachedHealth( m_pOuter, m_aHealers[i].bDispenserHeal, flHealAmount );
  2161. }
  2162. }
  2163. }
  2164. }
  2165. }
  2166. if ( InCond( TF_COND_BURNING ) )
  2167. {
  2168. // Reduce the duration of this burn
  2169. float flReduction = 2; // ( flReduction + 1 ) x faster reduction
  2170. m_flFlameRemoveTime -= flReduction * gpGlobals->frametime;
  2171. }
  2172. if ( InCond( TF_COND_BLEEDING ) )
  2173. {
  2174. // Reduce the duration of this bleeding
  2175. float flReduction = 2; // ( flReduction + 1 ) x faster reduction
  2176. FOR_EACH_VEC( m_PlayerBleeds, i )
  2177. {
  2178. m_PlayerBleeds[i].flBleedingRemoveTime -= flReduction * gpGlobals->frametime;
  2179. }
  2180. }
  2181. }
  2182. if ( !InCond( TF_COND_HEALTH_OVERHEALED ) && m_pOuter->GetHealth() > ( m_pOuter->GetMaxHealth() - m_pOuter->GetRuneHealthBonus() ) )
  2183. {
  2184. AddCond( TF_COND_HEALTH_OVERHEALED, PERMANENT_CONDITION );
  2185. }
  2186. else if ( InCond( TF_COND_HEALTH_OVERHEALED ) && m_pOuter->GetHealth() <= ( m_pOuter->GetMaxHealth() - m_pOuter->GetRuneHealthBonus() ) )
  2187. {
  2188. RemoveCond( TF_COND_HEALTH_OVERHEALED );
  2189. }
  2190. if ( bDecayHealth )
  2191. {
  2192. float flOverheal = GetMaxBuffedHealth( false, true );
  2193. // If we're not being buffed, our health drains back to our max
  2194. if ( m_pOuter->GetHealth() > m_pOuter->GetMaxHealth() )
  2195. {
  2196. // Items exist that get us over max health, without ever being healed, in which case our m_flBestOverhealDecayMult will still be -1.
  2197. float flDrainMult = (m_flBestOverhealDecayMult == -1) ? 1.0 : m_flBestOverhealDecayMult;
  2198. float flBoostMaxAmount = flOverheal - m_pOuter->GetMaxHealth();
  2199. float flDrain = flBoostMaxAmount / (tf_boost_drain_time.GetFloat() * flDrainMult);
  2200. m_flHealFraction += (gpGlobals->frametime * flDrain);
  2201. int nHealthToDrain = (int)m_flHealFraction;
  2202. if ( nHealthToDrain > 0 )
  2203. {
  2204. m_flHealFraction -= nHealthToDrain;
  2205. // Manually subtract the health so we don't generate pain sounds / etc
  2206. m_pOuter->m_iHealth -= nHealthToDrain;
  2207. }
  2208. }
  2209. else if ( m_flBestOverhealDecayMult != -1 )
  2210. {
  2211. m_flBestOverhealDecayMult = -1;
  2212. }
  2213. }
  2214. if ( bDecayDisguiseHealth )
  2215. {
  2216. float flOverheal = GetDisguiseMaxBuffedHealth( false, true );
  2217. if ( InCond( TF_COND_DISGUISED ) && (GetDisguiseHealth() > GetDisguiseMaxHealth()) )
  2218. {
  2219. // Items exist that get us over max health, without ever being healed, in which case our m_flBestOverhealDecayMult will still be -1.
  2220. float flDrainMult = (m_flBestOverhealDecayMult == -1) ? 1.0 : m_flBestOverhealDecayMult;
  2221. float flBoostMaxAmount = flOverheal - GetDisguiseMaxHealth();
  2222. float flDrain = (flBoostMaxAmount / tf_boost_drain_time.GetFloat()) * flDrainMult;
  2223. m_flDisguiseHealFraction += (gpGlobals->frametime * flDrain);
  2224. int nHealthToDrain = (int)m_flDisguiseHealFraction;
  2225. if ( nHealthToDrain > 0 )
  2226. {
  2227. m_flDisguiseHealFraction -= nHealthToDrain;
  2228. // Reduce our fake disguised health by roughly the same amount
  2229. m_iDisguiseHealth -= nHealthToDrain;
  2230. }
  2231. }
  2232. }
  2233. // Taunt
  2234. if ( InCond( TF_COND_TAUNTING ) )
  2235. {
  2236. if ( m_pOuter->IsAllowedToRemoveTaunt() && gpGlobals->curtime > m_pOuter->GetTauntRemoveTime() )
  2237. {
  2238. RemoveCond( TF_COND_TAUNTING );
  2239. }
  2240. }
  2241. if ( InCond( TF_COND_BURNING ) && !m_pOuter->m_bInPowerPlay )
  2242. {
  2243. if ( TFGameRules() && TFGameRules()->IsTruceActive() && m_hBurnAttacker && m_hBurnAttacker->IsTruceValidForEnt() )
  2244. {
  2245. RemoveCond( TF_COND_BURNING );
  2246. }
  2247. else if ( gpGlobals->curtime > m_flFlameRemoveTime || m_pOuter->GetWaterLevel() >= WL_Waist )
  2248. {
  2249. // If we're underwater, put the fire out
  2250. if ( m_pOuter->GetWaterLevel() >= WL_Waist )
  2251. {
  2252. // General achievement for jumping into water while you're on fire
  2253. m_pOuter->AwardAchievement( ACHIEVEMENT_TF_FIRE_WATERJUMP );
  2254. // Pyro achievement for forcing players into water
  2255. if ( m_hBurnAttacker )
  2256. {
  2257. m_hBurnAttacker->AwardAchievement( ACHIEVEMENT_TF_PYRO_FORCE_WATERJUMP );
  2258. }
  2259. }
  2260. RemoveCond( TF_COND_BURNING );
  2261. if ( InCond( TF_COND_HEALTH_BUFF ) )
  2262. {
  2263. // one or more players is healing us, send a "player_extinguished" event. We
  2264. // need to send one for each player who's healing us.
  2265. for ( int i = 0; i < m_aHealers.Count(); i++ )
  2266. {
  2267. Assert( m_aHealers[i].pHealer );
  2268. if ( m_aHealers[i].bDispenserHeal )
  2269. {
  2270. CObjectDispenser *pDispenser = dynamic_cast<CObjectDispenser*>( m_aHealers[i].pHealer.Get() );
  2271. if ( pDispenser )
  2272. {
  2273. CTFPlayer *pTFPlayer = pDispenser->GetBuilder();
  2274. if ( pTFPlayer )
  2275. {
  2276. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"player_extinguished\" against \"%s<%i><%s><%s>\" with \"dispenser\" (attacker_position \"%d %d %d\") (victim_position \"%d %d %d\")\n",
  2277. pTFPlayer->GetPlayerName(),
  2278. pTFPlayer->GetUserID(),
  2279. pTFPlayer->GetNetworkIDString(),
  2280. pTFPlayer->GetTeam()->GetName(),
  2281. m_pOuter->GetPlayerName(),
  2282. m_pOuter->GetUserID(),
  2283. m_pOuter->GetNetworkIDString(),
  2284. m_pOuter->GetTeam()->GetName(),
  2285. (int)m_aHealers[i].pHealer->GetAbsOrigin().x,
  2286. (int)m_aHealers[i].pHealer->GetAbsOrigin().y,
  2287. (int)m_aHealers[i].pHealer->GetAbsOrigin().z,
  2288. (int)m_pOuter->GetAbsOrigin().x,
  2289. (int)m_pOuter->GetAbsOrigin().y,
  2290. (int)m_pOuter->GetAbsOrigin().z );
  2291. }
  2292. }
  2293. // continue;
  2294. }
  2295. EHANDLE pHealer = m_aHealers[i].pHealer;
  2296. if ( m_aHealers[i].bDispenserHeal || !pHealer || !pHealer->IsPlayer() )
  2297. pHealer = m_aHealers[i].pHealScorer;
  2298. if ( !pHealer )
  2299. continue;
  2300. CTFPlayer *pTFPlayer = ToTFPlayer( pHealer );
  2301. if ( pTFPlayer && !m_aHealers[i].bDispenserHeal )
  2302. {
  2303. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"player_extinguished\" against \"%s<%i><%s><%s>\" with \"%s\" (attacker_position \"%d %d %d\") (victim_position \"%d %d %d\")\n",
  2304. pTFPlayer->GetPlayerName(),
  2305. pTFPlayer->GetUserID(),
  2306. pTFPlayer->GetNetworkIDString(),
  2307. pTFPlayer->GetTeam()->GetName(),
  2308. m_pOuter->GetPlayerName(),
  2309. m_pOuter->GetUserID(),
  2310. m_pOuter->GetNetworkIDString(),
  2311. m_pOuter->GetTeam()->GetName(),
  2312. ( pTFPlayer->GetActiveTFWeapon() ) ? pTFPlayer->GetActiveTFWeapon()->GetName() : "tf_weapon_medigun",
  2313. (int)pTFPlayer->GetAbsOrigin().x,
  2314. (int)pTFPlayer->GetAbsOrigin().y,
  2315. (int)pTFPlayer->GetAbsOrigin().z,
  2316. (int)m_pOuter->GetAbsOrigin().x,
  2317. (int)m_pOuter->GetAbsOrigin().y,
  2318. (int)m_pOuter->GetAbsOrigin().z );
  2319. }
  2320. // Tell the clients involved
  2321. CRecipientFilter involved_filter;
  2322. CBasePlayer *pBasePlayerHealer = ToBasePlayer( pHealer );
  2323. if ( pBasePlayerHealer )
  2324. {
  2325. involved_filter.AddRecipient( pBasePlayerHealer );
  2326. }
  2327. involved_filter.AddRecipient( m_pOuter );
  2328. UserMessageBegin( involved_filter, "PlayerExtinguished" );
  2329. WRITE_BYTE( pHealer->entindex() );
  2330. WRITE_BYTE( m_pOuter->entindex() );
  2331. MessageEnd();
  2332. IGameEvent *event = gameeventmanager->CreateEvent( "player_extinguished" );
  2333. if ( event )
  2334. {
  2335. event->SetInt( "victim", m_pOuter->entindex() );
  2336. event->SetInt( "healer", pHealer->entindex() );
  2337. gameeventmanager->FireEvent( event, true );
  2338. }
  2339. }
  2340. }
  2341. }
  2342. else if ( ( gpGlobals->curtime >= m_flFlameBurnTime ) && ( TF_CLASS_PYRO != m_pOuter->GetPlayerClass()->GetClassIndex() ) )
  2343. {
  2344. // Burn the player (if not pyro, who does not take persistent burning damage)
  2345. float flBurnDamage = TF_BURNING_DMG;
  2346. int nKillType = TF_DMG_CUSTOM_BURNING;
  2347. if ( m_hBurnWeapon )
  2348. {
  2349. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hBurnWeapon, flBurnDamage, mult_wpn_burndmg );
  2350. if ( m_hBurnWeapon.Get()->GetWeaponID() == TF_WEAPON_FLAREGUN )
  2351. {
  2352. nKillType = TF_DMG_CUSTOM_BURNING_FLARE;
  2353. }
  2354. else if ( m_hBurnWeapon.Get()->GetWeaponID() == TF_WEAPON_COMPOUND_BOW )
  2355. {
  2356. nKillType = TF_DMG_CUSTOM_BURNING_ARROW;
  2357. }
  2358. }
  2359. // Halloween Spell
  2360. if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  2361. {
  2362. int iHalloweenSpell = 0;
  2363. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_hBurnWeapon, iHalloweenSpell, halloween_green_flames );
  2364. if ( iHalloweenSpell > 0 )
  2365. {
  2366. const char *pEffectName = "halloween_burningplayer_flyingbits";
  2367. // Extra Halloween Particles
  2368. DispatchParticleEffect( pEffectName, PATTACH_ABSORIGIN_FOLLOW, m_pOuter, 0, false );
  2369. }
  2370. }
  2371. CTakeDamageInfo info( m_hBurnAttacker, m_hBurnAttacker, m_hBurnWeapon, flBurnDamage, DMG_BURN | DMG_PREVENT_PHYSICS_FORCE, nKillType );
  2372. m_pOuter->TakeDamage( info );
  2373. // Give health to attacker if they are carrying the Vampire Powerup.
  2374. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  2375. {
  2376. CTFPlayer *pTFAttacker = ToTFPlayer( GetConditionProvider( TF_COND_BURNING ) );
  2377. if ( pTFAttacker && pTFAttacker != m_pOuter )
  2378. {
  2379. if ( pTFAttacker->m_Shared.GetCarryingRuneType() == RUNE_VAMPIRE )
  2380. {
  2381. pTFAttacker->TakeHealth( flBurnDamage, DMG_GENERIC );
  2382. }
  2383. }
  2384. }
  2385. m_flFlameBurnTime = gpGlobals->curtime + TF_BURNING_FREQUENCY;
  2386. }
  2387. if ( m_flNextBurningSound < gpGlobals->curtime )
  2388. {
  2389. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_ONFIRE );
  2390. m_flNextBurningSound = gpGlobals->curtime + 2.5;
  2391. }
  2392. }
  2393. // Stops the drain hack.
  2394. if ( m_pOuter->IsPlayerClass( TF_CLASS_MEDIC ) )
  2395. {
  2396. CWeaponMedigun *pWeapon = ( CWeaponMedigun* )m_pOuter->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  2397. if ( pWeapon && pWeapon->IsReleasingCharge() )
  2398. {
  2399. pWeapon->DrainCharge();
  2400. }
  2401. }
  2402. TestAndExpireChargeEffect( MEDIGUN_CHARGE_INVULN );
  2403. TestAndExpireChargeEffect( MEDIGUN_CHARGE_CRITICALBOOST );
  2404. TestAndExpireChargeEffect( MEDIGUN_CHARGE_MEGAHEAL );
  2405. //TestAndExpireChargeEffect( MEDIGUN_CHARGE_BULLET_RESIST );
  2406. //TestAndExpireChargeEffect( MEDIGUN_CHARGE_BLAST_RESIST );
  2407. //TestAndExpireChargeEffect( MEDIGUN_CHARGE_FIRE_RESIST );
  2408. if ( InCond( TF_COND_STEALTHED_BLINK ) )
  2409. {
  2410. float flBlinkTime = TF_SPY_STEALTH_BLINKTIME;
  2411. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flBlinkTime, cloak_blink_time_penalty );
  2412. if ( flBlinkTime < ( gpGlobals->curtime - m_flLastStealthExposeTime ) )
  2413. {
  2414. RemoveCond( TF_COND_STEALTHED_BLINK );
  2415. }
  2416. }
  2417. if ( InCond( TF_COND_FEIGN_DEATH ) )
  2418. {
  2419. if ( m_flFeignDeathEnd < gpGlobals->curtime )
  2420. {
  2421. RemoveCond( TF_COND_FEIGN_DEATH );
  2422. }
  2423. }
  2424. if ( m_pOuter->GetWaterLevel() >= WL_Waist )
  2425. {
  2426. if ( InCond( TF_COND_URINE ) )
  2427. {
  2428. // If we're underwater, wash off the urine.
  2429. RemoveCond( TF_COND_URINE );
  2430. }
  2431. if ( InCond( TF_COND_MAD_MILK ) )
  2432. {
  2433. // If we're underwater, wash off the Mad Milk.
  2434. RemoveCond( TF_COND_MAD_MILK );
  2435. }
  2436. }
  2437. if ( !InCond( TF_COND_DISGUISED ) )
  2438. {
  2439. // Remove our disguise weapon if we are ever not disguised and we have one.
  2440. RemoveDisguiseWeapon();
  2441. // also clear the disguise weapon list
  2442. m_pOuter->ClearDisguiseWeaponList();
  2443. }
  2444. if ( InCond( TF_COND_BLEEDING ) )
  2445. {
  2446. FOR_EACH_VEC_BACK( m_PlayerBleeds, i )
  2447. {
  2448. bleed_struct_t& bleed = m_PlayerBleeds[i];
  2449. if ( TFGameRules() && TFGameRules()->IsTruceActive() && bleed.hBleedingAttacker && ( bleed.hBleedingAttacker != m_pOuter ) && bleed.hBleedingAttacker->IsTruceValidForEnt() )
  2450. {
  2451. m_PlayerBleeds.FastRemove( i );
  2452. }
  2453. else if ( gpGlobals->curtime >= bleed.flBleedingRemoveTime && !bleed.bPermanentBleeding )
  2454. {
  2455. m_PlayerBleeds.FastRemove( i );
  2456. }
  2457. else if ( ( gpGlobals->curtime >= bleed.flBleedingTime ) )
  2458. {
  2459. bleed.flBleedingTime = gpGlobals->curtime + TF_BLEEDING_FREQUENCY;
  2460. CTakeDamageInfo info( bleed.hBleedingAttacker, bleed.hBleedingAttacker, bleed.hBleedingWeapon, bleed.nBleedDmg, DMG_SLASH, TF_DMG_CUSTOM_BLEEDING );
  2461. m_pOuter->TakeDamage( info );
  2462. // It's very possible we died from the take damage, which clears all our conditions
  2463. // and nukes m_PlayerBleeds. If that happens, bust out of this loop.
  2464. if( m_PlayerBleeds.Count() == 0 )
  2465. break;
  2466. }
  2467. }
  2468. if ( !m_PlayerBleeds.Count() )
  2469. {
  2470. RemoveCond( TF_COND_BLEEDING );
  2471. }
  2472. }
  2473. #ifdef STAGING_ONLY
  2474. if ( InCond( TF_COND_TRANQ_SPY_BOOST ) )
  2475. {
  2476. m_flSpyTranqBuffDuration = GetConditionDuration( TF_COND_TRANQ_SPY_BOOST );
  2477. }
  2478. else
  2479. #endif // STAGING_ONLY
  2480. {
  2481. m_flSpyTranqBuffDuration = 0;
  2482. }
  2483. if ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsPlayingRobotDestructionMode() )
  2484. {
  2485. RadiusCurrencyCollectionCheck();
  2486. }
  2487. if ( TFGameRules()->IsMannVsMachineMode() && m_pOuter->IsPlayerClass( TF_CLASS_SPY) )
  2488. {
  2489. // In MvM, Spies reveal other spies in a radius around them
  2490. RadiusSpyScan();
  2491. }
  2492. if ( GetCarryingRuneType() == RUNE_PLAGUE )
  2493. {
  2494. RadiusHealthkitCollectionCheck();
  2495. }
  2496. #endif // GAME_DLL
  2497. }
  2498. //-----------------------------------------------------------------------------
  2499. // Purpose: Do CLIENT/SERVER SHARED condition thinks.
  2500. //-----------------------------------------------------------------------------
  2501. void CTFPlayerShared::ConditionThink( void )
  2502. {
  2503. // Client Only Updates Meters for Local Only
  2504. #ifdef CLIENT_DLL
  2505. if ( m_pOuter->IsLocalPlayer() )
  2506. #endif
  2507. {
  2508. UpdateCloakMeter();
  2509. UpdateRageBuffsAndRage();
  2510. UpdateEnergyDrinkMeter();
  2511. UpdateChargeMeter();
  2512. DemoShieldChargeThink();
  2513. #ifdef STAGING_ONLY
  2514. UpdateRocketPack();
  2515. #endif // STAGING_ONLY
  2516. }
  2517. VehicleThink();
  2518. if ( m_pOuter->GetFlags() & FL_ONGROUND && InCond( TF_COND_PARACHUTE_DEPLOYED ))
  2519. {
  2520. RemoveCond( TF_COND_PARACHUTE_DEPLOYED );
  2521. }
  2522. // See if we should be pulsing our radius heal
  2523. PulseMedicRadiusHeal();
  2524. PulseKingRuneBuff();
  2525. m_ConditionList.Think();
  2526. if ( InCond( TF_COND_STUNNED ) )
  2527. {
  2528. #ifdef GAME_DLL
  2529. if ( IsControlStunned() )
  2530. {
  2531. m_pOuter->SetAbsAngles( m_pOuter->m_angTauntCamera );
  2532. m_pOuter->SetLocalAngles( m_pOuter->m_angTauntCamera );
  2533. }
  2534. #endif
  2535. if ( GetActiveStunInfo() && gpGlobals->curtime > GetActiveStunInfo()->flExpireTime )
  2536. {
  2537. #ifdef GAME_DLL
  2538. m_PlayerStuns.Remove( m_iStunIndex );
  2539. m_iStunIndex = -1;
  2540. // Apply our next stun
  2541. if ( m_PlayerStuns.Count() )
  2542. {
  2543. int iStrongestIdx = 0;
  2544. for ( int i = 1; i < m_PlayerStuns.Count(); i++ )
  2545. {
  2546. if ( m_PlayerStuns[i].flStunAmount > m_PlayerStuns[iStrongestIdx].flStunAmount )
  2547. {
  2548. iStrongestIdx = i;
  2549. }
  2550. }
  2551. m_iStunIndex = iStrongestIdx;
  2552. AddCond( TF_COND_STUNNED, -1.f, m_PlayerStuns[m_iStunIndex].hPlayer );
  2553. m_iMovementStunParity = ( m_iMovementStunParity + 1 ) & ( ( 1 << MOVEMENTSTUN_PARITY_BITS ) - 1 );
  2554. Assert( GetActiveStunInfo() );
  2555. }
  2556. else
  2557. {
  2558. RemoveCond( TF_COND_STUNNED );
  2559. }
  2560. #endif // GAME_DLL
  2561. UpdateLegacyStunSystem();
  2562. }
  2563. else if ( IsControlStunned() && GetActiveStunInfo() && ( gpGlobals->curtime > GetActiveStunInfo()->flStartFadeTime ) )
  2564. {
  2565. // Control stuns have a final anim to play.
  2566. ControlStunFading();
  2567. }
  2568. #ifdef CLIENT_DLL
  2569. // turn off stun effect that gets turned on when incomplete stun msg is received on the client
  2570. if ( GetActiveStunInfo() && GetActiveStunInfo()->iStunFlags & TF_STUN_NO_EFFECTS )
  2571. {
  2572. if ( m_pOuter->m_pStunnedEffect )
  2573. {
  2574. // Remove stun stars if they are still around.
  2575. // They might be if we died, etc.
  2576. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pStunnedEffect );
  2577. m_pOuter->m_pStunnedEffect = NULL;
  2578. }
  2579. }
  2580. #endif
  2581. }
  2582. if ( InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  2583. {
  2584. #ifdef GAME_DLL
  2585. static struct
  2586. {
  2587. float flTimeLeft;
  2588. int nStage;
  2589. } s_vecBombStages[] = { { 8.0f, 0 }, { 3.0f, 1 }, { 0.0f, 2 } };
  2590. for ( int i = 0; i < ARRAYSIZE( s_vecBombStages ); ++i )
  2591. {
  2592. if ( m_ConditionData[TF_COND_HALLOWEEN_BOMB_HEAD].m_flExpireTime >= s_vecBombStages[i].flTimeLeft )
  2593. {
  2594. m_nHalloweenBombHeadStage = s_vecBombStages[i].nStage;
  2595. break;
  2596. }
  2597. }
  2598. if ( TFGameRules() && TFGameRules()->GetActiveBoss() && ( TFGameRules()->GetActiveBoss()->GetBossType() == HALLOWEEN_BOSS_MERASMUS ) )
  2599. {
  2600. if ( m_pOuter->IsAlive() )
  2601. {
  2602. Vector vToBoss = m_pOuter->EyePosition() - TFGameRules()->GetActiveBoss()->WorldSpaceCenter();
  2603. if ( vToBoss.IsLengthLessThan( 100.f ) )
  2604. {
  2605. CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
  2606. if ( pMerasmus )
  2607. {
  2608. pMerasmus->AddStun( m_pOuter );
  2609. }
  2610. }
  2611. }
  2612. }
  2613. #else
  2614. m_pOuter->HalloweenBombHeadUpdate();
  2615. #endif
  2616. }
  2617. else
  2618. {
  2619. #ifdef GAME_DLL
  2620. m_nHalloweenBombHeadStage = 0;
  2621. #endif
  2622. }
  2623. #ifdef GAME_DLL
  2624. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_VIADUCT ) && InCond( TF_COND_PURGATORY ) )
  2625. {
  2626. // escalating injury multiplier while in purgatory
  2627. if ( m_pOuter->m_purgatoryPainMultiplierTimer.IsElapsed() )
  2628. {
  2629. ++m_pOuter->m_purgatoryPainMultiplier;
  2630. // injury multiplies rapidly after initial period
  2631. m_pOuter->m_purgatoryPainMultiplierTimer.Start( 10.0f );
  2632. }
  2633. }
  2634. #endif
  2635. CheckDisguiseTimer();
  2636. #ifdef CLIENT_DLL
  2637. if ( InCond( TF_COND_TAUNTING ) && m_flTauntParticleRefireTime > 0.0f && gpGlobals->curtime >= m_flTauntParticleRefireTime )
  2638. {
  2639. FireClientTauntParticleEffects();
  2640. }
  2641. #endif // CLIENT_DLL
  2642. }
  2643. //-----------------------------------------------------------------------------
  2644. // Purpose:
  2645. //-----------------------------------------------------------------------------
  2646. void CTFPlayerShared::CheckDisguiseTimer( void )
  2647. {
  2648. if ( InCond( TF_COND_DISGUISING ) && GetDisguiseCompleteTime() > 0 )
  2649. {
  2650. if ( gpGlobals->curtime > GetDisguiseCompleteTime() )
  2651. {
  2652. CompleteDisguise();
  2653. }
  2654. }
  2655. }
  2656. //-----------------------------------------------------------------------------
  2657. // Purpose:
  2658. //-----------------------------------------------------------------------------
  2659. void CTFPlayerShared::OnAddZoomed( void )
  2660. {
  2661. #ifdef CLIENT_DLL
  2662. // hide cosmetic while zoom in thirdperson
  2663. if ( m_pOuter == C_TFPlayer::GetLocalTFPlayer() && ::input->CAM_IsThirdPerson() )
  2664. {
  2665. m_pOuter->UpdateWearables();
  2666. }
  2667. #endif // CLIENT_DLL
  2668. }
  2669. //-----------------------------------------------------------------------------
  2670. // Purpose:
  2671. //-----------------------------------------------------------------------------
  2672. void CTFPlayerShared::OnRemoveZoomed( void )
  2673. {
  2674. #ifdef GAME_DLL
  2675. m_pOuter->SetFOV( m_pOuter, 0, 0.1f );
  2676. #endif // GAME_DLL
  2677. #ifdef CLIENT_DLL
  2678. // unhide cosmetic after zoom in thirdperson
  2679. if ( m_pOuter == C_TFPlayer::GetLocalTFPlayer() && ::input->CAM_IsThirdPerson() )
  2680. {
  2681. m_pOuter->UpdateWearables();
  2682. }
  2683. #endif // CLIENT_DLL
  2684. }
  2685. //-----------------------------------------------------------------------------
  2686. // Purpose:
  2687. //-----------------------------------------------------------------------------
  2688. void CTFPlayerShared::OnAddDisguising( void )
  2689. {
  2690. #ifdef CLIENT_DLL
  2691. if ( m_pOuter->GetPredictable() && ( !prediction->IsFirstTimePredicted() || m_bSyncingConditions ) )
  2692. return;
  2693. if ( m_pOuter->m_pDisguisingEffect )
  2694. {
  2695. // m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pDisguisingEffect );
  2696. }
  2697. if ( !m_pOuter->IsLocalPlayer() && ( !IsStealthed() || !m_pOuter->IsEnemyPlayer() ) )
  2698. {
  2699. const char *pEffectName = ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? "spy_start_disguise_red" : "spy_start_disguise_blue";
  2700. m_pOuter->m_pDisguisingEffect = m_pOuter->ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW );
  2701. m_pOuter->m_flDisguiseEffectStartTime = gpGlobals->curtime;
  2702. }
  2703. m_pOuter->EmitSound( "Player.Spy_Disguise" );
  2704. #endif
  2705. }
  2706. //-----------------------------------------------------------------------------
  2707. // Purpose: set up effects for when player finished disguising
  2708. //-----------------------------------------------------------------------------
  2709. void CTFPlayerShared::OnAddDisguised( void )
  2710. {
  2711. #ifdef CLIENT_DLL
  2712. if ( m_pOuter->m_pDisguisingEffect )
  2713. {
  2714. // turn off disguising particles
  2715. // m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pDisguisingEffect );
  2716. m_pOuter->m_pDisguisingEffect = NULL;
  2717. }
  2718. m_pOuter->m_flDisguiseEndEffectStartTime = gpGlobals->curtime;
  2719. UpdateCritBoostEffect( kCritBoost_ForceRefresh );
  2720. m_pOuter->UpdateSpyStateChange();
  2721. #endif
  2722. }
  2723. //-----------------------------------------------------------------------------
  2724. // Purpose:
  2725. //-----------------------------------------------------------------------------
  2726. void CTFPlayerShared::OnAddDemoCharge( void )
  2727. {
  2728. #ifdef CLIENT_DLL
  2729. m_pOuter->StopSound( "DemoCharge.ChargeCritOn" );
  2730. m_pOuter->EmitSound( "DemoCharge.ChargeCritOn" );
  2731. UpdateCritBoostEffect();
  2732. #endif // CLIENT_DLL
  2733. }
  2734. #ifdef CLIENT_DLL
  2735. //-----------------------------------------------------------------------------
  2736. // Purpose: Return the team that the spy is displayed as.
  2737. // Not disguised: His own team
  2738. // Disguised: The team he is disguised as
  2739. //-----------------------------------------------------------------------------
  2740. int CTFPlayerShared::GetDisplayedTeam( void ) const
  2741. {
  2742. int iVisibleTeam = m_pOuter->GetTeamNumber();
  2743. // if this player is disguised and on the other team, use disguise team
  2744. if ( InCond( TF_COND_DISGUISED ) && m_pOuter->IsEnemyPlayer() )
  2745. {
  2746. iVisibleTeam = GetDisguiseTeam();
  2747. }
  2748. return iVisibleTeam;
  2749. }
  2750. //-----------------------------------------------------------------------------
  2751. // Purpose: start, end, and changing disguise classes
  2752. //-----------------------------------------------------------------------------
  2753. void CTFPlayerShared::OnDisguiseChanged( void )
  2754. {
  2755. // recalc disguise model index
  2756. //RecalcDisguiseWeapon( true );
  2757. m_pOuter->UpdateSpyStateChange();
  2758. }
  2759. #endif
  2760. //-----------------------------------------------------------------------------
  2761. // Purpose:
  2762. //-----------------------------------------------------------------------------
  2763. void CTFPlayerShared::OnAddInvulnerable( void )
  2764. {
  2765. #ifdef CLIENT_DLL
  2766. if ( m_pOuter->IsLocalPlayer() )
  2767. {
  2768. const char *pEffectName = NULL;
  2769. switch( m_pOuter->GetTeamNumber() )
  2770. {
  2771. case TF_TEAM_BLUE:
  2772. default:
  2773. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE;
  2774. break;
  2775. case TF_TEAM_RED:
  2776. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED;
  2777. break;
  2778. }
  2779. IMaterial *pMaterial = materials->FindMaterial( pEffectName, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  2780. if ( !IsErrorMaterial( pMaterial ) )
  2781. {
  2782. view->SetScreenOverlayMaterial( pMaterial );
  2783. }
  2784. if ( m_pOuter->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  2785. {
  2786. g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_RECEIVE_UBER_GRIND );
  2787. }
  2788. }
  2789. #else
  2790. // remove any persistent damaging conditions
  2791. if ( InCond( TF_COND_BURNING ) )
  2792. {
  2793. RemoveCond( TF_COND_BURNING );
  2794. }
  2795. if ( InCond( TF_COND_URINE ) )
  2796. {
  2797. RemoveCond( TF_COND_URINE );
  2798. }
  2799. if ( InCond( TF_COND_BLEEDING ) )
  2800. {
  2801. RemoveCond( TF_COND_BLEEDING );
  2802. }
  2803. if ( InCond( TF_COND_MAD_MILK ) )
  2804. {
  2805. RemoveCond( TF_COND_MAD_MILK );
  2806. }
  2807. if ( InCond( TF_COND_PLAGUE ) )
  2808. {
  2809. RemoveCond( TF_COND_PLAGUE );
  2810. }
  2811. #ifdef STAGING_ONLY
  2812. if ( InCond( TF_COND_TRANQ_MARKED ) )
  2813. {
  2814. RemoveCond( TF_COND_TRANQ_MARKED );
  2815. }
  2816. #endif // STAGING_ONLY
  2817. #endif
  2818. }
  2819. //-----------------------------------------------------------------------------
  2820. // Purpose:
  2821. //-----------------------------------------------------------------------------
  2822. void CTFPlayerShared::OnRemoveInvulnerable( void )
  2823. {
  2824. #ifdef CLIENT_DLL
  2825. if ( m_pOuter->IsLocalPlayer() )
  2826. {
  2827. // only remove the overlay if it is an invuln material
  2828. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  2829. if ( pMaterial &&
  2830. ( FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE ) ||
  2831. FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED ) ) )
  2832. {
  2833. view->SetScreenOverlayMaterial( NULL );
  2834. }
  2835. }
  2836. #endif
  2837. }
  2838. //-----------------------------------------------------------------------------
  2839. // Purpose:
  2840. //-----------------------------------------------------------------------------
  2841. void CTFPlayerShared::OnRemoveInvulnerableWearingOff( void )
  2842. {
  2843. #ifdef CLIENT_DLL
  2844. m_flInvulnerabilityRemoveTime = gpGlobals->curtime;
  2845. #endif
  2846. }
  2847. //-----------------------------------------------------------------------------
  2848. // Purpose:
  2849. //-----------------------------------------------------------------------------
  2850. void CTFPlayerShared::OnAddPhase( void )
  2851. {
  2852. UpdatePhaseEffects();
  2853. #ifdef CLIENT_DLL
  2854. #else
  2855. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "started_dodging:1" );
  2856. #endif
  2857. }
  2858. //-----------------------------------------------------------------------------
  2859. // Purpose:
  2860. //-----------------------------------------------------------------------------
  2861. void CTFPlayerShared::OnRemovePhase( void )
  2862. {
  2863. RemovePhaseEffects();
  2864. #ifdef CLIENT_DLL
  2865. #else
  2866. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "started_dodging:0" );
  2867. // Tell this player how much damage they dodged.
  2868. CSingleUserRecipientFilter user( m_pOuter );
  2869. UserMessageBegin( user, "DamageDodged" );
  2870. WRITE_SHORT( clamp( m_iPhaseDamage, 0, 10000 ) );
  2871. MessageEnd();
  2872. m_iPhaseDamage = 0;
  2873. #endif
  2874. }
  2875. //-----------------------------------------------------------------------------
  2876. // Purpose:
  2877. //-----------------------------------------------------------------------------
  2878. void CTFPlayerShared::OnAddUrine( void )
  2879. {
  2880. #ifdef CLIENT_DLL
  2881. if ( tf_colorblindassist.GetBool() )
  2882. {
  2883. m_pOuter->AddOverheadEffect( "peejar_icon" );
  2884. }
  2885. if ( !m_pOuter->m_pUrineEffect )
  2886. {
  2887. m_pOuter->m_pUrineEffect = m_pOuter->ParticleProp()->Create( "peejar_drips", PATTACH_ABSORIGIN_FOLLOW ); // pEffect! Kek!
  2888. }
  2889. if ( m_pOuter->m_pUrineEffect )
  2890. {
  2891. m_pOuter->ParticleProp()->AddControlPoint( m_pOuter->m_pUrineEffect, 1, m_pOuter, PATTACH_ABSORIGIN_FOLLOW );
  2892. }
  2893. if ( m_pOuter->IsLocalPlayer() )
  2894. {
  2895. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_URINE, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  2896. if ( !IsErrorMaterial( pMaterial ) )
  2897. {
  2898. view->SetScreenOverlayMaterial( pMaterial );
  2899. }
  2900. }
  2901. #else
  2902. // m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "urine_on" );
  2903. #endif
  2904. }
  2905. //-----------------------------------------------------------------------------
  2906. // Purpose:
  2907. //-----------------------------------------------------------------------------
  2908. void CTFPlayerShared::OnRemoveUrine( void )
  2909. {
  2910. #ifdef CLIENT_DLL
  2911. m_pOuter->RemoveOverheadEffect( "peejar_icon", true );
  2912. if ( m_pOuter->m_pUrineEffect )
  2913. {
  2914. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pUrineEffect );
  2915. m_pOuter->m_pUrineEffect = NULL;
  2916. }
  2917. if ( m_pOuter->IsLocalPlayer() )
  2918. {
  2919. // only remove the overlay if it is urine
  2920. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  2921. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_URINE ) )
  2922. {
  2923. view->SetScreenOverlayMaterial( NULL );
  2924. }
  2925. }
  2926. #else
  2927. // m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "urine_off" );
  2928. if ( m_hPeeAttacker )
  2929. {
  2930. // Tell the clients involved in the jarate
  2931. CRecipientFilter involved_filter;
  2932. involved_filter.AddRecipient( m_pOuter );
  2933. involved_filter.AddRecipient( m_hPeeAttacker );
  2934. UserMessageBegin( involved_filter, "PlayerJaratedFade" );
  2935. WRITE_BYTE( m_hPeeAttacker->entindex() );
  2936. WRITE_BYTE( m_pOuter->entindex() );
  2937. MessageEnd();
  2938. }
  2939. m_hPeeAttacker = NULL;
  2940. #endif
  2941. }
  2942. //-----------------------------------------------------------------------------
  2943. // Purpose:
  2944. //-----------------------------------------------------------------------------
  2945. void CTFPlayerShared::OnAddMarkedForDeath( void )
  2946. {
  2947. #ifdef CLIENT_DLL
  2948. m_pOuter->UpdatedMarkedForDeathEffect();
  2949. if ( m_pOuter->IsLocalPlayer() )
  2950. {
  2951. m_pOuter->EmitSound( "Weapon_Marked_for_Death.Indicator" );
  2952. }
  2953. else if ( !InCond( TF_COND_DISGUISED ) && !IsStealthed() )
  2954. {
  2955. m_pOuter->EmitSound( "Weapon_Marked_for_Death.Initial" );
  2956. }
  2957. /*
  2958. // Do we want to have a screen overlay effect?
  2959. {
  2960. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_URINE, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  2961. if ( !IsErrorMaterial( pMaterial ) )
  2962. {
  2963. view->SetScreenOverlayMaterial( pMaterial );
  2964. }
  2965. }
  2966. */
  2967. #endif
  2968. }
  2969. //-----------------------------------------------------------------------------
  2970. // Purpose:
  2971. //-----------------------------------------------------------------------------
  2972. void CTFPlayerShared::OnRemoveMarkedForDeath( void )
  2973. {
  2974. #ifdef CLIENT_DLL
  2975. m_pOuter->UpdatedMarkedForDeathEffect();
  2976. /*
  2977. if ( m_pOuter->IsLocalPlayer() )
  2978. {
  2979. // only remove the overlay if it is urine
  2980. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  2981. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_URINE ) )
  2982. {
  2983. view->SetScreenOverlayMaterial( NULL );
  2984. }
  2985. }
  2986. */
  2987. #endif
  2988. }
  2989. #ifdef STAGING_ONLY
  2990. //-----------------------------------------------------------------------------
  2991. // Purpose:
  2992. //-----------------------------------------------------------------------------
  2993. void CTFPlayerShared::OnAddTranqMark( void )
  2994. {
  2995. #ifdef CLIENT_DLL
  2996. //if ( !InCond( TF_COND_DISGUISED ) && !IsStealthed() )
  2997. //{
  2998. m_pOuter->UpdateTranqMark( true );
  2999. //}
  3000. //if ( m_pOuter->IsLocalPlayer() )
  3001. //{
  3002. // m_pOuter->EmitSound( "Weapon_Marked_for_Death.Indicator" );
  3003. //}
  3004. //else if ( !InCond( TF_COND_DISGUISED ) && !IsStealthed() )
  3005. //{
  3006. // m_pOuter->EmitSound( "Weapon_Marked_for_Death.Initial" );
  3007. //}
  3008. #endif
  3009. }
  3010. //-----------------------------------------------------------------------------
  3011. // Purpose:
  3012. //-----------------------------------------------------------------------------
  3013. void CTFPlayerShared::OnRemoveTranqMark( void )
  3014. {
  3015. #ifdef CLIENT_DLL
  3016. m_pOuter->UpdateTranqMark( false );
  3017. #endif
  3018. }
  3019. //-----------------------------------------------------------------------------
  3020. void CTFPlayerShared::OnAddCondSpyClassSteal( void )
  3021. {
  3022. #ifdef CLIENT_DLL
  3023. m_pOuter->UpdateSpyClassStealParticle( true );
  3024. #endif
  3025. }
  3026. //-----------------------------------------------------------------------------
  3027. void CTFPlayerShared::OnRemoveCondSpyClassSteal( void )
  3028. {
  3029. #ifdef CLIENT_DLL
  3030. m_pOuter->UpdateSpyClassStealParticle( false );
  3031. #endif
  3032. }
  3033. #endif // STAGING_ONLY
  3034. //-----------------------------------------------------------------------------
  3035. // Purpose: PARACHUTE
  3036. //-----------------------------------------------------------------------------
  3037. void CTFPlayerShared::OnAddCondParachute( void )
  3038. {
  3039. #ifdef CLIENT_DLL
  3040. if ( m_pOuter->GetPredictable() && ( prediction->IsFirstTimePredicted() && !m_bSyncingConditions ) )
  3041. {
  3042. m_pOuter->EmitSound( "Parachute_open" );
  3043. }
  3044. if ( InCond( TF_COND_HALLOWEEN_KART ) )
  3045. {
  3046. if ( !m_hKartParachuteEntity )
  3047. {
  3048. C_BaseAnimating* pBanner = new C_BaseAnimating;
  3049. //if ( pBanner )
  3050. // return;
  3051. pBanner->m_nSkin = 0;
  3052. pBanner->InitializeAsClientEntity( "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_parachute.mdl", RENDER_GROUP_OPAQUE_ENTITY );
  3053. pBanner->ForceClientSideAnimationOn();
  3054. int iSpine = m_pOuter->LookupBone( "bip_spine_3" );
  3055. Assert( iSpine != -1 );
  3056. if ( iSpine != -1 )
  3057. {
  3058. pBanner->AttachEntityToBone( m_pOuter, iSpine );
  3059. }
  3060. int sequence = pBanner->SelectWeightedSequence( ACT_PARACHUTE_DEPLOY_IDLE );
  3061. pBanner->ResetSequence( sequence );
  3062. m_hKartParachuteEntity.Set( pBanner );
  3063. }
  3064. }
  3065. else
  3066. {
  3067. IGameEvent *event = gameeventmanager->CreateEvent( "parachute_deploy" );
  3068. if ( event )
  3069. {
  3070. event->SetInt( "index", m_pOuter->entindex() );
  3071. gameeventmanager->FireEventClientSide( event );
  3072. }
  3073. }
  3074. #endif
  3075. }
  3076. //-----------------------------------------------------------------------------
  3077. void CTFPlayerShared::OnRemoveCondParachute( void )
  3078. {
  3079. #ifdef CLIENT_DLL
  3080. if ( m_pOuter->GetPredictable() && ( prediction->IsFirstTimePredicted() && !m_bSyncingConditions ) )
  3081. {
  3082. m_pOuter->EmitSound( "Parachute_close" );
  3083. }
  3084. if ( m_hKartParachuteEntity )
  3085. {
  3086. m_hKartParachuteEntity->Release();
  3087. m_hKartParachuteEntity = NULL;
  3088. }
  3089. if ( !InCond( TF_COND_HALLOWEEN_KART ) )
  3090. {
  3091. IGameEvent *event = gameeventmanager->CreateEvent( "parachute_holster" );
  3092. if ( event )
  3093. {
  3094. event->SetInt( "index", m_pOuter->entindex() );
  3095. gameeventmanager->FireEventClientSide( event );
  3096. }
  3097. }
  3098. #endif
  3099. }
  3100. //-----------------------------------------------------------------------------
  3101. // Purpose:
  3102. //-----------------------------------------------------------------------------
  3103. void CTFPlayerShared::OnAddMadMilk( void )
  3104. {
  3105. #ifdef GAME_DLL
  3106. Assert( InCond( TF_COND_MAD_MILK ) );
  3107. // Check for the attribute that extends duration on successive hits
  3108. if ( m_ConditionData[TF_COND_MAD_MILK].m_bPrevActive )
  3109. {
  3110. CBaseEntity *pProvider = GetConditionProvider( TF_COND_MAD_MILK );
  3111. if ( pProvider )
  3112. {
  3113. int iMadMilkSyringes = 0;
  3114. CALL_ATTRIB_HOOK_INT_ON_OTHER( pProvider, iMadMilkSyringes, mad_milk_syringes );
  3115. if ( iMadMilkSyringes )
  3116. {
  3117. float flDuration = GetConditionDuration( TF_COND_MAD_MILK ) + 0.5f;
  3118. SetConditionDuration( TF_COND_MAD_MILK, Min( flDuration , 4.f ) );
  3119. }
  3120. }
  3121. }
  3122. #else
  3123. if ( !m_pOuter->m_pMilkEffect )
  3124. {
  3125. m_pOuter->m_pMilkEffect = m_pOuter->ParticleProp()->Create( "peejar_drips_milk", PATTACH_ABSORIGIN_FOLLOW );
  3126. }
  3127. m_pOuter->ParticleProp()->AddControlPoint( m_pOuter->m_pMilkEffect, 1, m_pOuter, PATTACH_ABSORIGIN_FOLLOW );
  3128. // if ( m_pOuter->IsLocalPlayer() )
  3129. // {
  3130. // IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_MILK, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  3131. // if ( !IsErrorMaterial( pMaterial ) )
  3132. // {
  3133. // view->SetScreenOverlayMaterial( pMaterial );
  3134. // }
  3135. // }
  3136. #endif
  3137. }
  3138. //-----------------------------------------------------------------------------
  3139. // Purpose:
  3140. //-----------------------------------------------------------------------------
  3141. void CTFPlayerShared::OnRemoveMadMilk( void )
  3142. {
  3143. #ifdef CLIENT_DLL
  3144. if ( m_pOuter->m_pMilkEffect )
  3145. {
  3146. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pMilkEffect );
  3147. m_pOuter->m_pMilkEffect = NULL;
  3148. }
  3149. if ( m_pOuter->IsLocalPlayer() )
  3150. {
  3151. // IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  3152. // if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_MILK ) )
  3153. // {
  3154. // view->SetScreenOverlayMaterial( NULL );
  3155. // }
  3156. }
  3157. #endif
  3158. }
  3159. #ifdef CLIENT_DLL
  3160. //-----------------------------------------------------------------------------
  3161. // Purpose:
  3162. //-----------------------------------------------------------------------------
  3163. CTFPlayerShared::taunt_particle_state_t CTFPlayerShared::GetClientTauntParticleDesiredState() const
  3164. {
  3165. const itemid_t unTauntSourceItemID = GetTauntSourceItemID();
  3166. if ( unTauntSourceItemID != INVALID_ITEM_ID )
  3167. {
  3168. CSteamID steamIDForPlayer;
  3169. m_pOuter->GetSteamID( &steamIDForPlayer );
  3170. CPlayerInventory *pInventory = InventoryManager()->GetInventoryForAccount( steamIDForPlayer.GetAccountID() );
  3171. CEconItemView *pTauntItem = pInventory ? pInventory->GetInventoryItemByItemID( unTauntSourceItemID ) : NULL;
  3172. if ( pTauntItem )
  3173. {
  3174. // do community_sparkle effect if this is a community item?
  3175. const int iQualityParticleType = pTauntItem->GetQualityParticleType();
  3176. if ( iQualityParticleType > 0 )
  3177. {
  3178. const attachedparticlesystem_t *pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iQualityParticleType );
  3179. if ( pParticleSystem )
  3180. {
  3181. return taunt_particle_state_t( pParticleSystem->pszSystemName, pParticleSystem->fRefireTime );
  3182. }
  3183. }
  3184. static CSchemaAttributeDefHandle pAttrDef_OnTauntAttachParticleIndex( "on taunt attach particle index" );
  3185. uint32 unUnusualEffectIndex = 0;
  3186. if ( pTauntItem->FindAttribute( pAttrDef_OnTauntAttachParticleIndex, &unUnusualEffectIndex ) && unUnusualEffectIndex > 0 )
  3187. {
  3188. const attachedparticlesystem_t *pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( unUnusualEffectIndex );
  3189. if ( pParticleSystem )
  3190. {
  3191. // TF Team Color Particles
  3192. if ( m_pOuter->GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_red" ) )
  3193. {
  3194. static char pBlue[256];
  3195. V_StrSubst( pParticleSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 );
  3196. pParticleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pBlue );
  3197. }
  3198. else if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_blue" ) )
  3199. {
  3200. // Guard against accidentally giving out the blue team color (support tool)
  3201. static char pRed[256];
  3202. V_StrSubst( pParticleSystem->pszSystemName, "_teamcolor_blue", "_teamcolor_red", pRed, 256 );
  3203. pParticleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pRed );
  3204. }
  3205. return taunt_particle_state_t( pParticleSystem->pszSystemName, pParticleSystem->fRefireTime );
  3206. }
  3207. }
  3208. for ( int i=0; i<m_pOuter->GetNumWearables(); ++i )
  3209. {
  3210. C_EconWearable *pWearable = m_pOuter->GetWearable( i );
  3211. CEconItemView *pItem = pWearable && pWearable->GetAttributeContainer() && pWearable->GetAttributeContainer()->GetItem() ? pWearable->GetAttributeContainer()->GetItem() : NULL;
  3212. // check for Unusual Cap def index (1173)
  3213. if ( pItem && pItem->GetItemDefIndex() == 1173 && pItem->FindAttribute( pAttrDef_OnTauntAttachParticleIndex, &unUnusualEffectIndex ) && unUnusualEffectIndex > 0 )
  3214. {
  3215. const attachedparticlesystem_t *pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( unUnusualEffectIndex );
  3216. if ( pParticleSystem )
  3217. {
  3218. // TF Team Color Particles
  3219. if ( m_pOuter->GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_red" ) )
  3220. {
  3221. static char pBlue[256];
  3222. V_StrSubst( pParticleSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 );
  3223. pParticleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pBlue );
  3224. }
  3225. else if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_blue" ) )
  3226. {
  3227. // Guard against accidentally giving out the blue team color (support tool)
  3228. static char pRed[256];
  3229. V_StrSubst( pParticleSystem->pszSystemName, "_teamcolor_blue", "_teamcolor_red", pRed, 256 );
  3230. pParticleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pRed );
  3231. }
  3232. return taunt_particle_state_t( pParticleSystem->pszSystemName, pParticleSystem->fRefireTime );
  3233. }
  3234. }
  3235. }
  3236. }
  3237. }
  3238. return taunt_particle_state_t( NULL, 0.0f );
  3239. }
  3240. //-----------------------------------------------------------------------------
  3241. // Purpose:
  3242. //-----------------------------------------------------------------------------
  3243. void CTFPlayerShared::FireClientTauntParticleEffects()
  3244. {
  3245. taunt_particle_state_t TauntParticleState = GetClientTauntParticleDesiredState();
  3246. if ( TauntParticleState.first )
  3247. {
  3248. if ( m_pOuter->m_pTauntEffect )
  3249. {
  3250. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pTauntEffect );
  3251. }
  3252. if ( !m_pOuter->GetTauntEconItemView() )
  3253. return;
  3254. if ( !m_pOuter->GetTauntEconItemView()->GetStaticData() )
  3255. return;
  3256. if ( !m_pOuter->GetTauntEconItemView()->GetStaticData()->GetTauntData() )
  3257. return;
  3258. const char *pszAttachment = m_pOuter->GetTauntEconItemView()->GetStaticData()->GetTauntData()->GetParticleAttachment();
  3259. int iAttachment = pszAttachment ? m_pOuter->LookupAttachment( pszAttachment ) : INVALID_PARTICLE_ATTACHMENT;
  3260. m_pOuter->m_pTauntEffect = m_pOuter->ParticleProp()->Create( TauntParticleState.first, iAttachment != INVALID_PARTICLE_ATTACHMENT ? PATTACH_POINT_FOLLOW : PATTACH_ABSORIGIN_FOLLOW, iAttachment, vec3_origin );
  3261. if ( TauntParticleState.second > 0.0f )
  3262. {
  3263. m_flTauntParticleRefireTime = gpGlobals->curtime + TauntParticleState.second;
  3264. }
  3265. }
  3266. }
  3267. #endif // CLIENT_DLL
  3268. //-----------------------------------------------------------------------------
  3269. // Purpose:
  3270. //-----------------------------------------------------------------------------
  3271. void CTFPlayerShared::OnAddTaunting( void )
  3272. {
  3273. CTFWeaponBase *pWpn = m_pOuter->GetActiveTFWeapon();
  3274. if ( pWpn )
  3275. {
  3276. // cancel any reload in progress.
  3277. pWpn->AbortReload();
  3278. // Check for taunt healing.
  3279. if ( GetTauntIndex() == TAUNT_BASE_WEAPON )
  3280. {
  3281. int iAOEHeal = 0;
  3282. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWpn, iAOEHeal, enables_aoe_heal );
  3283. if ( iAOEHeal == 1 )
  3284. {
  3285. Heal_Radius( true );
  3286. }
  3287. }
  3288. }
  3289. // Unzoom if we are a sniper zoomed!
  3290. InstantlySniperUnzoom();
  3291. if ( ( m_pOuter->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) || m_pOuter->IsPlayerClass( TF_CLASS_SCOUT ) ) && GetTauntIndex() == TAUNT_BASE_WEAPON )
  3292. {
  3293. CTFLunchBox *pLunchBox = dynamic_cast <CTFLunchBox *> ( pWpn );
  3294. if ( pLunchBox )
  3295. {
  3296. pLunchBox->DrainAmmo();
  3297. }
  3298. }
  3299. #ifdef GAME_DLL
  3300. m_pOuter->PlayWearableAnimsForPlaybackEvent( WAP_START_TAUNTING );
  3301. #else
  3302. FireClientTauntParticleEffects();
  3303. #endif
  3304. }
  3305. //-----------------------------------------------------------------------------
  3306. // Purpose:
  3307. //-----------------------------------------------------------------------------
  3308. void CTFPlayerShared::OnRemoveTaunting( void )
  3309. {
  3310. #ifdef GAME_DLL
  3311. #ifdef STAGING_ONLY
  3312. if ( !m_pOuter->m_hTauntScene.Get() )
  3313. {
  3314. Warning( "Why do we not have taunt scene?\n" );
  3315. }
  3316. #endif
  3317. m_pOuter->StopTaunt();
  3318. if ( IsControlStunned() )
  3319. {
  3320. m_pOuter->SetAbsAngles( m_pOuter->m_angTauntCamera );
  3321. m_pOuter->SetLocalAngles( m_pOuter->m_angTauntCamera );
  3322. }
  3323. #endif // GAME_DLL
  3324. // Stop aoe healing if it's active.
  3325. Heal_Radius( false );
  3326. // We're done taunting, our weapons are not being repurposed anymore
  3327. for ( int i = 0; i < m_pOuter->WeaponCount(); i++)
  3328. {
  3329. CTFWeaponBase *pWpn = ( CTFWeaponBase *) m_pOuter->GetWeapon(i);
  3330. if ( !pWpn )
  3331. continue;
  3332. pWpn->SetIsBeingRepurposedForTaunt( false );
  3333. }
  3334. #ifdef GAME_DLL
  3335. // Switch to our melee weapon, if we are at the end of a type 2 lunchbox taunt.
  3336. if ( m_bBiteEffectWasApplied && InCond( TF_COND_CANNOT_SWITCH_FROM_MELEE ) )
  3337. {
  3338. CBaseCombatWeapon *pWpn = m_pOuter->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
  3339. if ( pWpn )
  3340. {
  3341. m_pOuter->Weapon_Switch( pWpn );
  3342. }
  3343. else
  3344. {
  3345. // Safety net
  3346. RemoveCond( TF_COND_ENERGY_BUFF );
  3347. RemoveCond( TF_COND_CANNOT_SWITCH_FROM_MELEE );
  3348. }
  3349. }
  3350. m_bBiteEffectWasApplied = false;
  3351. if ( m_pOuter->m_hTauntItem != NULL )
  3352. {
  3353. // destroy the item we were showing off
  3354. UTIL_Remove( m_pOuter->m_hTauntItem );
  3355. m_pOuter->m_hTauntItem = NULL;
  3356. }
  3357. m_pOuter->ClearTauntAttack();
  3358. m_pOuter->PlayWearableAnimsForPlaybackEvent( WAP_STOP_TAUNTING );
  3359. m_pOuter->HandleWeaponSlotAfterTaunt();
  3360. #else
  3361. CSteamID steamIDForPlayer;
  3362. m_pOuter->GetSteamID( &steamIDForPlayer );
  3363. int nMapDonationAmount = MapInfo_GetDonationAmount( steamIDForPlayer.GetAccountID(), engine->GetLevelName() );
  3364. m_pOuter->SetFootStamps( nMapDonationAmount );
  3365. if ( m_pOuter->m_pTauntEffect )
  3366. {
  3367. m_pOuter->ParticleProp()->StopEmissionAndDestroyImmediately( m_pOuter->m_pTauntEffect );
  3368. m_pOuter->m_pTauntEffect = NULL;
  3369. }
  3370. m_flTauntParticleRefireTime = 0.0f;
  3371. #endif
  3372. m_pOuter->m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  3373. // when we stop taunting, make sure active weapon is visible
  3374. if ( m_pOuter->GetActiveWeapon() )
  3375. {
  3376. m_pOuter->GetActiveWeapon()->SetWeaponVisible( true );
  3377. }
  3378. }
  3379. //-----------------------------------------------------------------------------
  3380. // Purpose:
  3381. //-----------------------------------------------------------------------------
  3382. void CTFPlayerShared::OnAddBleeding( void )
  3383. {
  3384. #ifdef CLIENT_DLL
  3385. if ( m_pOuter->IsLocalPlayer() )
  3386. {
  3387. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_BLEED, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  3388. if ( !IsErrorMaterial( pMaterial ) )
  3389. {
  3390. view->SetScreenOverlayMaterial( pMaterial );
  3391. }
  3392. }
  3393. #else
  3394. // We should have at least one bleed entry
  3395. Assert( m_PlayerBleeds.Count() );
  3396. #endif
  3397. }
  3398. //-----------------------------------------------------------------------------
  3399. // Purpose:
  3400. //-----------------------------------------------------------------------------
  3401. void CTFPlayerShared::OnRemoveBleeding( void )
  3402. {
  3403. #ifdef CLIENT_DLL
  3404. if ( m_pOuter->IsLocalPlayer() )
  3405. {
  3406. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  3407. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_BLEED ) )
  3408. {
  3409. view->SetScreenOverlayMaterial( NULL );
  3410. }
  3411. }
  3412. #else
  3413. m_PlayerBleeds.RemoveAll();
  3414. #endif
  3415. }
  3416. const char* CTFPlayerShared::GetSoldierBuffEffectName( void )
  3417. {
  3418. if ( TFGameRules()->IsMannVsMachineMode() )
  3419. {
  3420. if ( m_pOuter->GetTeamNumber() == TF_TEAM_BLUE )
  3421. {
  3422. // MVM robot version has fewer particles. Helps keep the framerate up.
  3423. return "soldierbuff_mvm";
  3424. }
  3425. else
  3426. {
  3427. return "soldierbuff_red_soldier";
  3428. }
  3429. }
  3430. else
  3431. {
  3432. if ( m_pOuter->GetTeamNumber() == TF_TEAM_BLUE )
  3433. {
  3434. return "soldierbuff_blue_soldier";
  3435. }
  3436. else
  3437. {
  3438. return "soldierbuff_red_soldier";
  3439. }
  3440. }
  3441. }
  3442. //-----------------------------------------------------------------------------
  3443. // Purpose:
  3444. //-----------------------------------------------------------------------------
  3445. void CTFPlayerShared::OnAddSoldierOffensiveBuff( void )
  3446. {
  3447. #ifdef CLIENT_DLL
  3448. const char* strBuffName = GetSoldierBuffEffectName();
  3449. if ( !m_pOuter->m_pSoldierOffensiveBuffEffect )
  3450. {
  3451. m_pOuter->m_pSoldierOffensiveBuffEffect = m_pOuter->ParticleProp()->Create( strBuffName, PATTACH_ABSORIGIN_FOLLOW );
  3452. }
  3453. #endif
  3454. }
  3455. void CTFPlayerShared::OnRemoveSoldierOffensiveBuff( void )
  3456. {
  3457. #ifdef CLIENT_DLL
  3458. if ( m_pOuter->m_pSoldierOffensiveBuffEffect )
  3459. {
  3460. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSoldierOffensiveBuffEffect );
  3461. m_pOuter->m_pSoldierOffensiveBuffEffect = NULL;
  3462. }
  3463. #endif
  3464. }
  3465. void CTFPlayerShared::OnAddSoldierDefensiveBuff( void )
  3466. {
  3467. #ifdef CLIENT_DLL
  3468. const char* strBuffName = GetSoldierBuffEffectName();
  3469. if ( !m_pOuter->m_pSoldierDefensiveBuffEffect )
  3470. {
  3471. m_pOuter->m_pSoldierDefensiveBuffEffect = m_pOuter->ParticleProp()->Create( strBuffName, PATTACH_ABSORIGIN_FOLLOW );
  3472. }
  3473. #endif
  3474. }
  3475. void CTFPlayerShared::OnRemoveSoldierDefensiveBuff( void )
  3476. {
  3477. #ifdef CLIENT_DLL
  3478. if ( m_pOuter->m_pSoldierDefensiveBuffEffect )
  3479. {
  3480. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSoldierDefensiveBuffEffect );
  3481. m_pOuter->m_pSoldierDefensiveBuffEffect = NULL;
  3482. }
  3483. #endif
  3484. }
  3485. void CTFPlayerShared::OnAddSoldierOffensiveHealthRegenBuff( void )
  3486. {
  3487. #ifdef GAME_DLL
  3488. AddCond( TF_COND_SPEED_BOOST );
  3489. #else
  3490. const char* strBuffName = GetSoldierBuffEffectName();
  3491. if ( !m_pOuter->m_pSoldierOffensiveHealthRegenBuffEffect )
  3492. {
  3493. m_pOuter->m_pSoldierOffensiveHealthRegenBuffEffect = m_pOuter->ParticleProp()->Create( strBuffName, PATTACH_ABSORIGIN_FOLLOW );
  3494. }
  3495. #endif
  3496. }
  3497. void CTFPlayerShared::OnRemoveSoldierOffensiveHealthRegenBuff( void )
  3498. {
  3499. #ifdef GAME_DLL
  3500. RemoveCond( TF_COND_SPEED_BOOST );
  3501. #else
  3502. if ( m_pOuter->m_pSoldierOffensiveHealthRegenBuffEffect )
  3503. {
  3504. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSoldierOffensiveHealthRegenBuffEffect );
  3505. m_pOuter->m_pSoldierOffensiveHealthRegenBuffEffect = NULL;
  3506. }
  3507. #endif
  3508. }
  3509. void CTFPlayerShared::OnAddSoldierNoHealingDamageBuff( void )
  3510. {
  3511. #ifdef CLIENT_DLL
  3512. const char* strBuffName = GetSoldierBuffEffectName();
  3513. if ( !m_pOuter->m_pSoldierNoHealingDamageBuffEffect )
  3514. {
  3515. m_pOuter->m_pSoldierNoHealingDamageBuffEffect = m_pOuter->ParticleProp()->Create( strBuffName, PATTACH_ABSORIGIN_FOLLOW );
  3516. }
  3517. #endif
  3518. }
  3519. void CTFPlayerShared::OnRemoveSoldierNoHealingDamageBuff( void )
  3520. {
  3521. #ifdef CLIENT_DLL
  3522. if ( m_pOuter->m_pSoldierNoHealingDamageBuffEffect )
  3523. {
  3524. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSoldierNoHealingDamageBuffEffect );
  3525. m_pOuter->m_pSoldierNoHealingDamageBuffEffect = NULL;
  3526. }
  3527. #endif
  3528. }
  3529. //-----------------------------------------------------------------------------
  3530. // Purpose:
  3531. //-----------------------------------------------------------------------------
  3532. void CTFPlayerShared::OnAddOffenseBuff( void )
  3533. {
  3534. OnAddSoldierOffensiveBuff();
  3535. }
  3536. //-----------------------------------------------------------------------------
  3537. // Purpose:
  3538. //-----------------------------------------------------------------------------
  3539. void CTFPlayerShared::OnRemoveOffenseBuff( void )
  3540. {
  3541. OnRemoveSoldierOffensiveBuff();
  3542. }
  3543. //-----------------------------------------------------------------------------
  3544. // Purpose:
  3545. //-----------------------------------------------------------------------------
  3546. void CTFPlayerShared::OnAddDefenseBuff( void )
  3547. {
  3548. OnAddSoldierDefensiveBuff();
  3549. }
  3550. //-----------------------------------------------------------------------------
  3551. // Purpose:
  3552. //-----------------------------------------------------------------------------
  3553. void CTFPlayerShared::OnRemoveDefenseBuff( void )
  3554. {
  3555. OnRemoveSoldierDefensiveBuff();
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. // Purpose:
  3559. //-----------------------------------------------------------------------------
  3560. void CTFPlayerShared::OnAddOffenseHealthRegenBuff( void )
  3561. {
  3562. OnAddSoldierOffensiveHealthRegenBuff();
  3563. }
  3564. //-----------------------------------------------------------------------------
  3565. // Purpose:
  3566. //-----------------------------------------------------------------------------
  3567. void CTFPlayerShared::OnRemoveOffenseHealthRegenBuff( void )
  3568. {
  3569. OnRemoveSoldierOffensiveHealthRegenBuff();
  3570. }
  3571. //-----------------------------------------------------------------------------
  3572. // Purpose:
  3573. //-----------------------------------------------------------------------------
  3574. void CTFPlayerShared::OnAddNoHealingDamageBuff( void )
  3575. {
  3576. OnAddSoldierNoHealingDamageBuff();
  3577. }
  3578. //-----------------------------------------------------------------------------
  3579. // Purpose:
  3580. //-----------------------------------------------------------------------------
  3581. void CTFPlayerShared::OnRemoveNoHealingDamageBuff( void )
  3582. {
  3583. OnRemoveSoldierNoHealingDamageBuff();
  3584. }
  3585. //-----------------------------------------------------------------------------
  3586. // Purpose:
  3587. //-----------------------------------------------------------------------------
  3588. void CTFPlayerShared::OnAddSpeedBoost( bool IsNonCombat )
  3589. {
  3590. #ifdef CLIENT_DLL
  3591. const char* strBuffName = "speed_boost_trail";
  3592. if ( !m_pOuter->m_pSpeedBoostEffect )
  3593. {
  3594. // No speedlines at all for stealth or feign death
  3595. if ( !InCond( TF_COND_STEALTHED ) && !InCond(TF_COND_FEIGN_DEATH) )
  3596. {
  3597. m_pOuter->m_pSpeedBoostEffect = m_pOuter->ParticleProp()->Create( strBuffName, PATTACH_ABSORIGIN_FOLLOW );
  3598. }
  3599. }
  3600. // InCombat is played on the teleporter for all players to here
  3601. // "Building_Speedpad.BoostStart"
  3602. if ( !IsNonCombat && m_pOuter->IsLocalPlayer())
  3603. {
  3604. m_pOuter->EmitSound( "DisciplineDevice.PowerUp" );
  3605. }
  3606. #else // !CLIENT_DLL
  3607. m_pOuter->TeamFortress_SetSpeed();
  3608. #endif // CLIENT_DLL
  3609. }
  3610. //-----------------------------------------------------------------------------
  3611. // Purpose:
  3612. //-----------------------------------------------------------------------------
  3613. void CTFPlayerShared::OnRemoveSpeedBoost( bool IsNonCombat )
  3614. {
  3615. #ifdef CLIENT_DLL
  3616. if ( m_pOuter->m_pSpeedBoostEffect )
  3617. {
  3618. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSpeedBoostEffect );
  3619. m_pOuter->m_pSpeedBoostEffect = NULL;
  3620. }
  3621. if ( !IsNonCombat && m_pOuter->IsLocalPlayer() )
  3622. {
  3623. m_pOuter->EmitSound( "DisciplineDevice.PowerDown" );
  3624. }
  3625. else
  3626. {
  3627. m_pOuter->EmitSound( "Building_Speedpad.BoostStop" );
  3628. }
  3629. #else // !CLIENT_DLL
  3630. m_pOuter->TeamFortress_SetSpeed();
  3631. #endif // CLIENT_DLL
  3632. }
  3633. //-----------------------------------------------------------------------------
  3634. // Purpose: Applied to bots
  3635. //-----------------------------------------------------------------------------
  3636. void CTFPlayerShared::OnAddSapped( void )
  3637. {
  3638. #ifdef CLIENT_DLL
  3639. if ( !m_pOuter->m_pSappedPlayerEffect )
  3640. {
  3641. const char* szParticle = "sapper_sentry1_fx";
  3642. m_pOuter->m_pSappedPlayerEffect = m_pOuter->ParticleProp()->Create( szParticle, PATTACH_POINT_FOLLOW, "head" );
  3643. }
  3644. #endif // CLIENT_DLL
  3645. }
  3646. //-----------------------------------------------------------------------------
  3647. // Purpose:
  3648. //-----------------------------------------------------------------------------
  3649. void CTFPlayerShared::OnRemoveSapped( void )
  3650. {
  3651. #ifdef CLIENT_DLL
  3652. if ( m_pOuter->m_pSappedPlayerEffect )
  3653. {
  3654. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSappedPlayerEffect );
  3655. m_pOuter->m_pSappedPlayerEffect = NULL;
  3656. }
  3657. #endif // CLIENT_DLL
  3658. }
  3659. //-----------------------------------------------------------------------------
  3660. // Purpose: Applied to bots
  3661. //-----------------------------------------------------------------------------
  3662. void CTFPlayerShared::OnAddReprogrammed( void )
  3663. {
  3664. #ifdef STAGING_ONLY
  3665. #ifdef GAME_DLL
  3666. CTFBot *pBot = ToTFBot( m_pOuter );
  3667. if ( pBot )
  3668. {
  3669. pBot->ChangeTeam( GetEnemyTeam( pBot->GetTeamNumber() ), false, true );
  3670. pBot->SetMission( CTFBot::MISSION_REPROGRAMMED );
  3671. pBot->Update();
  3672. pBot->MarkAsMissionEnemy();
  3673. }
  3674. #endif // GAME_DLL
  3675. #endif // STAGING_ONLY
  3676. }
  3677. //-----------------------------------------------------------------------------
  3678. // Purpose:
  3679. //-----------------------------------------------------------------------------
  3680. void CTFPlayerShared::OnRemoveReprogrammed( void )
  3681. {
  3682. }
  3683. void CTFPlayerShared::OnAddDisguisedAsDispenser( void )
  3684. {
  3685. m_pOuter->TeamFortress_SetSpeed();
  3686. }
  3687. //-----------------------------------------------------------------------------
  3688. // Purpose:
  3689. //-----------------------------------------------------------------------------
  3690. void CTFPlayerShared::OnAddHalloweenBombHead( void )
  3691. {
  3692. #ifdef CLIENT_DLL
  3693. m_pOuter->HalloweenBombHeadUpdate();
  3694. m_pOuter->CreateBombonomiconHint();
  3695. #else
  3696. if ( InCond( TF_COND_HALLOWEEN_KART ) )
  3697. {
  3698. RemoveAttributeFromPlayer( "head scale" );
  3699. }
  3700. #endif
  3701. }
  3702. //-----------------------------------------------------------------------------
  3703. // Purpose:
  3704. //-----------------------------------------------------------------------------
  3705. void CTFPlayerShared::OnRemoveHalloweenBombHead( void )
  3706. {
  3707. #ifdef CLIENT_DLL
  3708. m_pOuter->HalloweenBombHeadUpdate();
  3709. m_pOuter->DestroyBombonomiconHint();
  3710. #else
  3711. if ( InCond( TF_COND_HALLOWEEN_KART ) )
  3712. {
  3713. ApplyAttributeToPlayer( "head scale", 3.f );
  3714. }
  3715. if ( m_pOuter->IsAlive() )
  3716. {
  3717. m_pOuter->MerasmusPlayerBombExplode( false );
  3718. Vector vecOrigin = m_pOuter->GetAbsOrigin();
  3719. // explode has a small force but we want to increase it
  3720. if ( InCond ( TF_COND_HALLOWEEN_KART ) )
  3721. {
  3722. if ( !m_pOuter->GetKartBombHeadTarget() )
  3723. {
  3724. m_pOuter->AddHalloweenKartPushEvent( m_pOuter, NULL, NULL, Vector( 0, 0, 100 ), 50 );
  3725. }
  3726. m_pOuter->SetKartBombHeadTarget( NULL );
  3727. }
  3728. else if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) )
  3729. {
  3730. TFGameRules()->PushAllPlayersAway( vecOrigin, 150, 400, TEAM_ANY );
  3731. }
  3732. // Particle
  3733. CPVSFilter filter( vecOrigin );
  3734. TE_TFParticleEffect( filter, 0.0, "bombinomicon_burningdebris", vecOrigin, vec3_angle );
  3735. }
  3736. #endif // GAME_DLL
  3737. }
  3738. void CTFPlayerShared::OnAddHalloweenThriller( void )
  3739. {
  3740. #ifdef CLIENT_DLL
  3741. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  3742. if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  3743. {
  3744. if ( pLocalPlayer == m_pOuter )
  3745. {
  3746. m_pOuter->EmitSound( "Halloween.dance_howl" );
  3747. m_pOuter->EmitSound( "Halloween.dance_loop" );
  3748. }
  3749. }
  3750. #endif
  3751. }
  3752. void CTFPlayerShared::OnRemoveHalloweenThriller( void )
  3753. {
  3754. #ifdef CLIENT_DLL
  3755. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  3756. if ( !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  3757. {
  3758. if ( pLocalPlayer == m_pOuter )
  3759. {
  3760. m_pOuter->StopSound( "Halloween.dance_loop" );
  3761. }
  3762. }
  3763. #else
  3764. // If this is hightower, players will be healing themselves while dancing
  3765. StopHealing( m_pOuter );
  3766. #endif
  3767. }
  3768. void CTFPlayerShared::OnAddRadiusHealOnDamage( void )
  3769. {
  3770. Heal_Radius( true );
  3771. }
  3772. void CTFPlayerShared::OnRemoveRadiusHealOnDamage( void )
  3773. {
  3774. Heal_Radius( false );
  3775. }
  3776. //-----------------------------------------------------------------------------
  3777. // Purpose:
  3778. //-----------------------------------------------------------------------------
  3779. void CTFPlayerShared::OnAddMarkedForDeathSilent( void )
  3780. {
  3781. #ifdef CLIENT_DLL
  3782. m_pOuter->UpdatedMarkedForDeathEffect();
  3783. #endif
  3784. }
  3785. //-----------------------------------------------------------------------------
  3786. void CTFPlayerShared::OnRemoveMarkedForDeathSilent( void )
  3787. {
  3788. #ifdef CLIENT_DLL
  3789. m_pOuter->UpdatedMarkedForDeathEffect();
  3790. #endif
  3791. }
  3792. void CTFPlayerShared::OnRemoveDisguisedAsDispenser( void )
  3793. {
  3794. m_pOuter->TeamFortress_SetSpeed();
  3795. }
  3796. #ifdef STAGING_ONLY
  3797. //-----------------------------------------------------------------------------
  3798. // Purpose:
  3799. //-----------------------------------------------------------------------------
  3800. void CTFPlayerShared::OnAddRocketPack( void )
  3801. {
  3802. #ifdef CLIENT_DLL
  3803. if ( !m_pOuter->m_pRocketPackEffect )
  3804. {
  3805. const char* szParticle = "rocketbackblast";
  3806. m_pOuter->m_pRocketPackEffect = m_pOuter->ParticleProp()->Create( szParticle, PATTACH_POINT_FOLLOW, "flag" );
  3807. }
  3808. #endif // CLIENT_DLL
  3809. }
  3810. //-----------------------------------------------------------------------------
  3811. // Purpose:
  3812. //-----------------------------------------------------------------------------
  3813. void CTFPlayerShared::OnRemoveRocketPack( void )
  3814. {
  3815. #ifdef CLIENT_DLL
  3816. if ( m_pOuter->m_pRocketPackEffect )
  3817. {
  3818. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pRocketPackEffect );
  3819. m_pOuter->m_pRocketPackEffect = NULL;
  3820. }
  3821. #endif // CLIENT_DLL
  3822. }
  3823. //-----------------------------------------------------------------------------
  3824. // Purpose:
  3825. //-----------------------------------------------------------------------------
  3826. void CTFPlayerShared::UpdateRocketPack( void )
  3827. {
  3828. if ( !m_pOuter->IsPlayerClass( TF_CLASS_PYRO ) )
  3829. return;
  3830. if ( InCond( TF_COND_ROCKETPACK ) )
  3831. {
  3832. #ifdef GAME_DLL
  3833. // Check for landing
  3834. if ( m_pOuter->GetFlags() & FL_ONGROUND )
  3835. {
  3836. RemoveCond( TF_COND_ROCKETPACK );
  3837. }
  3838. #endif
  3839. }
  3840. }
  3841. //-----------------------------------------------------------------------------
  3842. // Purpose:
  3843. //-----------------------------------------------------------------------------
  3844. void CTFPlayerShared::ApplyRocketPackStun( float flStunDuration )
  3845. {
  3846. if ( flStunDuration < 0.1f )
  3847. return;
  3848. #ifdef GAME_DLL
  3849. const int nMaxEnts = 24;
  3850. CBaseEntity *pObjects[ nMaxEnts ];
  3851. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, m_pOuter->GetAbsOrigin(), 192.f, FL_CLIENT );
  3852. for ( int i = 0; i < nCount; i++ )
  3853. {
  3854. if ( !pObjects[i] )
  3855. continue;
  3856. if ( !pObjects[i]->IsAlive() )
  3857. continue;
  3858. if ( m_pOuter->InSameTeam( pObjects[i] ) )
  3859. continue;
  3860. if ( !m_pOuter->FVisible( pObjects[i], MASK_OPAQUE ) )
  3861. continue;
  3862. CTFPlayer *pTFPlayer = static_cast< CTFPlayer* >( pObjects[i] );
  3863. if ( !pTFPlayer )
  3864. continue;
  3865. pTFPlayer->m_Shared.StunPlayer( flStunDuration, 0.75f, TF_STUN_CONTROLS );
  3866. }
  3867. #endif // GAME_DLL
  3868. }
  3869. //-----------------------------------------------------------------------------
  3870. // Purpose:
  3871. //-----------------------------------------------------------------------------
  3872. bool CTFPlayerShared::CanBuildSpyTraps( void )
  3873. {
  3874. if ( !m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  3875. return false;
  3876. if ( m_pOuter->IsBot() )
  3877. return false;
  3878. if ( TFGameRules()->IsMannVsMachineMode() )
  3879. return true;
  3880. int iTraps = 0;
  3881. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iTraps, ability_spy_traps );
  3882. return ( iTraps > 0 );
  3883. }
  3884. #endif // STAGING_ONLY
  3885. #ifdef CLIENT_DLL
  3886. static void AddUberScreenEffect( const CTFPlayer* pPlayer )
  3887. {
  3888. // Add the uber effect onto the local player's screen
  3889. if ( pPlayer && pPlayer->IsLocalPlayer() )
  3890. {
  3891. const char *pEffectName = NULL;
  3892. if ( pPlayer->GetTeamNumber() == TF_TEAM_RED )
  3893. {
  3894. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED;
  3895. }
  3896. else
  3897. {
  3898. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE;
  3899. }
  3900. IMaterial *pMaterial = materials->FindMaterial( pEffectName, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  3901. if ( !IsErrorMaterial( pMaterial ) )
  3902. {
  3903. view->SetScreenOverlayMaterial( pMaterial );
  3904. }
  3905. }
  3906. }
  3907. static void RemoveUberScreenEffect( const CTFPlayer* pPlayer )
  3908. {
  3909. if ( pPlayer && pPlayer->IsLocalPlayer() )
  3910. {
  3911. // only remove the overlay if it is an invuln material
  3912. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  3913. if ( pMaterial &&
  3914. ( FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE ) ||
  3915. FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED ) ) )
  3916. {
  3917. view->SetScreenOverlayMaterial( NULL );
  3918. }
  3919. }
  3920. }
  3921. static const char* s_pszRedResistOverheadEffectName[] =
  3922. {
  3923. "vaccinator_red_buff1",
  3924. "vaccinator_red_buff2",
  3925. "vaccinator_red_buff3",
  3926. };
  3927. static const char* s_pszBlueResistOverheadEffectName[] =
  3928. {
  3929. "vaccinator_blue_buff1",
  3930. "vaccinator_blue_buff2",
  3931. "vaccinator_blue_buff3",
  3932. };
  3933. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszRedResistOverheadEffectName ) == MEDIGUN_NUM_RESISTS && ARRAYSIZE( s_pszBlueResistOverheadEffectName ) == MEDIGUN_NUM_RESISTS );
  3934. static void AddResistParticle( CTFPlayer* pPlayer, medigun_resist_types_t nResistType, ETFCond eYeildToCond = TF_COND_LAST )
  3935. {
  3936. // Don't spawn it over the local player's head
  3937. if ( !pPlayer || pPlayer->IsLocalPlayer() )
  3938. return;
  3939. // do not add if stealthed
  3940. if ( pPlayer->m_Shared.IsStealthed() )
  3941. return;
  3942. // Don't add this effect if the yield effect is passed in
  3943. if( eYeildToCond != TF_COND_LAST && pPlayer->m_Shared.InCond( eYeildToCond ) )
  3944. return;
  3945. if ( pPlayer->m_Shared.GetDisplayedTeam() == TF_TEAM_RED )
  3946. {
  3947. pPlayer->AddOverheadEffect( s_pszRedResistOverheadEffectName[ nResistType ] );
  3948. }
  3949. else
  3950. {
  3951. pPlayer->AddOverheadEffect( s_pszBlueResistOverheadEffectName[ nResistType ] );
  3952. }
  3953. }
  3954. static void RemoveResistParticle( CTFPlayer* pPlayer, medigun_resist_types_t nResistType )
  3955. {
  3956. if ( !pPlayer || pPlayer->IsLocalPlayer() )
  3957. return;
  3958. bool bKeep = false;
  3959. switch ( nResistType )
  3960. {
  3961. case MEDIGUN_BULLET_RESIST:
  3962. bKeep = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_BULLET_RESIST );
  3963. break;
  3964. case MEDIGUN_BLAST_RESIST:
  3965. bKeep = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_BLAST_RESIST );
  3966. break;
  3967. case MEDIGUN_FIRE_RESIST:
  3968. bKeep = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_FIRE_RESIST );
  3969. break;
  3970. default:
  3971. AssertMsg( 0, "Invalid medigun resist type" );
  3972. break;
  3973. }
  3974. // don't remove overhead effect if the uber's still active
  3975. if ( bKeep )
  3976. return;
  3977. if ( pPlayer->m_Shared.GetDisplayedTeam() == TF_TEAM_RED )
  3978. {
  3979. pPlayer->RemoveOverheadEffect( s_pszRedResistOverheadEffectName[ nResistType ], true );
  3980. }
  3981. else
  3982. {
  3983. pPlayer->RemoveOverheadEffect( s_pszBlueResistOverheadEffectName[ nResistType ], true );
  3984. }
  3985. }
  3986. static int GetResistShieldSkinForResistType( ETFCond eCond )
  3987. {
  3988. switch( eCond )
  3989. {
  3990. case TF_COND_MEDIGUN_UBER_BULLET_RESIST:
  3991. return 2;
  3992. case TF_COND_MEDIGUN_UBER_BLAST_RESIST:
  3993. return 3;
  3994. case TF_COND_MEDIGUN_UBER_FIRE_RESIST:
  3995. return 4;
  3996. default:
  3997. AssertMsg( 0, "Invalid condition passed into AddResistShield" );
  3998. return 0;
  3999. }
  4000. }
  4001. static void AddResistShield( C_LocalTempEntity** pShield, CTFPlayer* pPlayer, ETFCond eCond )
  4002. {
  4003. if( CBasePlayer::GetLocalPlayer() == pPlayer )
  4004. return;
  4005. // do not add if stealthed
  4006. if ( pPlayer->m_Shared.IsStealthed() )
  4007. return;
  4008. // Don't create a new shield if we already have one
  4009. if( *pShield )
  4010. return;
  4011. model_t *pModel = (model_t*) engine->LoadModel( "models/effects/resist_shield/resist_shield.mdl" );
  4012. (*pShield) = tempents->SpawnTempModel( pModel, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles(), Vector(0, 0, 0), 1, FTENT_NEVERDIE | FTENT_PLYRATTACHMENT );
  4013. if ( *pShield )
  4014. {
  4015. (*pShield)->ChangeTeam( pPlayer->m_Shared.GetDisplayedTeam() );
  4016. if( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pPlayer->GetTeamNumber() == TF_TEAM_BLUE )
  4017. {
  4018. (*pShield)->m_nSkin = GetResistShieldSkinForResistType( eCond );
  4019. }
  4020. else
  4021. {
  4022. (*pShield)->m_nSkin = ( pPlayer->m_Shared.GetDisplayedTeam() == TF_TEAM_RED ) ? 0 : 1;
  4023. }
  4024. (*pShield)->clientIndex = pPlayer->entindex();
  4025. (*pShield)->SetModelScale( pPlayer->GetModelScale() );
  4026. }
  4027. }
  4028. static void RemoveResistShield( C_LocalTempEntity** pShield, CTFPlayer* pPlayer )
  4029. {
  4030. if ( *pShield )
  4031. {
  4032. ETFCond eCond = TF_COND_INVALID;
  4033. // Check if we still have one of the other resist types on us
  4034. eCond = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_BULLET_RESIST ) ? TF_COND_MEDIGUN_UBER_BULLET_RESIST : eCond;
  4035. eCond = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_BLAST_RESIST ) ? TF_COND_MEDIGUN_UBER_BLAST_RESIST : eCond;
  4036. eCond = pPlayer->m_Shared.InCond( TF_COND_MEDIGUN_UBER_FIRE_RESIST ) ? TF_COND_MEDIGUN_UBER_FIRE_RESIST : eCond;
  4037. eCond = ( pPlayer->m_Shared.InCond( TF_COND_RUNE_RESIST ) && !pPlayer->m_Shared.IsStealthed() ) ? TF_COND_RUNE_RESIST : eCond;
  4038. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  4039. if ( pLocalPlayer )
  4040. {
  4041. eCond = ( pPlayer->IsEnemyPlayer() && pPlayer->m_Shared.InCond( TF_COND_RUNE_PLAGUE ) && pLocalPlayer->m_Shared.InCond( TF_COND_PLAGUE ) ) ? TF_COND_RUNE_PLAGUE : eCond;
  4042. }
  4043. // Still have one, don't remove the shield
  4044. if( eCond != TF_COND_INVALID )
  4045. {
  4046. // If we're in MvM, and we're one of the bots, change the shield color
  4047. if( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pPlayer->GetTeamNumber() == TF_TEAM_BLUE )
  4048. {
  4049. (*pShield)->m_nSkin = GetResistShieldSkinForResistType( eCond );
  4050. }
  4051. return;
  4052. }
  4053. else // No more bubble
  4054. {
  4055. (*pShield)->flags = FTENT_FADEOUT | FTENT_PLYRATTACHMENT;
  4056. (*pShield)->die = gpGlobals->curtime;
  4057. (*pShield)->fadeSpeed = 1.0f;
  4058. (*pShield) = NULL;
  4059. }
  4060. }
  4061. }
  4062. #endif // CLIENT_DLL
  4063. void CTFPlayerShared::OnAddMedEffectUberBulletResist( void )
  4064. {
  4065. #ifdef CLIENT_DLL
  4066. AddResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST );
  4067. AddUberScreenEffect( m_pOuter );
  4068. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_BULLET_RESIST );
  4069. #endif
  4070. }
  4071. void CTFPlayerShared::OnRemoveMedEffectUberBulletResist( void )
  4072. {
  4073. #ifdef CLIENT_DLL
  4074. RemoveResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST );
  4075. RemoveUberScreenEffect( m_pOuter );
  4076. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4077. OnAddMedEffectSmallBulletResist();
  4078. #endif
  4079. }
  4080. void CTFPlayerShared::OnAddMedEffectUberBlastResist( void )
  4081. {
  4082. #ifdef CLIENT_DLL
  4083. AddResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST );
  4084. AddUberScreenEffect( m_pOuter );
  4085. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_BLAST_RESIST );
  4086. #endif
  4087. }
  4088. void CTFPlayerShared::OnRemoveMedEffectUberBlastResist( void )
  4089. {
  4090. #ifdef CLIENT_DLL
  4091. RemoveResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST );
  4092. RemoveUberScreenEffect( m_pOuter );
  4093. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4094. OnAddMedEffectSmallBlastResist();
  4095. #endif
  4096. }
  4097. void CTFPlayerShared::OnAddMedEffectUberFireResist( void )
  4098. {
  4099. #ifdef CLIENT_DLL
  4100. AddResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST );
  4101. AddUberScreenEffect( m_pOuter );
  4102. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_FIRE_RESIST );
  4103. #endif
  4104. }
  4105. void CTFPlayerShared::OnRemoveMedEffectUberFireResist( void )
  4106. {
  4107. #ifdef CLIENT_DLL
  4108. RemoveResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST );
  4109. RemoveUberScreenEffect( m_pOuter );
  4110. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4111. OnAddMedEffectSmallFireResist();
  4112. #endif
  4113. }
  4114. void CTFPlayerShared::OnAddMedEffectSmallBulletResist( void )
  4115. {
  4116. #ifdef CLIENT_DLL
  4117. if( InCond( TF_COND_MEDIGUN_SMALL_BULLET_RESIST ) )
  4118. {
  4119. AddResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST, TF_COND_MEDIGUN_UBER_BULLET_RESIST );
  4120. }
  4121. #endif
  4122. }
  4123. void CTFPlayerShared::OnRemoveMedEffectSmallBulletResist( void )
  4124. {
  4125. #ifdef CLIENT_DLL
  4126. RemoveResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST );
  4127. #endif
  4128. }
  4129. void CTFPlayerShared::OnAddMedEffectSmallBlastResist( void )
  4130. {
  4131. #ifdef CLIENT_DLL
  4132. if( InCond( TF_COND_MEDIGUN_SMALL_BLAST_RESIST ) )
  4133. {
  4134. AddResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST, TF_COND_MEDIGUN_UBER_BLAST_RESIST );
  4135. }
  4136. #endif
  4137. }
  4138. void CTFPlayerShared::OnRemoveMedEffectSmallBlastResist( void )
  4139. {
  4140. #ifdef CLIENT_DLL
  4141. RemoveResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST );
  4142. #endif
  4143. }
  4144. void CTFPlayerShared::OnAddMedEffectSmallFireResist( void )
  4145. {
  4146. #ifdef CLIENT_DLL
  4147. if( InCond( TF_COND_MEDIGUN_SMALL_FIRE_RESIST ) )
  4148. {
  4149. AddResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST, TF_COND_MEDIGUN_UBER_FIRE_RESIST );
  4150. }
  4151. #endif
  4152. }
  4153. void CTFPlayerShared::OnRemoveMedEffectSmallFireResist( void )
  4154. {
  4155. #ifdef CLIENT_DLL
  4156. RemoveResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST );
  4157. #endif
  4158. }
  4159. void CTFPlayerShared::OnAddRuneResist( void )
  4160. {
  4161. #ifdef CLIENT_DLL
  4162. // Do use the condition bit here, it's passed along and is expected to be a cond.
  4163. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_RUNE_RESIST );
  4164. #endif
  4165. }
  4166. void CTFPlayerShared::OnRemoveRuneResist( void )
  4167. {
  4168. #ifdef CLIENT_DLL
  4169. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4170. #endif
  4171. }
  4172. void CTFPlayerShared::OnRemoveRuneKing( void )
  4173. {
  4174. #ifdef CLIENT_DLL
  4175. EndKingBuffRadiusEffect();
  4176. #endif
  4177. }
  4178. void CTFPlayerShared::OnAddGrapplingHookLatched( void )
  4179. {
  4180. m_pOuter->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_GRAPPLE_PULL_START );
  4181. }
  4182. void CTFPlayerShared::OnRemoveGrapplingHookLatched( void )
  4183. {
  4184. // DO NOTHING
  4185. }
  4186. void CTFPlayerShared::OnAddBulletImmune( void )
  4187. {
  4188. #ifdef CLIENT_DLL
  4189. AddResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST );
  4190. AddUberScreenEffect( m_pOuter );
  4191. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_BULLET_RESIST );
  4192. #endif
  4193. }
  4194. void CTFPlayerShared::OnRemoveBulletImmune( void )
  4195. {
  4196. #ifdef CLIENT_DLL
  4197. RemoveResistParticle( m_pOuter, MEDIGUN_BULLET_RESIST );
  4198. RemoveUberScreenEffect( m_pOuter );
  4199. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4200. #endif
  4201. }
  4202. void CTFPlayerShared::OnAddBlastImmune( void )
  4203. {
  4204. #ifdef CLIENT_DLL
  4205. AddResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST );
  4206. AddUberScreenEffect( m_pOuter );
  4207. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_BLAST_RESIST );
  4208. #endif
  4209. }
  4210. void CTFPlayerShared::OnRemoveBlastImmune( void )
  4211. {
  4212. #ifdef CLIENT_DLL
  4213. RemoveResistParticle( m_pOuter, MEDIGUN_BLAST_RESIST );
  4214. RemoveUberScreenEffect( m_pOuter );
  4215. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4216. #endif
  4217. }
  4218. void CTFPlayerShared::OnAddFireImmune( void )
  4219. {
  4220. #ifdef CLIENT_DLL
  4221. AddUberScreenEffect( m_pOuter );
  4222. if ( !m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  4223. {
  4224. AddResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST );
  4225. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_MEDIGUN_UBER_FIRE_RESIST );
  4226. }
  4227. #endif
  4228. }
  4229. void CTFPlayerShared::OnRemoveFireImmune( void )
  4230. {
  4231. #ifdef CLIENT_DLL
  4232. RemoveUberScreenEffect( m_pOuter );
  4233. if ( !m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  4234. {
  4235. RemoveResistParticle( m_pOuter, MEDIGUN_FIRE_RESIST );
  4236. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4237. }
  4238. #endif
  4239. }
  4240. void CTFPlayerShared::OnAddMVMBotRadiowave( void )
  4241. {
  4242. #ifdef CLIENT_DLL
  4243. if ( !m_pOuter->IsABot() )
  4244. return;
  4245. if ( !m_pOuter->m_pMVMBotRadiowave )
  4246. {
  4247. m_pOuter->m_pMVMBotRadiowave = m_pOuter->ParticleProp()->Create( "bot_radio_waves", PATTACH_POINT_FOLLOW, "head" );
  4248. }
  4249. #else
  4250. if ( !m_pOuter->IsBot() )
  4251. return;
  4252. StunPlayer( GetConditionDuration( TF_COND_MVM_BOT_STUN_RADIOWAVE ), 1.0, TF_STUN_BOTH | TF_STUN_NO_EFFECTS );
  4253. #endif
  4254. }
  4255. void CTFPlayerShared::OnRemoveMVMBotRadiowave( void )
  4256. {
  4257. #ifdef CLIENT_DLL
  4258. if ( !m_pOuter->IsABot() )
  4259. return;
  4260. if ( m_pOuter->m_pMVMBotRadiowave )
  4261. {
  4262. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pMVMBotRadiowave );
  4263. m_pOuter->m_pMVMBotRadiowave = NULL;
  4264. }
  4265. #endif
  4266. }
  4267. void CTFPlayerShared::OnAddHalloweenSpeedBoost( void )
  4268. {
  4269. #ifdef GAME_DLL
  4270. AddCond( TF_COND_SPEED_BOOST );
  4271. ApplyAttributeToPlayer( "halloween reload time decreased", 0.5f );
  4272. ApplyAttributeToPlayer( "halloween fire rate bonus", 0.5f );
  4273. ApplyAttributeToPlayer( "halloween increased jump height", 1.5f );
  4274. //
  4275. #endif
  4276. }
  4277. void CTFPlayerShared::OnRemoveHalloweenSpeedBoost( void )
  4278. {
  4279. #ifdef GAME_DLL
  4280. RemoveCond( TF_COND_SPEED_BOOST );
  4281. RemoveAttributeFromPlayer( "halloween reload time decreased" );
  4282. RemoveAttributeFromPlayer( "halloween fire rate bonus" );
  4283. RemoveAttributeFromPlayer( "halloween increased jump height" );
  4284. #endif
  4285. }
  4286. void CTFPlayerShared::OnAddHalloweenQuickHeal( void )
  4287. {
  4288. #ifdef GAME_DLL
  4289. AddCond( TF_COND_MEGAHEAL );
  4290. Heal( m_pOuter, 30.0f, 2.0f, 1.0f );
  4291. #endif
  4292. }
  4293. void CTFPlayerShared::OnRemoveHalloweenQuickHeal( void )
  4294. {
  4295. #ifdef GAME_DLL
  4296. RemoveCond( TF_COND_MEGAHEAL );
  4297. StopHealing( m_pOuter );
  4298. #endif
  4299. }
  4300. void CTFPlayerShared::OnAddHalloweenGiant( void )
  4301. {
  4302. #ifdef GAME_DLL
  4303. m_pOuter->SetModelScale( 2.f );
  4304. int nNewHP = tf_halloween_giant_health_scale.GetFloat() * m_pOuter->GetPlayerClass()->GetMaxHealth();
  4305. m_pOuter->SetHealth( nNewHP );
  4306. m_pOuter->SetMaxHealth( nNewHP );
  4307. #else
  4308. cam_idealdist.SetValue( 300.f );
  4309. cam_idealdistright.SetValue( 40.f );
  4310. #endif
  4311. }
  4312. void CTFPlayerShared::OnRemoveHalloweenGiant( void )
  4313. {
  4314. #ifdef GAME_DLL
  4315. m_pOuter->SetModelScale( 1.f );
  4316. int nNewHP = m_pOuter->GetPlayerClass()->GetMaxHealth();
  4317. m_pOuter->SetHealth( nNewHP );
  4318. m_pOuter->SetMaxHealth( nNewHP );
  4319. #else
  4320. cam_idealdist.SetValue( cam_idealdist.GetDefault() );
  4321. cam_idealdistright.SetValue( cam_idealdistright.GetDefault() );
  4322. #endif
  4323. }
  4324. void CTFPlayerShared::OnAddHalloweenTiny( void )
  4325. {
  4326. #ifdef GAME_DLL
  4327. m_pOuter->SetModelScale( 0.5f );
  4328. ApplyAttributeToPlayer( "voice pitch scale", 1.3f );
  4329. ApplyAttributeToPlayer( "head scale", 3.f );
  4330. #endif
  4331. }
  4332. void CTFPlayerShared::OnAddHalloweenGhostMode( void )
  4333. {
  4334. m_pOuter->SetGroundEntity( NULL );
  4335. m_pOuter->SetSolid( SOLID_NONE );
  4336. m_pOuter->SetSolidFlags( FSOLID_NOT_SOLID );
  4337. m_pOuter->AddFlag( FL_NOTARGET );
  4338. #ifdef GAME_DLL
  4339. CSingleUserRecipientFilter filter( m_pOuter );
  4340. if ( TFGameRules() )
  4341. {
  4342. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  4343. {
  4344. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_HOW_TO_CONTROL_GHOST );
  4345. }
  4346. else
  4347. {
  4348. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_HOW_TO_CONTROL_GHOST_NO_RESPAWN );
  4349. }
  4350. }
  4351. // The game rules listens for this event
  4352. IGameEvent *event = gameeventmanager->CreateEvent( "player_turned_to_ghost" );
  4353. if ( event )
  4354. {
  4355. event->SetInt( "userid", m_pOuter->GetUserID() );
  4356. gameeventmanager->FireEvent( event );
  4357. }
  4358. // Push them up a little bit
  4359. Vector vecNewVel( 0, 0, 40 );
  4360. m_pOuter->Teleport( NULL, NULL, &vecNewVel );
  4361. if ( m_pOuter->GetActiveWeapon() )
  4362. {
  4363. m_pOuter->GetActiveWeapon()->SendViewModelAnim( ACT_IDLE );
  4364. m_pOuter->GetActiveWeapon()->Holster();
  4365. }
  4366. m_pOuter->SetActiveWeapon( NULL );
  4367. CBaseObject * pCarriedObj = GetCarriedObject();
  4368. if ( pCarriedObj )
  4369. {
  4370. pCarriedObj->DetonateObject();
  4371. }
  4372. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  4373. if ( pSpellBook )
  4374. {
  4375. pSpellBook->ClearSpell();
  4376. }
  4377. #else
  4378. // Go thirdperson
  4379. SetAppropriateCamera( m_pOuter );
  4380. Color color;
  4381. m_pOuter->GetTeamColor( color );
  4382. m_pOuter->SetRenderColor( color.r(), color.g(), color.b() );
  4383. #endif
  4384. }
  4385. void CTFPlayerShared::OnAddHalloweenKartDash()
  4386. {
  4387. m_pOuter->SetFOV( m_pOuter, 110.f, 1.f, 0.f );
  4388. m_pOuter->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_ACTION_DASH );
  4389. #ifdef CLIENT_DLL
  4390. #else // CLIENT_DLL
  4391. m_pOuter->EmitSound( "BumperCar.SpeedBoostStart" );
  4392. #endif // GAME_DLL
  4393. }
  4394. void CTFPlayerShared::OnRemoveHalloweenKartDash()
  4395. {
  4396. m_pOuter->SetFOV( m_pOuter, 0.f, 1.f, 0.f );
  4397. #ifdef CLIENT_DLL
  4398. if ( m_pOuter->m_pSpeedBoostEffect )
  4399. {
  4400. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pSpeedBoostEffect );
  4401. m_pOuter->m_pSpeedBoostEffect = NULL;
  4402. }
  4403. #else // CLIENT_DLL
  4404. m_pOuter->EmitSound( "BumperCar.SpeedBoostStop" );
  4405. #endif // GAME_DLL
  4406. }
  4407. void CTFPlayerShared::OnRemoveHalloweenTiny( void )
  4408. {
  4409. #ifdef GAME_DLL
  4410. m_pOuter->SetModelScale( 1.f );
  4411. RemoveAttributeFromPlayer( "voice pitch scale" );
  4412. RemoveAttributeFromPlayer( "head scale" );
  4413. const Vector& vOrigin = m_pOuter->GetAbsOrigin();
  4414. const QAngle& qAngle = m_pOuter->GetAbsAngles();
  4415. const Vector& vHullMins = m_pOuter->GetFlags() & FL_DUCKING ? VEC_DUCK_HULL_MIN : VEC_HULL_MIN;
  4416. const Vector& vHullMaxs = m_pOuter->GetFlags() & FL_DUCKING ? VEC_DUCK_HULL_MAX : VEC_HULL_MAX;
  4417. trace_t result;
  4418. CTraceFilterIgnoreTeammates filter( m_pOuter, COLLISION_GROUP_NONE, m_pOuter->GetTeamNumber() );
  4419. UTIL_TraceHull( vOrigin, vOrigin, vHullMins, vHullMaxs, MASK_PLAYERSOLID, &filter, &result );
  4420. // am I stuck? try to resolve it
  4421. if ( result.DidHit() )
  4422. {
  4423. float flPlayerHeight = vHullMaxs.z - vHullMins.z;
  4424. float flExtraHeight = 10;
  4425. static Vector vTest[] =
  4426. {
  4427. Vector( 32, 32, flExtraHeight ),
  4428. Vector( -32, -32, flExtraHeight ),
  4429. Vector( -32, 32, flExtraHeight ),
  4430. Vector( 32, -32, flExtraHeight ),
  4431. Vector( 0, 0, flPlayerHeight + flExtraHeight ),
  4432. Vector( 0, 0, -flPlayerHeight - flExtraHeight )
  4433. };
  4434. for ( int i=0; i<ARRAYSIZE( vTest ); ++i )
  4435. {
  4436. Vector vTestPos = vOrigin + vTest[i];
  4437. UTIL_TraceHull( vOrigin, vTestPos, vHullMins, vHullMaxs, MASK_PLAYERSOLID, &filter, &result );
  4438. if ( !result.DidHit() )
  4439. {
  4440. //NDebugOverlay::Box( vTestPos, vHullMins, vHullMaxs, 0, 255, 0, 0, 5.f );
  4441. m_pOuter->Teleport( &vTestPos, &qAngle, NULL );
  4442. return;
  4443. }
  4444. else
  4445. {
  4446. //NDebugOverlay::Box( vTestPos, vHullMins, vHullMaxs, 255, 0, 0, 0, 5.f );
  4447. }
  4448. }
  4449. // just kill the player if we can't resolve getting stuck
  4450. m_pOuter->CommitSuicide( false, true );
  4451. }
  4452. #endif
  4453. }
  4454. void CTFPlayerShared::OnRemoveHalloweenGhostMode( void )
  4455. {
  4456. #ifdef CLIENT_DLL
  4457. m_pOuter->ParticleProp()->StopEmission();
  4458. m_pOuter->SetRenderColor( 255, 255, 255 );
  4459. m_pOuter->UpdateWearables();
  4460. #else
  4461. // We don't do the rest if we're a spectator
  4462. if ( m_pOuter->GetTeamNumber() == TEAM_SPECTATOR )
  4463. return;
  4464. m_pOuter->RemoveFlag( FL_NOTARGET );
  4465. // Restore solid
  4466. m_pOuter->SetSolid( SOLID_BBOX );
  4467. m_pOuter->SetSolidFlags( FSOLID_NOT_STANDABLE );
  4468. m_pOuter->SetCollisionGroup( COLLISION_GROUP_PLAYER );
  4469. // Bring their gun back
  4470. m_pOuter->SetActiveWeapon( m_pOuter->GetLastWeapon() );
  4471. if ( m_pOuter->GetActiveWeapon() )
  4472. {
  4473. m_pOuter->GetActiveWeapon()->Deploy();
  4474. }
  4475. #endif
  4476. }
  4477. #ifdef STAGING_ONLY
  4478. void CTFPlayerShared::OnAddSpaceGravity()
  4479. {
  4480. #ifdef CLIENT_DLL
  4481. if ( m_pOuter->IsLocalPlayer() )
  4482. {
  4483. m_pOuter->EmitSound( "RD.SpaceGravityTransition" );
  4484. }
  4485. #endif
  4486. }
  4487. void CTFPlayerShared::OnRemoveSpaceGravity()
  4488. {
  4489. #ifdef CLIENT_DLL
  4490. if ( m_pOuter->IsLocalPlayer() )
  4491. {
  4492. m_pOuter->EmitSound( "RD.SpaceGravityTransition" );
  4493. }
  4494. #endif
  4495. }
  4496. //-----------------------------------------------------------------------------
  4497. void CTFPlayerShared::OnAddSelfConc()
  4498. {
  4499. UpdatePhaseEffects();
  4500. }
  4501. //-----------------------------------------------------------------------------
  4502. void CTFPlayerShared::OnRemoveSelfConc()
  4503. {
  4504. RemovePhaseEffects();
  4505. }
  4506. //-----------------------------------------------------------------------------
  4507. // Purpose:
  4508. //-----------------------------------------------------------------------------
  4509. void CTFPlayerShared::OnAddStealthedPhase( void )
  4510. {
  4511. #ifdef GAME_DLL
  4512. AddCond( TF_COND_SPEED_BOOST );
  4513. m_pOuter->m_takedamage = DAMAGE_NO;
  4514. #else
  4515. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_STEALTH, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  4516. if ( !IsErrorMaterial( pMaterial ) )
  4517. {
  4518. view->SetScreenOverlayMaterial( pMaterial );
  4519. }
  4520. #endif
  4521. }
  4522. //-----------------------------------------------------------------------------
  4523. // Purpose:
  4524. //-----------------------------------------------------------------------------
  4525. void CTFPlayerShared::OnRemoveStealthedPhase( void )
  4526. {
  4527. #ifdef GAME_DLL
  4528. RemoveCond( TF_COND_SPEED_BOOST );
  4529. m_pOuter->m_takedamage = DAMAGE_YES;
  4530. // See if the spy is inside another player or object
  4531. Vector vecPos = m_pOuter->GetAbsOrigin();
  4532. trace_t trace;
  4533. UTIL_TraceHull( vecPos, vecPos, VEC_HULL_MIN_SCALED( m_pOuter ), VEC_HULL_MAX_SCALED( m_pOuter ), ( MASK_SOLID | CONTENTS_PLAYERCLIP ), m_pOuter, COLLISION_GROUP_NONE, &trace );
  4534. if ( trace.fraction < 1.f )
  4535. {
  4536. // Telefrag!
  4537. m_pOuter->TakeDamage( CTakeDamageInfo( m_pOuter, m_pOuter, 1000, DMG_CRUSH, TF_DMG_CUSTOM_TELEFRAG ) );
  4538. }
  4539. #else
  4540. if ( m_pOuter->IsLocalPlayer() )
  4541. {
  4542. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  4543. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_STEALTH ) )
  4544. {
  4545. view->SetScreenOverlayMaterial( NULL );
  4546. }
  4547. }
  4548. #endif
  4549. }
  4550. void CTFPlayerShared::OnAddClipOverload( void )
  4551. {
  4552. #ifdef GAME_DLL
  4553. m_pOuter->GiveAmmo( 1000, TF_AMMO_PRIMARY );
  4554. m_pOuter->GiveAmmo( 1000, TF_AMMO_SECONDARY );
  4555. m_pOuter->GiveAmmo( 1000, TF_AMMO_METAL );
  4556. m_pOuter->GiveAmmo( 1000, TF_AMMO_GRENADES1 );
  4557. m_pOuter->GiveAmmo( 1000, TF_AMMO_GRENADES2 );
  4558. m_pOuter->GiveAmmo( 1000, TF_AMMO_GRENADES3 );
  4559. // Refills weapon clips, too
  4560. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4561. {
  4562. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( m_pOuter->GetWeapon( i ) );
  4563. if ( !pWeapon )
  4564. continue;
  4565. float flPrevClipScale = pWeapon->GetClipScale();
  4566. if ( m_pOuter->GetActiveTFWeapon() == pWeapon )
  4567. {
  4568. pWeapon->AbortReload(); // Abort reload or else their reload will "fix" their clip size
  4569. pWeapon->SendWeaponAnim( ACT_VM_IDLE );
  4570. }
  4571. pWeapon->SetClipScale( 3.f );
  4572. pWeapon->GiveDefaultAmmo();
  4573. pWeapon->SetClipScale( flPrevClipScale );
  4574. if ( pWeapon->IsEnergyWeapon() )
  4575. {
  4576. pWeapon->WeaponRegenerate();
  4577. }
  4578. }
  4579. #endif
  4580. }
  4581. void CTFPlayerShared::OnRemoveClipOverload( void )
  4582. {
  4583. // Nothing required
  4584. }
  4585. #endif // STAGING_ONLY
  4586. void CTFPlayerShared::OnAddHalloweenKart( void )
  4587. {
  4588. #ifdef GAME_DLL
  4589. CSingleUserRecipientFilter filter( m_pOuter );
  4590. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_HOW_TO_CONTROL_KART );
  4591. ApplyAttributeToPlayer( "head scale", 3.f );
  4592. //ResetKartDamage
  4593. m_pOuter->ResetKartDamage();
  4594. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  4595. if ( pSpellBook )
  4596. {
  4597. pSpellBook->ClearSpell();
  4598. }
  4599. m_pOuter->m_flKartNextAvailableBoost = gpGlobals->curtime + 3.0f;
  4600. // Switch to melee to make sure Spies and Engies don't have build menus open
  4601. CTFWeaponBase *pMeleeWeapon = dynamic_cast<CTFWeaponBase*>( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_MELEE ) );
  4602. Assert( pMeleeWeapon );
  4603. if ( pMeleeWeapon )
  4604. {
  4605. m_pOuter->Weapon_Switch( pMeleeWeapon );
  4606. }
  4607. m_pOuter->m_flNextBonusDucksVOAllowedTime = gpGlobals->curtime + 17.f; // The longest Merasmus line + 1 second
  4608. #else
  4609. extern ConVar tf_halloween_kart_cam_dist;
  4610. m_pOuter->SetTauntCameraTargets( tf_halloween_kart_cam_dist.GetFloat(), 0.0f );
  4611. m_pOuter->CreateKart();
  4612. // Set vehicle angles to be our current angles so we don't spin around
  4613. // when we get in the car
  4614. //$ This is handled in the ForcePlayerViewAngles user message.
  4615. //$ m_angVehicleMoveAngles = m_pOuter->GetAbsAngles();
  4616. if ( m_pOuter->GetActiveWeapon() )
  4617. {
  4618. m_pOuter->GetActiveWeapon()->SetWeaponVisible( false );
  4619. }
  4620. #endif
  4621. }
  4622. void CTFPlayerShared::OnRemoveHalloweenKart( void )
  4623. {
  4624. #ifdef GAME_DLL
  4625. RemoveAttributeFromPlayer( "head scale" );
  4626. //ResetKartDamage
  4627. m_pOuter->ResetKartDamage();
  4628. CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  4629. if ( pSpellBook )
  4630. {
  4631. pSpellBook->ClearSpell();
  4632. }
  4633. #else
  4634. // When we have every taunt cam use this system, we should clean up after ourselves. But for now, this causes a bad interaction
  4635. // with other systems.
  4636. // m_pOuter->SetTauntCameraTargets( 0.0f, 0.0f );
  4637. m_pOuter->RemoveKart();
  4638. // Reset any tilting now that we're out of the karts
  4639. if ( m_pOuter->m_PlayerAnimState )
  4640. {
  4641. QAngle renderAngles = m_pOuter->m_PlayerAnimState->GetRenderAngles();
  4642. renderAngles[ ROLL ] = renderAngles[ PITCH ] = 0.f;
  4643. m_pOuter->m_PlayerAnimState->SetRenderangles( renderAngles );
  4644. }
  4645. if ( m_pOuter->GetActiveWeapon() )
  4646. {
  4647. m_pOuter->GetActiveWeapon()->SetWeaponVisible( true );
  4648. }
  4649. if ( m_hKartParachuteEntity )
  4650. {
  4651. m_hKartParachuteEntity->Release();
  4652. m_hKartParachuteEntity = NULL;
  4653. }
  4654. #endif
  4655. }
  4656. void CTFPlayerShared::OnAddBalloonHead( void )
  4657. {
  4658. #ifdef GAME_DLL
  4659. ApplyAttributeToPlayer( "voice pitch scale", 0.85f );
  4660. ApplyAttributeToPlayer( "head scale", 4.f );
  4661. ApplyAttributeToPlayer( "increased jump height", 0.8f );
  4662. ApplyAttributeToPlayer( "increased air control", 0.2f );
  4663. #endif // GAME_DLL
  4664. m_pOuter->SetGravity( 0.3f );
  4665. }
  4666. void CTFPlayerShared::OnRemoveBalloonHead( void )
  4667. {
  4668. #ifdef GAME_DLL
  4669. RemoveAttributeFromPlayer( "voice pitch scale" );
  4670. RemoveAttributeFromPlayer( "head scale" );
  4671. RemoveAttributeFromPlayer( "increased jump height" );
  4672. RemoveAttributeFromPlayer( "increased air control" );
  4673. #endif // GAME_DLL
  4674. m_pOuter->SetGravity( 0.f );
  4675. }
  4676. void CTFPlayerShared::OnAddMeleeOnly( void )
  4677. {
  4678. #ifdef GAME_DLL
  4679. CTFWeaponBase *pMeleeWeapon = dynamic_cast<CTFWeaponBase*>( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_MELEE ) );
  4680. Assert( pMeleeWeapon );
  4681. if ( pMeleeWeapon )
  4682. {
  4683. m_pOuter->Weapon_Switch( pMeleeWeapon );
  4684. }
  4685. ApplyAttributeToPlayer( "disable weapon switch", true );
  4686. ApplyAttributeToPlayer( "hand scale", 3.f );
  4687. AddCond( TF_COND_HALLOWEEN_TINY );
  4688. AddCond( TF_COND_SPEED_BOOST );
  4689. #endif // GAME_DLL
  4690. }
  4691. void CTFPlayerShared::OnRemoveMeleeOnly( void )
  4692. {
  4693. #ifdef GAME_DLL
  4694. RemoveAttributeFromPlayer( "disable weapon switch" );
  4695. RemoveAttributeFromPlayer( "hand scale" );
  4696. RemoveCond( TF_COND_HALLOWEEN_TINY );
  4697. RemoveCond( TF_COND_SPEED_BOOST );
  4698. #endif // GAME_DLL
  4699. }
  4700. void CTFPlayerShared::OnAddSwimmingCurse( void )
  4701. {
  4702. #ifdef CLIENT_DLL
  4703. if ( m_pOuter->IsLocalPlayer() )
  4704. {
  4705. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_SWIMMING_CURSE, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  4706. if ( !IsErrorMaterial( pMaterial ) )
  4707. {
  4708. view->SetScreenOverlayMaterial( pMaterial );
  4709. }
  4710. }
  4711. #endif // CLIENT_DLL
  4712. }
  4713. void CTFPlayerShared::OnRemoveSwimmingCurse( void )
  4714. {
  4715. #ifdef CLIENT_DLL
  4716. if ( m_pOuter->IsLocalPlayer() )
  4717. {
  4718. // only remove the overlay if it is urine
  4719. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  4720. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_SWIMMING_CURSE ) )
  4721. {
  4722. view->SetScreenOverlayMaterial( NULL );
  4723. }
  4724. }
  4725. #else
  4726. AddCond( TF_COND_URINE, 10.0f );
  4727. #endif // CLIENT_DLL
  4728. }
  4729. void CTFPlayerShared::OnAddHalloweenKartCage( void )
  4730. {
  4731. #ifdef CLIENT_DLL
  4732. Assert( !m_pOuter->m_hHalloweenKartCage );
  4733. if ( !m_pOuter->m_hHalloweenKartCage )
  4734. {
  4735. m_pOuter->m_hHalloweenKartCage = C_PlayerAttachedModel::Create( "models/props_halloween/bumpercar_cage.mdl", m_pOuter, 0, vec3_origin, PAM_PERMANENT, 0 );
  4736. m_pOuter->m_hHalloweenKartCage->FollowEntity( m_pOuter, true );
  4737. }
  4738. #else
  4739. AddCond( TF_COND_FREEZE_INPUT );
  4740. #endif // CLIENT_DLL
  4741. }
  4742. void CTFPlayerShared::OnRemoveHalloweenKartCage( void )
  4743. {
  4744. #ifdef CLIENT_DLL
  4745. Assert( m_pOuter->m_hHalloweenKartCage );
  4746. if ( m_pOuter->m_hHalloweenKartCage )
  4747. {
  4748. m_pOuter->m_hHalloweenKartCage->StopFollowingEntity();
  4749. m_pOuter->m_hHalloweenKartCage->Release();
  4750. }
  4751. #else
  4752. RemoveCond( TF_COND_FREEZE_INPUT );
  4753. DispatchParticleEffect( "ghost_appearation", PATTACH_ABSORIGIN, m_pOuter );
  4754. #endif // CLIENT_DLL
  4755. }
  4756. void CTFPlayerShared::OnAddPasstimeInterception( void )
  4757. {
  4758. #ifdef CLIENT_DLL
  4759. if ( !m_pOuter->m_pPhaseStandingEffect )
  4760. {
  4761. m_pOuter->m_pPhaseStandingEffect = m_pOuter->ParticleProp()->Create( "warp_version", PATTACH_ABSORIGIN_FOLLOW );
  4762. }
  4763. if ( m_pOuter->IsLocalPlayer() )
  4764. {
  4765. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_PHASE, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  4766. if ( !IsErrorMaterial( pMaterial ) )
  4767. {
  4768. view->SetScreenOverlayMaterial( pMaterial );
  4769. }
  4770. }
  4771. #else
  4772. if ( !m_bPhaseFXOn )
  4773. {
  4774. AddPhaseEffects();
  4775. }
  4776. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "started_dodging:1" );
  4777. #endif
  4778. }
  4779. void CTFPlayerShared::OnRemovePasstimeInterception( void )
  4780. {
  4781. #ifdef CLIENT_DLL
  4782. if ( m_pOuter->m_pPhaseStandingEffect )
  4783. {
  4784. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pPhaseStandingEffect );
  4785. m_pOuter->m_pPhaseStandingEffect = NULL;
  4786. }
  4787. if ( m_pOuter->IsLocalPlayer() )
  4788. {
  4789. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  4790. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_PHASE ) )
  4791. {
  4792. view->SetScreenOverlayMaterial( NULL );
  4793. }
  4794. }
  4795. #else
  4796. if ( m_bPhaseFXOn )
  4797. {
  4798. RemovePhaseEffects();
  4799. }
  4800. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_DODGING, "started_dodging:0" );
  4801. #endif
  4802. }
  4803. void CTFPlayerShared::OnAddRunePlague( void )
  4804. {
  4805. #ifdef CLIENT_DLL
  4806. m_pOuter->m_pRunePlagueEffect = m_pOuter->ParticleProp()->Create( "powerup_plague_carrier", PATTACH_ABSORIGIN_FOLLOW );
  4807. // show resist effect on enemy player that has plague rune if local player is in plague cond
  4808. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  4809. if ( pLocalPlayer && pLocalPlayer != m_pOuter && pLocalPlayer->m_Shared.InCond( TF_COND_PLAGUE ) && m_pOuter->IsEnemyPlayer() )
  4810. {
  4811. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_RUNE_PLAGUE );
  4812. }
  4813. #endif // CLIENT_DLL
  4814. }
  4815. void CTFPlayerShared::OnRemoveRunePlague( void )
  4816. {
  4817. #ifdef CLIENT_DLL
  4818. if ( m_pOuter->m_pRunePlagueEffect )
  4819. {
  4820. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pRunePlagueEffect );
  4821. m_pOuter->m_pRunePlagueEffect = NULL;
  4822. }
  4823. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  4824. #endif // CLIENT_DLL
  4825. }
  4826. void CTFPlayerShared::OnAddPlague( void )
  4827. {
  4828. #ifdef CLIENT_DLL
  4829. m_pOuter->EmitSound( "Powerup.PickUpPlagueInfected" );
  4830. #endif
  4831. CTFPlayer *pProvider = ToTFPlayer( m_ConditionData[TF_COND_PLAGUE].m_pProvider );
  4832. //plague damage is a percentage of player health so everyone has the same life expectancy
  4833. float flPlagueDmg = 0.05f * m_pOuter->GetMaxHealth();
  4834. if ( pProvider )
  4835. {
  4836. MakeBleed( pProvider, NULL, 0.f, flPlagueDmg, true );
  4837. CSingleUserRecipientFilter localFilter( pProvider );
  4838. pProvider->EmitSound( localFilter, pProvider->entindex(), "Powerup.PickUpPlagueInfected" );
  4839. }
  4840. m_pOuter->EmitSound( "Powerup.PickUpPlagueInfectedLoop" );
  4841. ClientPrint( m_pOuter, HUD_PRINTCENTER, "#TF_Powerup_Contract_Plague" );
  4842. #ifdef CLIENT_DLL
  4843. // show resist effect on enemy player that has plague rune if local player is in plague cond
  4844. if ( m_pOuter->IsLocalPlayer() && pProvider && pProvider->IsEnemyPlayer() )
  4845. {
  4846. AddResistShield( &pProvider->m_pTempShield, pProvider, TF_COND_RUNE_PLAGUE );
  4847. }
  4848. #endif // CLIENT_DLL
  4849. }
  4850. void CTFPlayerShared::OnRemovePlague( void )
  4851. {
  4852. m_pOuter->StopSound( "Powerup.PickUpPlagueInfectedLoop" );
  4853. #ifdef CLIENT_DLL
  4854. if ( m_pOuter->IsLocalPlayer() )
  4855. {
  4856. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  4857. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_BLEED ) )
  4858. {
  4859. view->SetScreenOverlayMaterial( NULL );
  4860. }
  4861. // remove shield from the current plague rune carrier
  4862. int iEnemyTeam = m_pOuter->GetTeamNumber() == TF_TEAM_RED ? TF_TEAM_BLUE : TF_TEAM_RED;
  4863. CTFPlayer *pCurrentRuneCarrier = GetRuneCarrier( RUNE_PLAGUE, iEnemyTeam );
  4864. if ( pCurrentRuneCarrier )
  4865. {
  4866. RemoveResistShield( &pCurrentRuneCarrier->m_pTempShield, pCurrentRuneCarrier );
  4867. }
  4868. }
  4869. #endif
  4870. // RemoveCond( TF_COND_BLEEDING );
  4871. }
  4872. //-----------------------------------------------------------------------------
  4873. // Purpose:
  4874. //-----------------------------------------------------------------------------
  4875. void CTFPlayerShared::OnAddInPurgatory( void )
  4876. {
  4877. #ifdef GAME_DLL
  4878. // just entered
  4879. m_pOuter->m_purgatoryPainMultiplierTimer.Start( 40.0f );
  4880. m_pOuter->m_purgatoryPainMultiplier = 1;
  4881. // Set our health to full
  4882. m_pOuter->SetHealth( m_pOuter->GetMaxHealth() );
  4883. // Remove our projectiles
  4884. m_pOuter->RemoveOwnedProjectiles();
  4885. // Give us a brief period of invuln while we drop into purgatory
  4886. AddCond( TF_COND_INVULNERABLE, 1.5f );
  4887. #endif
  4888. }
  4889. //-----------------------------------------------------------------------------
  4890. // Purpose:
  4891. //-----------------------------------------------------------------------------
  4892. void CTFPlayerShared::OnRemoveInPurgatory( void )
  4893. {
  4894. #ifdef GAME_DLL
  4895. if ( m_pOuter->IsAlive() )
  4896. {
  4897. // we escaped purgatory alive!
  4898. const float buffDuration = 10.0f;
  4899. AddCond( TF_COND_CRITBOOSTED_PUMPKIN, buffDuration );
  4900. AddCond( TF_COND_SPEED_BOOST, buffDuration );
  4901. AddCond( TF_COND_INVULNERABLE, buffDuration );
  4902. m_pOuter->SetHealth( 2.0f * m_pOuter->GetMaxHealth() );
  4903. m_pOuter->m_purgatoryBuffTimer.Start( buffDuration );
  4904. TFGameRules()->BroadcastSound( 255, "Halloween.PlayerEscapedUnderworld" );
  4905. // Remove our projectiles
  4906. m_pOuter->RemoveOwnedProjectiles();
  4907. CReliableBroadcastRecipientFilter filter;
  4908. const char* pszEscapeMessage = "#TF_Halloween_Underworld";
  4909. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) )
  4910. {
  4911. pszEscapeMessage = "#TF_Halloween_Skull_Island_Escape";
  4912. }
  4913. UTIL_SayText2Filter( filter, m_pOuter, false, pszEscapeMessage, m_pOuter->GetPlayerName() );
  4914. IGameEvent *pEvent = gameeventmanager->CreateEvent( "escaped_loot_island" );
  4915. if ( pEvent )
  4916. {
  4917. pEvent->SetInt( "player", m_pOuter->GetUserID() );
  4918. gameeventmanager->FireEvent( pEvent, true );
  4919. }
  4920. if ( m_pOuter->GetTeam() )
  4921. {
  4922. const char* pszLogMessage = "purgatory_escaped";
  4923. if ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) )
  4924. {
  4925. pszEscapeMessage = "skull_island_escaped";
  4926. }
  4927. UTIL_LogPrintf( "HALLOWEEN: \"%s<%i><%s><%s>\" %s\n",
  4928. m_pOuter->GetPlayerName(),
  4929. m_pOuter->GetUserID(),
  4930. m_pOuter->GetNetworkIDString(),
  4931. m_pOuter->GetTeam()->GetName(),
  4932. pszLogMessage );
  4933. }
  4934. }
  4935. #endif
  4936. }
  4937. //-----------------------------------------------------------------------------
  4938. // Purpose:
  4939. //-----------------------------------------------------------------------------
  4940. void CTFPlayerShared::OnAddCompetitiveWinner( void )
  4941. {
  4942. #ifdef GAME_DLL
  4943. #else
  4944. if ( m_pOuter->IsLocalPlayer() )
  4945. {
  4946. gHUD.LockRenderGroup( gHUD.LookupRenderGroupIndexByName( "mid" ) );
  4947. m_pOuter->UpdateVisibility();
  4948. m_pOuter->UpdateWearables();
  4949. }
  4950. #endif
  4951. }
  4952. //-----------------------------------------------------------------------------
  4953. // Purpose:
  4954. //-----------------------------------------------------------------------------
  4955. void CTFPlayerShared::OnRemoveCompetitiveWinner( void )
  4956. {
  4957. #ifdef GAME_DLL
  4958. #else
  4959. #endif
  4960. }
  4961. //-----------------------------------------------------------------------------
  4962. // Purpose:
  4963. //-----------------------------------------------------------------------------
  4964. void CTFPlayerShared::OnAddCompetitiveLoser( void )
  4965. {
  4966. #ifdef GAME_DLL
  4967. #else
  4968. if ( m_pOuter->IsLocalPlayer() )
  4969. {
  4970. gHUD.LockRenderGroup( gHUD.LookupRenderGroupIndexByName( "mid" ) );
  4971. m_pOuter->UpdateVisibility();
  4972. m_pOuter->UpdateWearables();
  4973. }
  4974. #endif
  4975. }
  4976. //-----------------------------------------------------------------------------
  4977. // Purpose:
  4978. //-----------------------------------------------------------------------------
  4979. void CTFPlayerShared::OnRemoveCompetitiveLoser( void )
  4980. {
  4981. #ifdef GAME_DLL
  4982. #else
  4983. #endif
  4984. }
  4985. //-----------------------------------------------------------------------------
  4986. // Purpose:
  4987. //-----------------------------------------------------------------------------
  4988. void CTFPlayerShared::UpdateChargeMeter( void )
  4989. {
  4990. if ( !m_pOuter->IsPlayerClass( TF_CLASS_DEMOMAN ) )
  4991. return;
  4992. if ( InCond( TF_COND_SHIELD_CHARGE ) )
  4993. {
  4994. // Drain the meter while we are charging.
  4995. float flChargeDrainTime = tf_demoman_charge_drain_time.GetFloat();
  4996. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flChargeDrainTime, mod_charge_time );
  4997. float flChargeDrainMod = 100.f / flChargeDrainTime;
  4998. m_flChargeMeter -= gpGlobals->frametime * flChargeDrainMod;
  4999. if ( m_flChargeMeter <= 0 )
  5000. {
  5001. m_flChargeMeter = 0;
  5002. RemoveCond( TF_COND_SHIELD_CHARGE );
  5003. }
  5004. m_flLastNoChargeTime = gpGlobals->curtime;
  5005. }
  5006. else if ( m_flChargeMeter < 100.f )
  5007. {
  5008. // Recharge the meter while we are not charging.
  5009. float flChargeRegenMod = tf_demoman_charge_regen_rate.GetFloat();
  5010. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flChargeRegenMod, charge_recharge_rate );
  5011. if ( TFGameRules() && TFGameRules()->IsPowerupMode() && GetCarryingRuneType() == RUNE_KNOCKOUT )
  5012. {
  5013. flChargeRegenMod *= 0.2f;
  5014. }
  5015. flChargeRegenMod = Max( flChargeRegenMod, 1.f );
  5016. m_flChargeMeter += gpGlobals->frametime * flChargeRegenMod;
  5017. if ( m_flChargeMeter > 100.f )
  5018. {
  5019. m_flChargeMeter = 100.f;
  5020. }
  5021. // Used for the weapon glow cooldown.
  5022. if ( !m_bChargeGlowing )
  5023. {
  5024. m_flLastNoChargeTime = gpGlobals->curtime;
  5025. }
  5026. }
  5027. }
  5028. //-----------------------------------------------------------------------------
  5029. // Purpose:
  5030. //-----------------------------------------------------------------------------
  5031. void CTFPlayerShared::EndCharge()
  5032. {
  5033. if ( !InCond( TF_COND_SHIELD_CHARGE ) )
  5034. return;
  5035. #ifdef GAME_DLL
  5036. if ( GetDemomanChargeMeter() < 90 )
  5037. {
  5038. // Impacts drain the charge meter completely.
  5039. float flMeterAtImpact = m_flChargeMeter;
  5040. CTFWearableDemoShield *pWearableShield = GetEquippedDemoShield( m_pOuter );
  5041. if ( pWearableShield )
  5042. {
  5043. pWearableShield->ShieldBash( m_pOuter, flMeterAtImpact );
  5044. }
  5045. CalcChargeCrit();
  5046. // Removing the condition here would cause issues with prediction, so we set the
  5047. // duration to zero so that it will be removed during the next condition think.
  5048. SetConditionDuration( TF_COND_SHIELD_CHARGE, 0 );
  5049. }
  5050. #endif
  5051. }
  5052. //-----------------------------------------------------------------------------
  5053. // Purpose:
  5054. //-----------------------------------------------------------------------------
  5055. float CTFPlayerShared::CalculateChargeCap( void ) const
  5056. {
  5057. float flCap = 0.45f;
  5058. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flCap, charge_turn_control );
  5059. // Scale yaw cap based on frametime to prevent differences in turn effectiveness due to variable framerate (between clients mainly)
  5060. if ( tf_demoman_charge_frametime_scaling.GetBool() )
  5061. {
  5062. // There's probably something better to use here as a baseline, instead of TICK_INTERVAL
  5063. float flMod = RemapValClamped( gpGlobals->frametime, ( TICK_INTERVAL * YAW_CAP_SCALE_MIN ), ( TICK_INTERVAL * YAW_CAP_SCALE_MAX ), 0.25f, 2.f );
  5064. flCap *= flMod;
  5065. }
  5066. return flCap;
  5067. }
  5068. bool CTFPlayerShared::HasDemoShieldEquipped() const
  5069. {
  5070. return GetEquippedDemoShield( m_pOuter ) != NULL;
  5071. }
  5072. //-----------------------------------------------------------------------------
  5073. // Purpose:
  5074. //-----------------------------------------------------------------------------
  5075. void CTFPlayerShared::CalcChargeCrit( bool bForceCrit )
  5076. {
  5077. #ifdef GAME_DLL
  5078. // Keying on TideTurner
  5079. int iDemoChargeDamagePenalty = 0;
  5080. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iDemoChargeDamagePenalty, lose_demo_charge_on_damage_when_charging );
  5081. if ( iDemoChargeDamagePenalty && GetDemomanChargeMeter() <= 75 )
  5082. {
  5083. SetNextMeleeCrit( MELEE_MINICRIT );
  5084. }
  5085. else if ( GetDemomanChargeMeter() <= 40 || bForceCrit)
  5086. {
  5087. SetNextMeleeCrit( MELEE_CRIT );
  5088. }
  5089. else if ( GetDemomanChargeMeter() <= 75 )
  5090. {
  5091. SetNextMeleeCrit( MELEE_MINICRIT );
  5092. }
  5093. m_pOuter->SetContextThink( &CTFPlayer::RemoveMeleeCrit, gpGlobals->curtime + 0.3f, "RemoveMeleeCrit" );
  5094. #endif
  5095. }
  5096. //-----------------------------------------------------------------------------
  5097. // Purpose:
  5098. //-----------------------------------------------------------------------------
  5099. void CTFPlayerShared::OnAddShieldCharge( void )
  5100. {
  5101. UpdatePhaseEffects();
  5102. m_pOuter->TeamFortress_SetSpeed();
  5103. #ifdef CLIENT_DLL
  5104. m_pOuter->EmitSound( "DemoCharge.Charging" );
  5105. #else
  5106. m_hPlayersVisibleAtChargeStart.Purge();
  5107. // Remove debuffs
  5108. for ( int i = 0; g_aDebuffConditions[i] != TF_COND_LAST; i++ )
  5109. {
  5110. RemoveCond( g_aDebuffConditions[i] );
  5111. }
  5112. // store the players we CAN see for the TF_DEMOMAN_KILL_PLAYER_YOU_DIDNT_SEE achievement
  5113. CUtlVector<CTFPlayer *> vecPlayers;
  5114. CollectPlayers( &vecPlayers, ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED, true );
  5115. FOR_EACH_VEC( vecPlayers, i )
  5116. {
  5117. if ( !vecPlayers[i] )
  5118. continue;
  5119. if ( vecPlayers[i]->m_Shared.InCond( TF_COND_STEALTHED ) )
  5120. continue;
  5121. // can we see them?
  5122. if ( m_pOuter->FVisible( vecPlayers[i], MASK_OPAQUE ) == false )
  5123. continue;
  5124. // are they in our field of view? (might be behind us)
  5125. if ( m_pOuter->IsInFieldOfView( vecPlayers[i]) == false )
  5126. continue;
  5127. m_hPlayersVisibleAtChargeStart.AddToTail( vecPlayers[i] );
  5128. }
  5129. #endif
  5130. }
  5131. //-----------------------------------------------------------------------------
  5132. // Purpose:
  5133. //-----------------------------------------------------------------------------
  5134. void CTFPlayerShared::OnRemoveShieldCharge( void )
  5135. {
  5136. RemovePhaseEffects();
  5137. m_pOuter->TeamFortress_SetSpeed();
  5138. m_bPostShieldCharge = true;
  5139. m_flChargeEndTime = gpGlobals->curtime;
  5140. m_flChargeMeter = 0;
  5141. }
  5142. //-----------------------------------------------------------------------------
  5143. // Purpose:
  5144. //-----------------------------------------------------------------------------
  5145. void CTFPlayerShared::InterruptCharge( void )
  5146. {
  5147. if ( !InCond( TF_COND_SHIELD_CHARGE ) )
  5148. return;
  5149. SetConditionDuration( TF_COND_SHIELD_CHARGE, 0 );
  5150. }
  5151. //-----------------------------------------------------------------------------
  5152. // Purpose:
  5153. //-----------------------------------------------------------------------------
  5154. #ifdef GAME_DLL
  5155. void CTFPlayer::RemoveMeleeCrit( void )
  5156. {
  5157. m_Shared.SetNextMeleeCrit( MELEE_NOCRIT );
  5158. m_Shared.m_bPostShieldCharge = false;
  5159. // Remove crit boost right away. DemoShieldChargeThink depends on m_bPostShieldCharge being true
  5160. // to attempt to remove crits (which we just cleared) so clear crits here as well.
  5161. if ( m_Shared.InCond( TF_COND_CRITBOOSTED_DEMO_CHARGE ) )
  5162. {
  5163. m_Shared.RemoveCond( TF_COND_CRITBOOSTED_DEMO_CHARGE );
  5164. }
  5165. }
  5166. #endif
  5167. //-----------------------------------------------------------------------------
  5168. // Purpose:
  5169. //-----------------------------------------------------------------------------
  5170. void CTFPlayerShared::DemoShieldChargeThink( void )
  5171. {
  5172. if ( InCond( TF_COND_SHIELD_CHARGE ) || m_bPostShieldCharge )
  5173. {
  5174. if ( m_bPostShieldCharge && (gpGlobals->curtime - m_flChargeEndTime) >= 0.3f )
  5175. {
  5176. if ( InCond( TF_COND_CRITBOOSTED_DEMO_CHARGE ) )
  5177. {
  5178. RemoveCond( TF_COND_CRITBOOSTED_DEMO_CHARGE );
  5179. }
  5180. m_bPostShieldCharge = false;
  5181. }
  5182. else if ( InCond( TF_COND_SHIELD_CHARGE ) && GetDemomanChargeMeter() < 75 )
  5183. {
  5184. if ( !InCond( TF_COND_CRITBOOSTED_DEMO_CHARGE ) )
  5185. {
  5186. AddCond( TF_COND_CRITBOOSTED_DEMO_CHARGE );
  5187. }
  5188. }
  5189. }
  5190. }
  5191. //-----------------------------------------------------------------------------
  5192. // Purpose:
  5193. //-----------------------------------------------------------------------------
  5194. void CTFPlayerShared::OnAddDemoBuff( void )
  5195. {
  5196. #ifdef CLIENT_DLL
  5197. m_pOuter->UpdateDemomanEyeEffect( m_iDecapitations );
  5198. #else
  5199. #endif
  5200. }
  5201. //-----------------------------------------------------------------------------
  5202. // Purpose:
  5203. //-----------------------------------------------------------------------------
  5204. void CTFPlayerShared::OnAddEnergyDrinkBuff( void )
  5205. {
  5206. #ifdef CLIENT_DLL
  5207. UpdateCritBoostEffect();
  5208. #endif
  5209. #ifdef GAME_DLL
  5210. if ( m_pOuter->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) || m_pOuter->IsPlayerClass( TF_CLASS_SCOUT ) )
  5211. {
  5212. // Begin berzerker speed buff.
  5213. m_pOuter->TeamFortress_SetSpeed();
  5214. }
  5215. #endif
  5216. }
  5217. //-----------------------------------------------------------------------------
  5218. // Purpose:
  5219. //-----------------------------------------------------------------------------
  5220. void CTFPlayerShared::OnRemoveEnergyDrinkBuff( void )
  5221. {
  5222. #ifdef CLIENT_DLL
  5223. UpdateCritBoostEffect();
  5224. #endif
  5225. #ifdef GAME_DLL
  5226. if ( m_pOuter->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) || m_pOuter->IsPlayerClass( TF_CLASS_SCOUT ) )
  5227. {
  5228. // End berzerker speed buff.
  5229. m_pOuter->TeamFortress_SetSpeed();
  5230. }
  5231. #endif
  5232. }
  5233. #ifdef CLIENT_DLL
  5234. //-----------------------------------------------------------------------------
  5235. // Purpose:
  5236. //-----------------------------------------------------------------------------
  5237. void CTFPlayerShared::EndRadiusHealEffect( void )
  5238. {
  5239. if ( m_pOuter->m_pRadiusHealEffect )
  5240. {
  5241. m_pOuter->m_pRadiusHealEffect->StopEmission();
  5242. m_pOuter->m_pRadiusHealEffect = NULL;
  5243. }
  5244. }
  5245. //-----------------------------------------------------------------------------
  5246. // Purpose:
  5247. //-----------------------------------------------------------------------------
  5248. void CTFPlayerShared::EndKingBuffRadiusEffect( void )
  5249. {
  5250. // For buffed player
  5251. if ( m_pOuter->m_pKingBuffRadiusEffect )
  5252. {
  5253. m_pOuter->m_pKingBuffRadiusEffect->StopEmission();
  5254. m_pOuter->m_pKingBuffRadiusEffect = NULL;
  5255. }
  5256. // For carrier of King Rune
  5257. if ( m_pOuter->m_pKingRuneRadiusEffect )
  5258. {
  5259. m_pOuter->m_pKingRuneRadiusEffect->StopEmission();
  5260. m_pOuter->m_pKingRuneRadiusEffect = NULL;
  5261. }
  5262. }
  5263. #endif
  5264. //-----------------------------------------------------------------------------
  5265. // Purpose:
  5266. //-----------------------------------------------------------------------------
  5267. void CTFPlayerShared::OnAddRadiusHeal( void )
  5268. {
  5269. #ifdef CLIENT_DLL
  5270. if ( InCond( TF_COND_RADIUSHEAL ) )
  5271. {
  5272. if ( IsStealthed() )
  5273. {
  5274. EndRadiusHealEffect();
  5275. return;
  5276. }
  5277. const char *pszRadiusHealEffect;
  5278. int nTeamNumber = m_pOuter->GetTeamNumber();
  5279. if ( m_pOuter->IsPlayerClass( TF_CLASS_SPY ) && InCond( TF_COND_DISGUISED ) && ( GetDisguiseTeam() == GetLocalPlayerTeam() ) )
  5280. {
  5281. nTeamNumber = GetLocalPlayerTeam();
  5282. }
  5283. if ( nTeamNumber == TF_TEAM_RED )
  5284. {
  5285. pszRadiusHealEffect = "medic_healradius_red_buffed";
  5286. }
  5287. else
  5288. {
  5289. pszRadiusHealEffect = "medic_healradius_blue_buffed";
  5290. }
  5291. if ( !m_pOuter->m_pRadiusHealEffect )
  5292. {
  5293. m_pOuter->m_pRadiusHealEffect = m_pOuter->ParticleProp()->Create( pszRadiusHealEffect, PATTACH_ABSORIGIN_FOLLOW );
  5294. }
  5295. }
  5296. #endif
  5297. }
  5298. //-----------------------------------------------------------------------------
  5299. // Purpose:
  5300. //-----------------------------------------------------------------------------
  5301. void CTFPlayerShared::OnRemoveRadiusHeal( void )
  5302. {
  5303. #ifdef CLIENT_DLL
  5304. EndRadiusHealEffect();
  5305. #endif
  5306. }
  5307. //-----------------------------------------------------------------------------
  5308. // Purpose:
  5309. //-----------------------------------------------------------------------------
  5310. void CTFPlayerShared::OnAddMegaHeal( void )
  5311. {
  5312. #ifdef CLIENT_DLL
  5313. if ( m_pOuter->IsLocalPlayer() )
  5314. {
  5315. const char *pEffectName = NULL;
  5316. if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED )
  5317. {
  5318. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED;
  5319. }
  5320. else
  5321. {
  5322. pEffectName = TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE;
  5323. }
  5324. IMaterial *pMaterial = materials->FindMaterial( pEffectName, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  5325. if ( !IsErrorMaterial( pMaterial ) )
  5326. {
  5327. view->SetScreenOverlayMaterial( pMaterial );
  5328. }
  5329. }
  5330. if ( InCond( TF_COND_MEGAHEAL ) )
  5331. {
  5332. const char *pszMegaHealEffectName;
  5333. if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED )
  5334. {
  5335. pszMegaHealEffectName = "medic_megaheal_red";
  5336. }
  5337. else
  5338. {
  5339. pszMegaHealEffectName = "medic_megaheal_blue";
  5340. }
  5341. if ( !m_pOuter->m_pMegaHealEffect )
  5342. {
  5343. m_pOuter->m_pMegaHealEffect = m_pOuter->ParticleProp()->Create( pszMegaHealEffectName, PATTACH_ABSORIGIN_FOLLOW );
  5344. }
  5345. }
  5346. #endif
  5347. }
  5348. //-----------------------------------------------------------------------------
  5349. // Purpose:
  5350. //-----------------------------------------------------------------------------
  5351. void CTFPlayerShared::OnRemoveMegaHeal( void )
  5352. {
  5353. #ifdef CLIENT_DLL
  5354. if ( m_pOuter->m_pMegaHealEffect )
  5355. {
  5356. m_pOuter->m_pMegaHealEffect->StopEmission();
  5357. m_pOuter->m_pMegaHealEffect = NULL;
  5358. }
  5359. if ( m_pOuter->IsLocalPlayer() )
  5360. {
  5361. // only remove the overlay if it is an invuln material
  5362. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  5363. if ( pMaterial &&
  5364. ( FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_BLUE ) ||
  5365. FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_INVULN_RED ) ) )
  5366. {
  5367. view->SetScreenOverlayMaterial( NULL );
  5368. }
  5369. }
  5370. #endif
  5371. }
  5372. //-----------------------------------------------------------------------------
  5373. // Purpose:
  5374. //-----------------------------------------------------------------------------
  5375. void CTFPlayerShared::OnAddKingBuff( void )
  5376. {
  5377. #ifdef CLIENT_DLL
  5378. if ( IsStealthed() )
  5379. {
  5380. EndKingBuffRadiusEffect();
  5381. return;
  5382. }
  5383. #endif
  5384. }
  5385. //-----------------------------------------------------------------------------
  5386. // Purpose:
  5387. //-----------------------------------------------------------------------------
  5388. void CTFPlayerShared::OnRemoveKingBuff( void )
  5389. {
  5390. #ifdef CLIENT_DLL
  5391. EndKingBuffRadiusEffect();
  5392. #endif
  5393. }
  5394. //-----------------------------------------------------------------------------
  5395. // Purpose:
  5396. //-----------------------------------------------------------------------------
  5397. void CTFPlayerShared::OnRemoveRuneSupernova( void )
  5398. {
  5399. SetRuneCharge( 0.f );
  5400. }
  5401. //-----------------------------------------------------------------------------
  5402. // Purpose:
  5403. //-----------------------------------------------------------------------------
  5404. void CTFPlayerShared::OnRemoveDemoBuff( void )
  5405. {
  5406. #ifdef CLIENT_DLL
  5407. m_pOuter->UpdateDemomanEyeEffect( 0 );
  5408. #else
  5409. #endif
  5410. }
  5411. //-----------------------------------------------------------------------------
  5412. // Purpose:
  5413. //-----------------------------------------------------------------------------
  5414. #ifdef CLIENT_DLL
  5415. void CTFPlayerShared::ClientDemoBuffThink( void )
  5416. {
  5417. if ( m_iDecapitations > 0 )
  5418. {
  5419. if ( m_iDecapitations != m_iOldDecapitations )
  5420. {
  5421. m_iOldDecapitations = m_iDecapitations;
  5422. m_pOuter->UpdateDemomanEyeEffect( m_iDecapitations );
  5423. }
  5424. }
  5425. }
  5426. //-----------------------------------------------------------------------------
  5427. void CTFPlayerShared::ClientKillStreakBuffThink( void )
  5428. {
  5429. int nLoadoutSlot = m_pOuter->GetActiveTFWeapon() ? m_pOuter->GetActiveTFWeapon()->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( m_pOuter->GetPlayerClass()->GetClassIndex() ) : LOADOUT_POSITION_PRIMARY;
  5430. int nKillStreak = GetStreak(kTFStreak_Kills);
  5431. if ( nKillStreak != m_iOldKillStreak || m_iOldKillStreakWepSlot != nLoadoutSlot )
  5432. {
  5433. m_pOuter->UpdateKillStreakEffects( nKillStreak, m_iOldKillStreak < nKillStreak );
  5434. m_iOldKillStreak = nKillStreak;
  5435. m_iOldKillStreakWepSlot = nLoadoutSlot;
  5436. }
  5437. else if ( !m_pOuter->IsAlive() )
  5438. {
  5439. m_pOuter->UpdateKillStreakEffects( 0, false );
  5440. }
  5441. else
  5442. {
  5443. static bool bAlternate = false;
  5444. Vector vColor = bAlternate ? m_pOuter->m_vEyeGlowColor1 : m_pOuter->m_vEyeGlowColor2;
  5445. if ( m_pOuter->m_pEyeGlowEffect[0] )
  5446. {
  5447. m_pOuter->m_pEyeGlowEffect[0]->SetControlPoint( CUSTOM_COLOR_CP1, vColor );
  5448. }
  5449. if ( m_pOuter->m_pEyeGlowEffect[1] )
  5450. {
  5451. m_pOuter->m_pEyeGlowEffect[1]->SetControlPoint( CUSTOM_COLOR_CP1, vColor );
  5452. }
  5453. //
  5454. bAlternate = !bAlternate;
  5455. }
  5456. }
  5457. #endif
  5458. //-----------------------------------------------------------------------------
  5459. // Purpose:
  5460. //-----------------------------------------------------------------------------
  5461. void CTFPlayerShared::OnAddTeleported( void )
  5462. {
  5463. #ifdef CLIENT_DLL
  5464. m_pOuter->UpdateRecentlyTeleportedEffect();
  5465. m_flGotTeleEffectAt = gpGlobals->curtime;
  5466. #endif
  5467. }
  5468. //-----------------------------------------------------------------------------
  5469. // Purpose:
  5470. //-----------------------------------------------------------------------------
  5471. void CTFPlayerShared::OnRemoveTeleported( void )
  5472. {
  5473. #ifdef CLIENT_DLL
  5474. m_pOuter->UpdateRecentlyTeleportedEffect();
  5475. #endif
  5476. }
  5477. #ifdef CLIENT_DLL
  5478. //-----------------------------------------------------------------------------
  5479. // Purpose:
  5480. //-----------------------------------------------------------------------------
  5481. bool CTFPlayerShared::ShouldShowRecentlyTeleported( void )
  5482. {
  5483. if ( IsStealthed() )
  5484. {
  5485. return false;
  5486. }
  5487. if ( m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  5488. {
  5489. // disguised as an enemy
  5490. if ( InCond( TF_COND_DISGUISED ) && GetDisguiseTeam() != m_pOuter->GetTeamNumber() )
  5491. {
  5492. // was this my own team's teleporter?
  5493. if ( GetTeamTeleporterUsed() == m_pOuter->GetTeamNumber() )
  5494. {
  5495. // don't show my trail
  5496. return false;
  5497. }
  5498. else
  5499. {
  5500. // okay to show the local player the trail, but not his team (might confuse them)
  5501. if ( !m_pOuter->IsLocalPlayer() && m_pOuter->GetTeamNumber() == GetLocalPlayerTeam() )
  5502. {
  5503. return false;
  5504. }
  5505. }
  5506. }
  5507. else
  5508. {
  5509. if ( GetTeamTeleporterUsed() != m_pOuter->GetTeamNumber() )
  5510. {
  5511. return false;
  5512. }
  5513. }
  5514. }
  5515. return InCond( TF_COND_TELEPORTED );
  5516. }
  5517. #endif
  5518. //-----------------------------------------------------------------------------
  5519. // Purpose:
  5520. //-----------------------------------------------------------------------------
  5521. void CTFPlayerShared::Burn( CTFPlayer *pAttacker, CTFWeaponBase *pWeapon, float flBurningTime /*=-1*/ )
  5522. {
  5523. #ifdef CLIENT_DLL
  5524. #else
  5525. // Don't bother igniting players who have just been killed by the fire damage.
  5526. if ( !m_pOuter->IsAlive() )
  5527. return;
  5528. //Don't ignite if I'm in phase mode.
  5529. if ( InCond( TF_COND_PHASE ) || InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  5530. return;
  5531. // pyros don't burn persistently or take persistent burning damage, but we show brief burn effect so attacker can tell they hit
  5532. bool bVictimIsPyro = ( TF_CLASS_PYRO == m_pOuter->GetPlayerClass()->GetClassIndex() );
  5533. if ( !InCond( TF_COND_BURNING ) )
  5534. {
  5535. // Start burning
  5536. AddCond( TF_COND_BURNING, -1.f, pAttacker );
  5537. m_flFlameBurnTime = gpGlobals->curtime; //asap
  5538. // let the attacker know he burned me
  5539. if ( pAttacker && !bVictimIsPyro )
  5540. {
  5541. pAttacker->OnBurnOther( m_pOuter, pWeapon );
  5542. m_hOriginalBurnAttacker = pAttacker;
  5543. }
  5544. }
  5545. int nAfterburnImmunity = 0;
  5546. // Check my weapon
  5547. CTFWeaponBase *pMyWeapon = GetActiveTFWeapon();
  5548. if ( pMyWeapon )
  5549. {
  5550. CALL_ATTRIB_HOOK_INT_ON_OTHER( pMyWeapon, nAfterburnImmunity, afterburn_immunity );
  5551. }
  5552. // STAGING_SPY
  5553. if ( InCond( TF_COND_AFTERBURN_IMMUNE ) )
  5554. {
  5555. nAfterburnImmunity = 1;
  5556. m_flFlameRemoveTime = 0;
  5557. }
  5558. // Check shield
  5559. if ( !nAfterburnImmunity && IsShieldEquipped() )
  5560. {
  5561. CTFWearableDemoShield *pWearableShield = GetEquippedDemoShield( m_pOuter );
  5562. if ( pWearableShield && !pWearableShield->IsDisguiseWearable() )
  5563. {
  5564. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWearableShield, nAfterburnImmunity, afterburn_immunity );
  5565. }
  5566. }
  5567. bool bIsFlareGun = ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_FLAREGUN );
  5568. float flFlameLife;
  5569. if ( bVictimIsPyro || nAfterburnImmunity )
  5570. {
  5571. flFlameLife = TF_BURNING_FLAME_LIFE_PYRO;
  5572. }
  5573. else if ( flBurningTime > 0 )
  5574. {
  5575. flFlameLife = flBurningTime;
  5576. }
  5577. else if ( bIsFlareGun )
  5578. {
  5579. flFlameLife = TF_BURNING_FLAME_LIFE_FLARE;
  5580. }
  5581. else if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_PARTICLE_CANNON )
  5582. {
  5583. flFlameLife = TF_BURNING_FLAME_LIFE_PLASMA;
  5584. }
  5585. else
  5586. {
  5587. flFlameLife = TF_BURNING_FLAME_LIFE;
  5588. }
  5589. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flFlameLife, mult_wpn_burntime );
  5590. float flBurnEnd = gpGlobals->curtime + flFlameLife;
  5591. if ( flBurnEnd > m_flFlameRemoveTime )
  5592. {
  5593. m_flFlameRemoveTime = flBurnEnd;
  5594. }
  5595. m_hBurnAttacker = pAttacker;
  5596. m_hBurnWeapon = pWeapon;
  5597. #endif
  5598. }
  5599. //-----------------------------------------------------------------------------
  5600. // Purpose: A non-TF Player burned us
  5601. // Used for Bosses, they inflict self burn
  5602. //-----------------------------------------------------------------------------
  5603. void CTFPlayerShared::SelfBurn( float flBurningTime )
  5604. {
  5605. #ifdef GAME_DLL
  5606. Burn( m_pOuter, NULL, flBurningTime );
  5607. #endif // GAME_DLL
  5608. }
  5609. //-----------------------------------------------------------------------------
  5610. // Purpose:
  5611. //-----------------------------------------------------------------------------
  5612. void CTFPlayerShared::MakeBleed( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon, float flBleedingTime, int nBleedDmg /* = TF_BLEEDING_DMG */, bool bPermanentBleeding /*= false*/ )
  5613. {
  5614. #ifdef CLIENT_DLL
  5615. #else
  5616. // Don't bother if they are dead
  5617. if ( !m_pOuter->IsAlive() )
  5618. return;
  5619. // Required for the CTakeDamageInfo we create later
  5620. Assert( pPlayer && pWeapon );
  5621. if ( !pPlayer && !pWeapon )
  5622. return;
  5623. float flExpireTime = gpGlobals->curtime + flBleedingTime;
  5624. // See if this weapon has already applied a bleed and extend the time
  5625. FOR_EACH_VEC( m_PlayerBleeds, i )
  5626. {
  5627. if ( m_PlayerBleeds[i].hBleedingAttacker && m_PlayerBleeds[i].hBleedingAttacker == pPlayer &&
  5628. m_PlayerBleeds[i].hBleedingWeapon && m_PlayerBleeds[i].hBleedingWeapon == pWeapon )
  5629. {
  5630. if ( flExpireTime > m_PlayerBleeds[i].flBleedingRemoveTime )
  5631. {
  5632. m_PlayerBleeds[i].flBleedingRemoveTime = flExpireTime;
  5633. return;
  5634. }
  5635. }
  5636. }
  5637. // New bleed source
  5638. bleed_struct_t bleedinfo =
  5639. {
  5640. pPlayer, // hBleedingAttacker
  5641. pWeapon, // hBleedingWeapon
  5642. flBleedingTime, // flBleedingTime
  5643. flExpireTime, // flBleedingRemoveTime
  5644. nBleedDmg, // nBleedDmg
  5645. bPermanentBleeding
  5646. };
  5647. m_PlayerBleeds.AddToTail( bleedinfo );
  5648. if ( !InCond( TF_COND_BLEEDING ) )
  5649. {
  5650. AddCond( TF_COND_BLEEDING, -1.f, pPlayer );
  5651. }
  5652. #endif
  5653. }
  5654. #ifdef GAME_DLL
  5655. void CTFPlayerShared::StopBleed( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon )
  5656. {
  5657. FOR_EACH_VEC_BACK( m_PlayerBleeds, i )
  5658. {
  5659. const bleed_struct_t& bleed = m_PlayerBleeds[i];
  5660. if ( bleed.hBleedingAttacker == pPlayer && bleed.hBleedingWeapon == pWeapon )
  5661. {
  5662. m_PlayerBleeds.FastRemove( i );
  5663. }
  5664. }
  5665. // remove condition right away when the list is empty
  5666. if ( !m_PlayerBleeds.Count() )
  5667. {
  5668. RemoveCond( TF_COND_BLEEDING );
  5669. }
  5670. }
  5671. #endif // GAME_DLL
  5672. //-----------------------------------------------------------------------------
  5673. // Purpose:
  5674. //-----------------------------------------------------------------------------
  5675. void CTFPlayerShared::OnRemoveBurning( void )
  5676. {
  5677. #ifdef CLIENT_DLL
  5678. m_pOuter->StopBurningSound();
  5679. if ( m_pOuter->m_nOldWaterLevel > 0 )
  5680. {
  5681. m_pOuter->ParticleProp()->Create( "water_burning_steam", PATTACH_ABSORIGIN );
  5682. }
  5683. if ( m_pOuter->m_pBurningEffect )
  5684. {
  5685. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pBurningEffect );
  5686. m_pOuter->m_pBurningEffect = NULL;
  5687. }
  5688. if ( m_pOuter->IsLocalPlayer() )
  5689. {
  5690. // only remove the overlay if it is the burning
  5691. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  5692. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_BURNING ) )
  5693. {
  5694. view->SetScreenOverlayMaterial( NULL );
  5695. }
  5696. }
  5697. m_pOuter->m_flBurnEffectStartTime = 0;
  5698. #else
  5699. m_hBurnAttacker = NULL;
  5700. m_hOriginalBurnAttacker = NULL;
  5701. m_hBurnWeapon = NULL;
  5702. m_pOuter->ClearBurnFromBehindAttackers();
  5703. // If we were on fire and now we're not, and we're still alive, then give ourself some credit
  5704. // for surviving this fire if we have any items that track it.
  5705. if ( m_nPlayerState == TF_STATE_ACTIVE )
  5706. {
  5707. HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( m_pOuter, kKillEaterEvent_FiresSurvived );
  5708. }
  5709. #endif
  5710. }
  5711. //-----------------------------------------------------------------------------
  5712. // Purpose:
  5713. //-----------------------------------------------------------------------------
  5714. void CTFPlayerShared::OnRemoveOverhealed( void )
  5715. {
  5716. #ifdef CLIENT_DLL
  5717. if ( !m_pOuter->IsLocalPlayer() )
  5718. {
  5719. m_pOuter->UpdateOverhealEffect();
  5720. }
  5721. #endif
  5722. }
  5723. //-----------------------------------------------------------------------------
  5724. // Purpose:
  5725. //-----------------------------------------------------------------------------
  5726. void CTFPlayerShared::OnRemoveDemoCharge( void )
  5727. {
  5728. #ifdef CLIENT_DLL
  5729. m_pOuter->StopSound( "DemoCharge.ChargeCritOn" );
  5730. m_pOuter->EmitSound( "DemoCharge.ChargeCritOff" );
  5731. UpdateCritBoostEffect();
  5732. #endif // CLIENT_DLL
  5733. }
  5734. //-----------------------------------------------------------------------------
  5735. // Purpose:
  5736. //-----------------------------------------------------------------------------
  5737. void CTFPlayerShared::OnRemoveCritBoost( void )
  5738. {
  5739. #ifdef CLIENT_DLL
  5740. UpdateCritBoostEffect();
  5741. #endif
  5742. }
  5743. //-----------------------------------------------------------------------------
  5744. // Purpose:
  5745. //-----------------------------------------------------------------------------
  5746. void CTFPlayerShared::OnRemoveTmpDamageBonus( void )
  5747. {
  5748. m_flTmpDamageBonusAmount = 1.0;
  5749. }
  5750. //-----------------------------------------------------------------------------
  5751. // Purpose:
  5752. //-----------------------------------------------------------------------------
  5753. void CTFPlayerShared::OnAddStealthed( void )
  5754. {
  5755. #ifdef CLIENT_DLL
  5756. if ( m_pOuter->GetPredictable() && ( !prediction->IsFirstTimePredicted() || m_bSyncingConditions ) )
  5757. return;
  5758. if ( !InCond( TF_COND_FEIGN_DEATH ) )
  5759. {
  5760. m_pOuter->EmitSound( "Player.Spy_Cloak" );
  5761. }
  5762. m_pOuter->RemoveAllDecals();
  5763. UpdateCritBoostEffect();
  5764. if ( m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST )
  5765. {
  5766. RemoveResistShield( &m_pOuter->m_pTempShield, m_pOuter );
  5767. }
  5768. #endif
  5769. bool bSetInvisChangeTime = true;
  5770. #ifdef CLIENT_DLL
  5771. if ( !m_pOuter->IsLocalPlayer() )
  5772. {
  5773. // We only clientside predict changetime for the local player
  5774. bSetInvisChangeTime = false;
  5775. }
  5776. if ( InCond( TF_COND_STEALTHED_USER_BUFF ) && m_pOuter->IsLocalPlayer() )
  5777. {
  5778. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_STEALTH, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  5779. if ( !IsErrorMaterial( pMaterial ) )
  5780. {
  5781. view->SetScreenOverlayMaterial( pMaterial );
  5782. }
  5783. }
  5784. #endif
  5785. if ( bSetInvisChangeTime )
  5786. {
  5787. if ( !InCond( TF_COND_FEIGN_DEATH ) && !InCond( TF_COND_STEALTHED_USER_BUFF ) )
  5788. {
  5789. float flInvisTime = tf_spy_invis_time.GetFloat();
  5790. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flInvisTime, mult_cloak_rate );
  5791. m_flInvisChangeCompleteTime = gpGlobals->curtime + flInvisTime;
  5792. }
  5793. else
  5794. {
  5795. m_flInvisChangeCompleteTime = gpGlobals->curtime; // Stealth immediately if we are in feign death.
  5796. }
  5797. }
  5798. // set our offhand weapon to be the invis weapon, but only for the spy's stealth
  5799. if ( InCond( TF_COND_STEALTHED ) )
  5800. {
  5801. for (int i = 0; i < m_pOuter->WeaponCount(); i++)
  5802. {
  5803. CTFWeaponInvis *pWpn = (CTFWeaponInvis *) m_pOuter->GetWeapon(i);
  5804. if ( !pWpn )
  5805. continue;
  5806. if ( pWpn->GetWeaponID() != TF_WEAPON_INVIS )
  5807. continue;
  5808. // try to switch to this weapon
  5809. m_pOuter->SetOffHandWeapon( pWpn );
  5810. m_bMotionCloak = pWpn->HasMotionCloak();
  5811. break;
  5812. }
  5813. }
  5814. m_pOuter->TeamFortress_SetSpeed();
  5815. #ifdef CLIENT_DLL
  5816. // Remove water balloon effect if it on player
  5817. m_pOuter->ParticleProp()->StopParticlesNamed( "balloontoss_drip", true );
  5818. m_pOuter->UpdateSpyStateChange();
  5819. m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) );
  5820. #endif
  5821. #ifdef GAME_DLL
  5822. m_flCloakStartTime = gpGlobals->curtime;
  5823. #endif
  5824. }
  5825. //-----------------------------------------------------------------------------
  5826. // Purpose:
  5827. //-----------------------------------------------------------------------------
  5828. void CTFPlayerShared::OnRemoveStealthed( void )
  5829. {
  5830. #ifdef CLIENT_DLL
  5831. if ( !m_bSyncingConditions )
  5832. return;
  5833. CTFWeaponInvis *pWpn = (CTFWeaponInvis *) m_pOuter->Weapon_OwnsThisID( TF_WEAPON_INVIS );
  5834. int iReducedCloak = 0;
  5835. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iReducedCloak, set_quiet_unstealth );
  5836. if ( iReducedCloak == 1 )
  5837. {
  5838. m_pOuter->EmitSound( "Player.Spy_UnCloakReduced" );
  5839. }
  5840. else if ( pWpn && pWpn->HasFeignDeath() )
  5841. {
  5842. m_pOuter->EmitSound( "Player.Spy_UnCloakFeignDeath" );
  5843. }
  5844. else
  5845. {
  5846. m_pOuter->EmitSound( "Player.Spy_UnCloak" );
  5847. }
  5848. UpdateCritBoostEffect( kCritBoost_ForceRefresh );
  5849. if ( m_pOuter->IsLocalPlayer() && !InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) )
  5850. {
  5851. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  5852. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_STEALTH ) )
  5853. {
  5854. view->SetScreenOverlayMaterial( NULL );
  5855. }
  5856. }
  5857. if ( !m_pOuter->m_pTempShield && GetCarryingRuneType() == RUNE_RESIST )
  5858. {
  5859. AddResistShield( &m_pOuter->m_pTempShield, m_pOuter, TF_COND_RUNE_RESIST );
  5860. }
  5861. #else
  5862. if ( m_flCloakStartTime > 0 )
  5863. {
  5864. // Calc a time and report every minute
  5865. float flCloaktime = ( gpGlobals->curtime - m_flCloakStartTime );
  5866. if ( flCloaktime > 0 )
  5867. {
  5868. EconEntity_OnOwnerKillEaterEventNoPartner(
  5869. dynamic_cast<CEconEntity *>( m_pOuter->GetEntityForLoadoutSlot( LOADOUT_POSITION_PDA2 ) ),
  5870. m_pOuter,
  5871. kKillEaterEvent_TimeCloaked,
  5872. (int)flCloaktime
  5873. );
  5874. }
  5875. m_flCloakStartTime = 0;
  5876. }
  5877. #endif
  5878. // End feign death if we leave stealth for some reason.
  5879. if ( InCond( TF_COND_FEIGN_DEATH ) )
  5880. {
  5881. RemoveCond( TF_COND_FEIGN_DEATH );
  5882. }
  5883. m_pOuter->HolsterOffHandWeapon();
  5884. m_pOuter->TeamFortress_SetSpeed();
  5885. m_bMotionCloak = false;
  5886. #ifdef CLIENT_DLL
  5887. m_pOuter->UpdateSpyStateChange();
  5888. m_pOuter->UpdateKillStreakEffects( GetStreak( kTFStreak_Kills ) );
  5889. #endif
  5890. #ifdef STAGING_ONLY
  5891. if ( HasPhaseCloakAbility() )
  5892. {
  5893. RemoveCond( TF_COND_STEALTHED_PHASE );
  5894. }
  5895. #endif
  5896. }
  5897. //-----------------------------------------------------------------------------
  5898. // Purpose:
  5899. //-----------------------------------------------------------------------------
  5900. void CTFPlayerShared::OnAddStealthedUserBuffFade( void )
  5901. {
  5902. #ifdef CLIENT_DLL
  5903. // If a player is firing their weapon while radius stealth hits them, we never
  5904. // get a chance to apply the screenoverlay effect, so apply it here instead.
  5905. if ( m_pOuter->IsLocalPlayer() )
  5906. {
  5907. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_STEALTH, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  5908. if ( !IsErrorMaterial( pMaterial ) )
  5909. {
  5910. view->SetScreenOverlayMaterial( pMaterial );
  5911. }
  5912. }
  5913. #endif // CLIENT_DLL
  5914. }
  5915. //-----------------------------------------------------------------------------
  5916. // Purpose:
  5917. //-----------------------------------------------------------------------------
  5918. void CTFPlayerShared::OnRemoveStealthedUserBuffFade( void )
  5919. {
  5920. #ifdef CLIENT_DLL
  5921. if ( m_pOuter->IsLocalPlayer() )
  5922. {
  5923. IMaterial *pMaterial = view->GetScreenOverlayMaterial();
  5924. if ( pMaterial && FStrEq( pMaterial->GetName(), TF_SCREEN_OVERLAY_MATERIAL_STEALTH ) )
  5925. {
  5926. view->SetScreenOverlayMaterial( NULL );
  5927. return;
  5928. }
  5929. }
  5930. #endif // CLIENT_DLL
  5931. }
  5932. //-----------------------------------------------------------------------------
  5933. // Purpose:
  5934. //-----------------------------------------------------------------------------
  5935. void CTFPlayerShared::OnAddFeignDeath( void )
  5936. {
  5937. #ifdef CLIENT_DLL
  5938. // STAGING_SPY
  5939. AddUberScreenEffect( m_pOuter );
  5940. #else
  5941. #endif
  5942. // Go stealth w/o sound or fade out.
  5943. if ( !IsStealthed() )
  5944. {
  5945. AddCond( TF_COND_STEALTHED, -1.f, m_pOuter );
  5946. }
  5947. // STAGING_SPY
  5948. // Add a speed boost while feigned and afterburn immunity while running away
  5949. AddCond( TF_COND_SPEED_BOOST, tf_feign_death_speed_duration.GetFloat() );
  5950. AddCond( TF_COND_AFTERBURN_IMMUNE, tf_feign_death_speed_duration.GetFloat() );
  5951. SetFeignDeathReady( false );
  5952. m_flFeignDeathEnd = gpGlobals->curtime + tf_feign_death_speed_duration.GetFloat();
  5953. }
  5954. //-----------------------------------------------------------------------------
  5955. // Purpose:
  5956. //-----------------------------------------------------------------------------
  5957. void CTFPlayerShared::OnRemoveFeignDeath( void )
  5958. {
  5959. #ifdef CLIENT_DLL
  5960. // STAGING_SPY
  5961. RemoveUberScreenEffect( m_pOuter );
  5962. #endif
  5963. // Previous code removed cloak meter, this has been moved to on RemoveStealth checking for steath type
  5964. // FeignDeath is the duration of cloak where speed, no shimmer and damage reduction take place
  5965. }
  5966. //-----------------------------------------------------------------------------
  5967. // Purpose:
  5968. //-----------------------------------------------------------------------------
  5969. void CTFPlayerShared::OnRemoveDisguising( void )
  5970. {
  5971. #ifdef CLIENT_DLL
  5972. if ( m_pOuter->m_pDisguisingEffect )
  5973. {
  5974. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pDisguisingEffect );
  5975. m_pOuter->m_pDisguisingEffect = NULL;
  5976. }
  5977. #else
  5978. m_nDesiredDisguiseTeam = TF_SPY_UNDEFINED;
  5979. // Do not reset this value, we use the last desired disguise class for the
  5980. // 'lastdisguise' command
  5981. //m_nDesiredDisguiseClass = TF_CLASS_UNDEFINED;
  5982. #endif
  5983. }
  5984. //-----------------------------------------------------------------------------
  5985. // Purpose:
  5986. //-----------------------------------------------------------------------------
  5987. void CTFPlayerShared::OnRemoveDisguised( void )
  5988. {
  5989. #ifdef CLIENT_DLL
  5990. if ( m_pOuter->GetPredictable() && ( !prediction->IsFirstTimePredicted() || m_bSyncingConditions ) )
  5991. return;
  5992. // if local player is on the other team, reset the model of this player
  5993. CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  5994. if ( !m_pOuter->InSameTeam( pLocalPlayer ) )
  5995. {
  5996. TFPlayerClassData_t *pData = GetPlayerClassData( TF_CLASS_SPY );
  5997. int iIndex = modelinfo->GetModelIndex( pData->GetModelName() );
  5998. m_pOuter->SetModelIndex( iIndex );
  5999. }
  6000. m_pOuter->EmitSound( "Player.Spy_Disguise" );
  6001. // They may have called for medic and created a visible medic bubble
  6002. m_pOuter->StopSaveMeEffect( true );
  6003. m_pOuter->StopTauntWithMeEffect();
  6004. UpdateCritBoostEffect( kCritBoost_ForceRefresh );
  6005. m_pOuter->UpdateSpyStateChange();
  6006. #else
  6007. m_nDisguiseTeam = TF_SPY_UNDEFINED;
  6008. m_nDisguiseClass.Set( TF_CLASS_UNDEFINED );
  6009. m_nDisguiseSkinOverride = 0;
  6010. m_hDisguiseTarget.Set( NULL );
  6011. m_iDisguiseTargetIndex = TF_DISGUISE_TARGET_INDEX_NONE;
  6012. m_iDisguiseHealth = 0;
  6013. SetDisguiseBody( 0 );
  6014. m_iDisguiseAmmo = 0;
  6015. // Update the player model and skin.
  6016. m_pOuter->UpdateModel();
  6017. m_pOuter->ClearExpression();
  6018. m_pOuter->ClearDisguiseWeaponList();
  6019. RemoveDisguiseWeapon();
  6020. RemoveDisguiseWearables();
  6021. #endif
  6022. m_pOuter->TeamFortress_SetSpeed();
  6023. }
  6024. //-----------------------------------------------------------------------------
  6025. // Purpose:
  6026. //-----------------------------------------------------------------------------
  6027. void CTFPlayerShared::OnAddBurning( void )
  6028. {
  6029. #ifdef CLIENT_DLL
  6030. // Start the burning effect
  6031. if ( !m_pOuter->m_pBurningEffect )
  6032. {
  6033. if ( !( IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO ) && m_pOuter->IsLocalPlayer() ) )
  6034. {
  6035. const char *pEffectName = ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? "burningplayer_red" : "burningplayer_blue";
  6036. m_pOuter->m_pBurningEffect = m_pOuter->ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW );
  6037. }
  6038. m_pOuter->m_flBurnEffectStartTime = gpGlobals->curtime;
  6039. }
  6040. // set the burning screen overlay
  6041. if ( m_pOuter->IsLocalPlayer() )
  6042. {
  6043. IMaterial *pMaterial = materials->FindMaterial( TF_SCREEN_OVERLAY_MATERIAL_BURNING, TEXTURE_GROUP_CLIENT_EFFECTS, false );
  6044. if ( !IsErrorMaterial( pMaterial ) )
  6045. {
  6046. view->SetScreenOverlayMaterial( pMaterial );
  6047. }
  6048. }
  6049. #endif
  6050. // play a fire-starting sound
  6051. m_pOuter->EmitSound( "Fire.Engulf" );
  6052. }
  6053. //-----------------------------------------------------------------------------
  6054. // Purpose:
  6055. //-----------------------------------------------------------------------------
  6056. void CTFPlayerShared::OnAddOverhealed( void )
  6057. {
  6058. #ifdef CLIENT_DLL
  6059. // Start the Overheal effect
  6060. if ( !m_pOuter->IsLocalPlayer() )
  6061. {
  6062. m_pOuter->UpdateOverhealEffect();
  6063. }
  6064. #endif
  6065. }
  6066. //-----------------------------------------------------------------------------
  6067. // Purpose:
  6068. //-----------------------------------------------------------------------------
  6069. void CTFPlayerShared::OnAddStunned( void )
  6070. {
  6071. if ( IsControlStunned() || IsLoserStateStunned() )
  6072. {
  6073. #ifdef CLIENT_DLL
  6074. if ( GetActiveStunInfo() )
  6075. {
  6076. if ( !m_pOuter->m_pStunnedEffect && !( GetActiveStunInfo()->iStunFlags & TF_STUN_NO_EFFECTS ) )
  6077. {
  6078. if ( ( GetActiveStunInfo()->iStunFlags & TF_STUN_BY_TRIGGER ) || InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  6079. {
  6080. const char* pEffectName = "yikes_fx";
  6081. m_pOuter->m_pStunnedEffect = m_pOuter->ParticleProp()->Create( pEffectName, PATTACH_POINT_FOLLOW, "head" );
  6082. }
  6083. else
  6084. {
  6085. const char* pEffectName = "conc_stars";
  6086. m_pOuter->m_pStunnedEffect = m_pOuter->ParticleProp()->Create( pEffectName, PATTACH_POINT_FOLLOW, "head" );
  6087. }
  6088. }
  6089. }
  6090. #endif
  6091. // Notify our weapon that we have been stunned.
  6092. CTFWeaponBase* pWpn = m_pOuter->GetActiveTFWeapon();
  6093. if ( pWpn )
  6094. {
  6095. pWpn->OnControlStunned();
  6096. }
  6097. if ( InCond( TF_COND_SHIELD_CHARGE ) )
  6098. {
  6099. SetDemomanChargeMeter( 0 );
  6100. RemoveCond( TF_COND_SHIELD_CHARGE );
  6101. }
  6102. m_pOuter->TeamFortress_SetSpeed();
  6103. }
  6104. }
  6105. //-----------------------------------------------------------------------------
  6106. // Purpose:
  6107. //-----------------------------------------------------------------------------
  6108. void CTFPlayerShared::OnRemoveStunned( void )
  6109. {
  6110. m_iStunFlags = 0;
  6111. m_hStunner = NULL;
  6112. #ifdef CLIENT_DLL
  6113. if ( m_pOuter->m_pStunnedEffect )
  6114. {
  6115. // Remove stun stars if they are still around.
  6116. // They might be if we died, etc.
  6117. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pStunnedEffect );
  6118. m_pOuter->m_pStunnedEffect = NULL;
  6119. }
  6120. #else
  6121. m_iStunIndex = -1;
  6122. m_PlayerStuns.RemoveAll();
  6123. #endif
  6124. m_pOuter->TeamFortress_SetSpeed();
  6125. }
  6126. //-----------------------------------------------------------------------------
  6127. // Purpose:
  6128. //-----------------------------------------------------------------------------
  6129. void CTFPlayerShared::ControlStunFading( void )
  6130. {
  6131. #ifdef CLIENT_DLL
  6132. if ( m_pOuter->m_pStunnedEffect )
  6133. {
  6134. // Remove stun stars early...
  6135. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pStunnedEffect );
  6136. m_pOuter->m_pStunnedEffect = NULL;
  6137. }
  6138. #endif
  6139. }
  6140. //-----------------------------------------------------------------------------
  6141. // Purpose:
  6142. //-----------------------------------------------------------------------------
  6143. void CTFPlayerShared::SetStunExpireTime( float flTime )
  6144. {
  6145. #ifdef GAME_DLL
  6146. if ( GetActiveStunInfo() )
  6147. {
  6148. GetActiveStunInfo()->flExpireTime = flTime;
  6149. }
  6150. #else
  6151. m_flStunEnd = flTime;
  6152. #endif
  6153. UpdateLegacyStunSystem();
  6154. }
  6155. //-----------------------------------------------------------------------------
  6156. // Purpose: Mirror stun info to the old system for networking
  6157. //-----------------------------------------------------------------------------
  6158. void CTFPlayerShared::UpdateLegacyStunSystem( void )
  6159. {
  6160. // What a mess.
  6161. #ifdef GAME_DLL
  6162. stun_struct_t *pStun = GetActiveStunInfo();
  6163. if ( pStun )
  6164. {
  6165. m_hStunner = pStun->hPlayer;
  6166. m_flStunFade = gpGlobals->curtime + pStun->flDuration;
  6167. m_flMovementStunTime = pStun->flDuration;
  6168. m_flStunEnd = pStun->flExpireTime;
  6169. if ( pStun->iStunFlags & TF_STUN_CONTROLS )
  6170. {
  6171. m_flStunEnd = pStun->flExpireTime;
  6172. }
  6173. m_iMovementStunAmount = pStun->flStunAmount;
  6174. m_iStunFlags = pStun->iStunFlags;
  6175. m_iMovementStunParity = ( m_iMovementStunParity + 1 ) & ( ( 1 << MOVEMENTSTUN_PARITY_BITS ) - 1 );
  6176. }
  6177. #else
  6178. m_ActiveStunInfo.hPlayer = m_hStunner;
  6179. m_ActiveStunInfo.flDuration = m_flMovementStunTime;
  6180. m_ActiveStunInfo.flExpireTime = m_flStunEnd;
  6181. m_ActiveStunInfo.flStartFadeTime = m_flStunEnd;
  6182. m_ActiveStunInfo.flStunAmount = m_iMovementStunAmount;
  6183. m_ActiveStunInfo.iStunFlags = m_iStunFlags;
  6184. #endif // GAME_DLL
  6185. }
  6186. //-----------------------------------------------------------------------------
  6187. // Purpose:
  6188. //-----------------------------------------------------------------------------
  6189. stun_struct_t *CTFPlayerShared::GetActiveStunInfo( void ) const
  6190. {
  6191. #ifdef GAME_DLL
  6192. return ( m_PlayerStuns.IsValidIndex( m_iStunIndex ) ) ? const_cast<stun_struct_t*>( &m_PlayerStuns[m_iStunIndex] ) : NULL;
  6193. #else
  6194. return ( m_iStunIndex >= 0 ) ? const_cast<stun_struct_t*>( &m_ActiveStunInfo ) : NULL;
  6195. #endif
  6196. }
  6197. //-----------------------------------------------------------------------------
  6198. // Purpose:
  6199. //-----------------------------------------------------------------------------
  6200. CTFPlayer *CTFPlayerShared::GetStunner( void )
  6201. {
  6202. return GetActiveStunInfo() ? GetActiveStunInfo()->hPlayer : NULL;
  6203. }
  6204. //-----------------------------------------------------------------------------
  6205. // Purpose:
  6206. //-----------------------------------------------------------------------------
  6207. void CTFPlayerShared::OnAddCritBoost( void )
  6208. {
  6209. #ifdef CLIENT_DLL
  6210. UpdateCritBoostEffect();
  6211. #endif
  6212. }
  6213. #ifdef CLIENT_DLL
  6214. //-----------------------------------------------------------------------------
  6215. // Purpose:
  6216. //-----------------------------------------------------------------------------
  6217. void CTFPlayerShared::UpdateCritBoostEffect( ECritBoostUpdateType eUpdateType )
  6218. {
  6219. bool bShouldDisplayCritBoostEffect = IsCritBoosted()
  6220. || InCond( TF_COND_ENERGY_BUFF )
  6221. //|| IsHypeBuffed()
  6222. || InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF );
  6223. if ( m_pOuter->GetActiveTFWeapon() )
  6224. {
  6225. bShouldDisplayCritBoostEffect &= m_pOuter->GetActiveTFWeapon()->CanBeCritBoosted();
  6226. }
  6227. // Never show crit boost effects when stealthed
  6228. bShouldDisplayCritBoostEffect &= !IsStealthed();
  6229. // Never show crit boost effects when disguised unless we're the local player (so crits show on our viewmodel)
  6230. if ( !m_pOuter->IsLocalPlayer() )
  6231. {
  6232. bShouldDisplayCritBoostEffect &= !InCond( TF_COND_DISGUISED );
  6233. }
  6234. // Remove our current crit-boosted effect if we're forcing a refresh (in which case we'll
  6235. // regenerate an effect below) or if we aren't supposed to have an effect active.
  6236. if ( eUpdateType == kCritBoost_ForceRefresh || !bShouldDisplayCritBoostEffect )
  6237. {
  6238. if ( m_pOuter->m_pCritBoostEffect )
  6239. {
  6240. Assert( m_pOuter->m_pCritBoostEffect->IsValid() );
  6241. if ( m_pOuter->m_pCritBoostEffect->GetOwner() )
  6242. {
  6243. m_pOuter->m_pCritBoostEffect->GetOwner()->ParticleProp()->StopEmissionAndDestroyImmediately( m_pOuter->m_pCritBoostEffect );
  6244. }
  6245. else
  6246. {
  6247. m_pOuter->m_pCritBoostEffect->StopEmission();
  6248. }
  6249. m_pOuter->m_pCritBoostEffect = NULL;
  6250. }
  6251. #ifdef CLIENT_DLL
  6252. if ( m_pCritBoostSoundLoop )
  6253. {
  6254. CSoundEnvelopeController::GetController().SoundDestroy( m_pCritBoostSoundLoop );
  6255. m_pCritBoostSoundLoop = NULL;
  6256. }
  6257. #endif
  6258. }
  6259. // Should we have an active crit effect?
  6260. if ( bShouldDisplayCritBoostEffect )
  6261. {
  6262. CBaseEntity *pWeapon = NULL;
  6263. // Use GetRenderedWeaponModel() instead?
  6264. if ( m_pOuter->IsLocalPlayer() )
  6265. {
  6266. pWeapon = m_pOuter->GetViewModel(0);
  6267. }
  6268. else
  6269. {
  6270. // is this player an enemy?
  6271. if ( m_pOuter->GetTeamNumber() != GetLocalPlayerTeam() )
  6272. {
  6273. // are they a cloaked spy? or disguised as someone who almost assuredly isn't also critboosted?
  6274. if ( IsStealthed() || InCond( TF_COND_STEALTHED_BLINK ) || InCond( TF_COND_DISGUISED ) )
  6275. return;
  6276. }
  6277. pWeapon = m_pOuter->GetActiveWeapon();
  6278. }
  6279. if ( pWeapon )
  6280. {
  6281. if ( !m_pOuter->m_pCritBoostEffect )
  6282. {
  6283. if ( InCond( TF_COND_DISGUISED ) && !m_pOuter->IsLocalPlayer() && m_pOuter->GetTeamNumber() != GetLocalPlayerTeam() )
  6284. {
  6285. const char *pEffectName = ( GetDisguiseTeam() == TF_TEAM_RED ) ? "critgun_weaponmodel_red" : "critgun_weaponmodel_blu";
  6286. m_pOuter->m_pCritBoostEffect = pWeapon->ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW );
  6287. }
  6288. else
  6289. {
  6290. const char *pEffectName = ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? "critgun_weaponmodel_red" : "critgun_weaponmodel_blu";
  6291. m_pOuter->m_pCritBoostEffect = pWeapon->ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW );
  6292. }
  6293. if ( m_pOuter->IsLocalPlayer() )
  6294. {
  6295. if ( m_pOuter->m_pCritBoostEffect )
  6296. {
  6297. ClientLeafSystem()->SetRenderGroup( m_pOuter->m_pCritBoostEffect->RenderHandle(), RENDER_GROUP_VIEW_MODEL_TRANSLUCENT );
  6298. }
  6299. }
  6300. }
  6301. else
  6302. {
  6303. m_pOuter->m_pCritBoostEffect->StartEmission();
  6304. }
  6305. Assert( m_pOuter->m_pCritBoostEffect->IsValid() );
  6306. }
  6307. #ifdef CLIENT_DLL
  6308. if ( m_pOuter->GetActiveTFWeapon() && !m_pCritBoostSoundLoop )
  6309. {
  6310. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  6311. CLocalPlayerFilter filter;
  6312. m_pCritBoostSoundLoop = controller.SoundCreate( filter, m_pOuter->entindex(), "Weapon_General.CritPower" );
  6313. controller.Play( m_pCritBoostSoundLoop, 1.0, 100 );
  6314. }
  6315. #endif
  6316. }
  6317. }
  6318. #endif
  6319. //-----------------------------------------------------------------------------
  6320. // Soda Popper Condition
  6321. //-----------------------------------------------------------------------------
  6322. void CTFPlayerShared::OnAddSodaPopperHype( void )
  6323. {
  6324. #ifdef CLIENT_DLL
  6325. if ( m_pOuter->IsLocalPlayer() )
  6326. {
  6327. m_pOuter->EmitSound( "DisciplineDevice.PowerUp" );
  6328. }
  6329. #endif // CLIENT_DLL
  6330. }
  6331. //-----------------------------------------------------------------------------
  6332. void CTFPlayerShared::OnRemoveSodaPopperHype( void )
  6333. {
  6334. #ifdef CLIENT_DLL
  6335. if ( m_pOuter->IsLocalPlayer() )
  6336. {
  6337. m_pOuter->EmitSound( "DisciplineDevice.PowerDown" );
  6338. }
  6339. #endif // CLIENT_DLL
  6340. }
  6341. //-----------------------------------------------------------------------------
  6342. // Purpose:
  6343. //-----------------------------------------------------------------------------
  6344. float CTFPlayerShared::GetStealthNoAttackExpireTime( void )
  6345. {
  6346. return m_flStealthNoAttackExpire;
  6347. }
  6348. //-----------------------------------------------------------------------------
  6349. // Purpose: Sets whether this player is dominating the specified other player
  6350. //-----------------------------------------------------------------------------
  6351. void CTFPlayerShared::SetPlayerDominated( CTFPlayer *pPlayer, bool bDominated )
  6352. {
  6353. int iPlayerIndex = pPlayer->entindex();
  6354. m_bPlayerDominated.Set( iPlayerIndex, bDominated );
  6355. pPlayer->m_Shared.SetPlayerDominatingMe( m_pOuter, bDominated );
  6356. }
  6357. //-----------------------------------------------------------------------------
  6358. // Purpose: Sets whether this player is being dominated by the other player
  6359. //-----------------------------------------------------------------------------
  6360. void CTFPlayerShared::SetPlayerDominatingMe( CTFPlayer *pPlayer, bool bDominated )
  6361. {
  6362. int iPlayerIndex = pPlayer->entindex();
  6363. m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
  6364. #ifdef GAME_DLL
  6365. if ( bDominated )
  6366. {
  6367. CTFPlayer *pDominatingPlayer = ToTFPlayer( pPlayer );
  6368. if ( pDominatingPlayer && pDominatingPlayer->IsPlayerClass( TF_CLASS_MEDIC ) )
  6369. {
  6370. CBaseEntity *pHealedEntity = pPlayer->MedicGetHealTarget();
  6371. CTFPlayer *pHealedPlayer = ToTFPlayer( pHealedEntity );
  6372. if ( pHealedPlayer && pHealedPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  6373. {
  6374. pHealedPlayer->AwardAchievement( ACHIEVEMENT_TF_HEAVY_EARN_MEDIC_DOMINATION );
  6375. }
  6376. }
  6377. }
  6378. #endif
  6379. }
  6380. //-----------------------------------------------------------------------------
  6381. // Purpose: Returns whether this player is dominating the specified other player
  6382. //-----------------------------------------------------------------------------
  6383. bool CTFPlayerShared::IsPlayerDominated( int iPlayerIndex )
  6384. {
  6385. #ifdef CLIENT_DLL
  6386. // On the client, we only have data for the local player.
  6387. // As a result, it's only valid to ask for dominations related to the local player
  6388. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  6389. if ( !pLocalPlayer )
  6390. return false;
  6391. Assert( m_pOuter->IsLocalPlayer() || pLocalPlayer->entindex() == iPlayerIndex );
  6392. if ( m_pOuter->IsLocalPlayer() )
  6393. return m_bPlayerDominated.Get( iPlayerIndex );
  6394. return pLocalPlayer->m_Shared.IsPlayerDominatingMe( m_pOuter->entindex() );
  6395. #else
  6396. // Server has all the data.
  6397. return m_bPlayerDominated.Get( iPlayerIndex );
  6398. #endif
  6399. }
  6400. //-----------------------------------------------------------------------------
  6401. // Purpose:
  6402. //-----------------------------------------------------------------------------
  6403. bool CTFPlayerShared::IsPlayerDominatingMe( int iPlayerIndex )
  6404. {
  6405. return m_bPlayerDominatingMe.Get( iPlayerIndex );
  6406. }
  6407. //-----------------------------------------------------------------------------
  6408. // Purpose: True if the given player is a spy disguised as our team.
  6409. //-----------------------------------------------------------------------------
  6410. bool CTFPlayerShared::IsSpyDisguisedAsMyTeam( CTFPlayer *pPlayer )
  6411. {
  6412. if ( !pPlayer )
  6413. return false;
  6414. if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) &&
  6415. pPlayer->GetTeamNumber() != m_pOuter->GetTeamNumber() &&
  6416. pPlayer->m_Shared.GetDisguiseTeam() == m_pOuter->GetTeamNumber() )
  6417. {
  6418. return true;
  6419. }
  6420. else
  6421. {
  6422. return false;
  6423. }
  6424. }
  6425. //-----------------------------------------------------------------------------
  6426. // Purpose:
  6427. //-----------------------------------------------------------------------------
  6428. void CTFPlayerShared::NoteLastDamageTime( int nDamage )
  6429. {
  6430. // we took damage
  6431. if ( ( nDamage > 5 || InCond( TF_COND_BLEEDING ) ) && !InCond( TF_COND_FEIGN_DEATH ) && InCond( TF_COND_STEALTHED ) )
  6432. {
  6433. m_flLastStealthExposeTime = gpGlobals->curtime;
  6434. AddCond( TF_COND_STEALTHED_BLINK );
  6435. }
  6436. }
  6437. //-----------------------------------------------------------------------------
  6438. // Purpose:
  6439. //-----------------------------------------------------------------------------
  6440. void CTFPlayerShared::OnSpyTouchedByEnemy( void )
  6441. {
  6442. if ( !InCond( TF_COND_FEIGN_DEATH ) && InCond( TF_COND_STEALTHED ) )
  6443. {
  6444. m_flLastStealthExposeTime = gpGlobals->curtime;
  6445. AddCond( TF_COND_STEALTHED_BLINK );
  6446. }
  6447. }
  6448. //-----------------------------------------------------------------------------
  6449. // Purpose:
  6450. //-----------------------------------------------------------------------------
  6451. bool CTFPlayerShared::IsEnteringOrExitingFullyInvisible( void )
  6452. {
  6453. return ( ( GetPercentInvisiblePrevious() != 1.f && GetPercentInvisible() == 1.f ) ||
  6454. ( GetPercentInvisiblePrevious() == 1.f && GetPercentInvisible() != 1.f ) );
  6455. }
  6456. //-----------------------------------------------------------------------------
  6457. // Purpose:
  6458. //-----------------------------------------------------------------------------
  6459. bool CTFPlayerShared::CanRuneCharge() const
  6460. {
  6461. return InCond( TF_COND_RUNE_SUPERNOVA );
  6462. }
  6463. #ifdef STAGING_ONLY
  6464. //-----------------------------------------------------------------------------
  6465. // Purpose:
  6466. //-----------------------------------------------------------------------------
  6467. bool CTFPlayerShared::HasPhaseCloakAbility( void )
  6468. {
  6469. int iPhaseStealth = 0;
  6470. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iPhaseStealth, ability_cloak_phase );
  6471. return iPhaseStealth > 0;
  6472. }
  6473. #endif // STAGING_ONLY
  6474. //-----------------------------------------------------------------------------
  6475. // Purpose:
  6476. //-----------------------------------------------------------------------------
  6477. void CTFPlayerShared::FadeInvis( float fAdditionalRateScale )
  6478. {
  6479. ETFCond nExpiringCondition = TF_COND_LAST;
  6480. if ( InCond( TF_COND_STEALTHED ) )
  6481. {
  6482. nExpiringCondition = TF_COND_STEALTHED;
  6483. RemoveCond( TF_COND_STEALTHED );
  6484. }
  6485. else if ( InCond( TF_COND_STEALTHED_USER_BUFF ) )
  6486. {
  6487. nExpiringCondition = TF_COND_STEALTHED_USER_BUFF;
  6488. RemoveCond( TF_COND_STEALTHED_USER_BUFF );
  6489. }
  6490. #ifdef GAME_DLL
  6491. // inform the bots
  6492. CTFWeaponInvis *pInvis = dynamic_cast< CTFWeaponInvis * >( m_pOuter->Weapon_OwnsThisID( TF_WEAPON_INVIS ) );
  6493. if ( pInvis )
  6494. {
  6495. TheNextBots().OnWeaponFired( m_pOuter, pInvis );
  6496. }
  6497. #endif
  6498. // If present, give our invisibility weapon a chance to override our decloak
  6499. // rate scale.
  6500. float flDecloakRateScale = 0.0f;
  6501. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, flDecloakRateScale, mult_decloak_rate );
  6502. // This comes from script, so sanity check the result.
  6503. if ( flDecloakRateScale <= 0.0f )
  6504. {
  6505. flDecloakRateScale = 1.0f;
  6506. }
  6507. float flInvisFadeTime = fAdditionalRateScale
  6508. * (tf_spy_invis_unstealth_time.GetFloat() * flDecloakRateScale);
  6509. if ( flInvisFadeTime < 0.15 )
  6510. {
  6511. // this was a force respawn, they can attack whenever
  6512. }
  6513. else if ( ( nExpiringCondition != TF_COND_STEALTHED_USER_BUFF ) && !InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) )
  6514. {
  6515. // next attack in some time
  6516. m_flStealthNoAttackExpire = gpGlobals->curtime + (tf_spy_cloak_no_attack_time.GetFloat() * flDecloakRateScale * fAdditionalRateScale);
  6517. }
  6518. m_flInvisChangeCompleteTime = gpGlobals->curtime + flInvisFadeTime;
  6519. }
  6520. //-----------------------------------------------------------------------------
  6521. // Purpose: Approach our desired level of invisibility
  6522. //-----------------------------------------------------------------------------
  6523. void CTFPlayerShared::InvisibilityThink( void )
  6524. {
  6525. if ( m_pOuter->GetPlayerClass()->GetClassIndex() != TF_CLASS_SPY && InCond( TF_COND_STEALTHED ) )
  6526. {
  6527. // Shouldn't happen, but it's a safety net
  6528. m_flInvisibility = 0.0f;
  6529. if ( InCond( TF_COND_STEALTHED ) )
  6530. {
  6531. RemoveCond( TF_COND_STEALTHED );
  6532. }
  6533. return;
  6534. }
  6535. float flTargetInvis = 0.0f;
  6536. float flTargetInvisScale = 1.0f;
  6537. if ( InCond( TF_COND_STEALTHED_BLINK ) || InCond( TF_COND_URINE ) )
  6538. {
  6539. // We were bumped into or hit for some damage.
  6540. flTargetInvisScale = TF_SPY_STEALTH_BLINKSCALE;
  6541. }
  6542. // Go invisible or appear.
  6543. if ( m_flInvisChangeCompleteTime > gpGlobals->curtime )
  6544. {
  6545. if ( IsStealthed() )
  6546. {
  6547. flTargetInvis = 1.0f - ( ( m_flInvisChangeCompleteTime - gpGlobals->curtime ) );
  6548. }
  6549. else
  6550. {
  6551. flTargetInvis = ( ( m_flInvisChangeCompleteTime - gpGlobals->curtime ) * 0.5f );
  6552. }
  6553. }
  6554. else
  6555. {
  6556. if ( IsStealthed() )
  6557. {
  6558. flTargetInvis = 1.0f;
  6559. m_flLastNoMovementTime = -1.f;
  6560. if ( m_bMotionCloak )
  6561. {
  6562. if ( m_flCloakMeter == 0.f )
  6563. {
  6564. Vector vVel = m_pOuter->GetAbsVelocity();
  6565. float fSpdSqr = vVel.LengthSqr();
  6566. flTargetInvis = RemapVal( fSpdSqr, 0, m_pOuter->MaxSpeed()*m_pOuter->MaxSpeed(), 1.0f, 0.5f );
  6567. }
  6568. else
  6569. {
  6570. flTargetInvis = 1.f;
  6571. }
  6572. }
  6573. }
  6574. else
  6575. {
  6576. flTargetInvis = 0.0f;
  6577. }
  6578. }
  6579. flTargetInvis *= flTargetInvisScale;
  6580. m_flPrevInvisibility = m_flInvisibility;
  6581. m_flInvisibility = clamp( flTargetInvis, 0.0f, 1.0f );
  6582. #ifdef STAGING_ONLY
  6583. // Sniper cloak
  6584. int iSniperCloak = 0;
  6585. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iSniperCloak, ability_cloak_sniper );
  6586. if ( iSniperCloak )
  6587. {
  6588. static const float flSniperCloakDelay = 2.f;
  6589. bool bMoving = !m_pOuter->GetAbsVelocity().IsZero();
  6590. bool bAiming = IsAiming();
  6591. if ( !bMoving && !bAiming && !IsCarryingObject() )
  6592. {
  6593. if ( m_flLastNoMovementTime == -1.f )
  6594. {
  6595. m_flLastNoMovementTime = gpGlobals->curtime;
  6596. }
  6597. if ( !InCond( TF_COND_STEALTHED_USER_BUFF ) && !m_flInvisibility && m_flLastNoMovementTime > 0.f && gpGlobals->curtime - m_flLastNoMovementTime > flSniperCloakDelay )
  6598. {
  6599. AddCond( TF_COND_STEALTHED_USER_BUFF, -1.f, m_pOuter );
  6600. }
  6601. }
  6602. else
  6603. {
  6604. if ( IsStealthed() )
  6605. {
  6606. FadeInvis( 0.1f );
  6607. }
  6608. m_flLastNoMovementTime = -1.f;
  6609. }
  6610. }
  6611. #endif // STAGING_ONLY
  6612. }
  6613. //-----------------------------------------------------------------------------
  6614. // Purpose: How invisible is the player [0..1]
  6615. //-----------------------------------------------------------------------------
  6616. float CTFPlayerShared::GetPercentInvisible( void ) const
  6617. {
  6618. #ifdef STAGING_ONLY
  6619. #ifdef CLIENT_DLL
  6620. // This is a client hack to make everyone else invisible to the local player
  6621. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  6622. if ( pLocalTFPlayer && pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) && pLocalTFPlayer != m_pOuter )
  6623. return 1.f;
  6624. #endif // CLIENT_DLL
  6625. #endif // STAGING_ONLY
  6626. return m_flInvisibility;
  6627. }
  6628. //-----------------------------------------------------------------------------
  6629. // Purpose:
  6630. //-----------------------------------------------------------------------------
  6631. bool CTFPlayerShared::IsCritBoosted( void ) const
  6632. {
  6633. bool bAllWeaponCritActive = ( InCond( TF_COND_CRITBOOSTED ) ||
  6634. InCond( TF_COND_CRITBOOSTED_PUMPKIN ) ||
  6635. InCond( TF_COND_CRITBOOSTED_USER_BUFF ) ||
  6636. #ifdef CLIENT_DLL
  6637. InCond( TF_COND_CRITBOOSTED_DEMO_CHARGE ) ||
  6638. #endif
  6639. InCond( TF_COND_CRITBOOSTED_FIRST_BLOOD ) ||
  6640. InCond( TF_COND_CRITBOOSTED_BONUS_TIME ) ||
  6641. InCond( TF_COND_CRITBOOSTED_CTF_CAPTURE ) ||
  6642. InCond( TF_COND_CRITBOOSTED_ON_KILL ) ||
  6643. InCond( TF_COND_CRITBOOSTED_CARD_EFFECT ) ||
  6644. InCond( TF_COND_CRITBOOSTED_RUNE_TEMP ) );
  6645. if ( bAllWeaponCritActive )
  6646. return true;
  6647. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( m_pOuter->GetActiveWeapon() );
  6648. if ( pWeapon )
  6649. {
  6650. if ( InCond( TF_COND_CRITBOOSTED_RAGE_BUFF ) && pWeapon->GetTFWpnData().m_iWeaponType == TF_WPN_TYPE_PRIMARY )
  6651. {
  6652. // Only primary weapon can be crit boosted by pyro rage
  6653. return true;
  6654. }
  6655. float flCritHealthPercent = 1.0f;
  6656. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flCritHealthPercent, mult_crit_when_health_is_below_percent );
  6657. if ( flCritHealthPercent < 1.0f && m_pOuter->HealthFraction() < flCritHealthPercent )
  6658. {
  6659. return true;
  6660. }
  6661. }
  6662. return false;
  6663. }
  6664. //-----------------------------------------------------------------------------
  6665. // Purpose:
  6666. //-----------------------------------------------------------------------------
  6667. bool CTFPlayerShared::IsInvulnerable( void ) const
  6668. {
  6669. bool bInvuln = InCond( TF_COND_INVULNERABLE ) ||
  6670. InCond( TF_COND_INVULNERABLE_USER_BUFF ) ||
  6671. InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) ||
  6672. InCond( TF_COND_INVULNERABLE_CARD_EFFECT );
  6673. return bInvuln;
  6674. }
  6675. //-----------------------------------------------------------------------------
  6676. // Purpose:
  6677. //-----------------------------------------------------------------------------
  6678. bool CTFPlayerShared::IsStealthed( void ) const
  6679. {
  6680. #ifdef STAGING_ONLY
  6681. #ifdef CLIENT_DLL
  6682. // This is a client hack to make everyone else invisible to the local player
  6683. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  6684. if ( pLocalTFPlayer && pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) && pLocalTFPlayer != m_pOuter )
  6685. return true;
  6686. #endif // CLIENT_DLL
  6687. #endif // STAGING_ONLY
  6688. return ( InCond( TF_COND_STEALTHED ) || InCond( TF_COND_STEALTHED_USER_BUFF ) || InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) );
  6689. }
  6690. bool CTFPlayerShared::CanBeDebuffed( void ) const
  6691. {
  6692. if ( IsInvulnerable() )
  6693. return false;
  6694. if ( InCond( TF_COND_PHASE ) || InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  6695. return false;
  6696. return true;
  6697. }
  6698. //-----------------------------------------------------------------------------
  6699. // Purpose: Start the process of disguising
  6700. //-----------------------------------------------------------------------------
  6701. void CTFPlayerShared::Disguise( int nTeam, int nClass, CTFPlayer* pDesiredTarget, bool bOnKill )
  6702. {
  6703. int nRealTeam = m_pOuter->GetTeamNumber();
  6704. int nRealClass = m_pOuter->GetPlayerClass()->GetClassIndex();
  6705. Assert ( ( nClass >= TF_CLASS_SCOUT ) && ( nClass <= TF_CLASS_ENGINEER ) );
  6706. // we're not a spy
  6707. if ( nRealClass != TF_CLASS_SPY )
  6708. {
  6709. return;
  6710. }
  6711. if ( InCond( TF_COND_TAUNTING ) )
  6712. {
  6713. // not allowed to disguise while taunting
  6714. return;
  6715. }
  6716. // we're not disguising as anything but ourselves (so reset everything)
  6717. if ( nRealTeam == nTeam && nRealClass == nClass )
  6718. {
  6719. RemoveDisguise();
  6720. return;
  6721. }
  6722. // Ignore disguise of the same type, unless we're using 'Your Eternal Reward'
  6723. if ( nTeam == m_nDisguiseTeam && nClass == m_nDisguiseClass && !bOnKill )
  6724. {
  6725. #ifdef GAME_DLL
  6726. DetermineDisguiseWeapon( false );
  6727. #endif
  6728. return;
  6729. }
  6730. // invalid team
  6731. if ( nTeam <= TEAM_SPECTATOR || nTeam >= TF_TEAM_COUNT )
  6732. {
  6733. return;
  6734. }
  6735. // invalid class
  6736. if ( nClass <= TF_CLASS_UNDEFINED || nClass >= TF_CLASS_COUNT )
  6737. {
  6738. return;
  6739. }
  6740. // are we already in the middle of disguising as this class?
  6741. // (the lastdisguise key might get pushed multiple times before the disguise is complete)
  6742. if ( InCond( TF_COND_DISGUISING ) )
  6743. {
  6744. if ( nTeam == m_nDesiredDisguiseTeam && nClass == m_nDesiredDisguiseClass )
  6745. {
  6746. return;
  6747. }
  6748. }
  6749. m_hDesiredDisguiseTarget.Set( pDesiredTarget );
  6750. m_nDesiredDisguiseClass = nClass;
  6751. m_nDesiredDisguiseTeam = nTeam;
  6752. m_bLastDisguisedAsOwnTeam = ( m_nDesiredDisguiseTeam == m_pOuter->GetTeamNumber() );
  6753. AddCond( TF_COND_DISGUISING );
  6754. // Start the think to complete our disguise
  6755. float flTimeToDisguise = TF_TIME_TO_DISGUISE;
  6756. //CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pOuter, iTimeToDisguise, disguise_speed_penalty ); // Unused Attr
  6757. // STAGING_SPY
  6758. // Quick disguise if you already disguised
  6759. if ( InCond( TF_COND_DISGUISED ) )
  6760. {
  6761. flTimeToDisguise = TF_TIME_TO_QUICK_DISGUISE;
  6762. }
  6763. if ( pDesiredTarget )
  6764. {
  6765. flTimeToDisguise = 0;
  6766. }
  6767. m_flDisguiseCompleteTime = gpGlobals->curtime + flTimeToDisguise;
  6768. }
  6769. //-----------------------------------------------------------------------------
  6770. // Purpose: Set our target with a player we've found to emulate
  6771. //-----------------------------------------------------------------------------
  6772. #ifndef CLIENT_DLL
  6773. void CTFPlayerShared::FindDisguiseTarget( void )
  6774. {
  6775. if ( m_hDesiredDisguiseTarget )
  6776. {
  6777. m_hDisguiseTarget.Set( m_hDesiredDisguiseTarget.Get() );
  6778. m_hDesiredDisguiseTarget.Set( NULL );
  6779. }
  6780. else
  6781. {
  6782. m_hDisguiseTarget = m_pOuter->TeamFortress_GetDisguiseTarget( m_nDisguiseTeam, m_nDisguiseClass );
  6783. }
  6784. if ( m_hDisguiseTarget )
  6785. {
  6786. m_iDisguiseTargetIndex.Set( m_hDisguiseTarget.Get()->entindex() );
  6787. Assert( m_iDisguiseTargetIndex >= 1 && m_iDisguiseTargetIndex <= MAX_PLAYERS );
  6788. }
  6789. else
  6790. {
  6791. m_iDisguiseTargetIndex.Set( TF_DISGUISE_TARGET_INDEX_NONE );
  6792. }
  6793. m_pOuter->CreateDisguiseWeaponList( ToTFPlayer( m_hDisguiseTarget.Get() ) );
  6794. }
  6795. #endif
  6796. int CTFPlayerShared::GetDisguiseTeam( void ) const
  6797. {
  6798. return InCond( TF_COND_DISGUISED_AS_DISPENSER ) ? (int)( ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED ) : m_nDisguiseTeam;
  6799. }
  6800. //-----------------------------------------------------------------------------
  6801. // Purpose: Complete our disguise
  6802. //-----------------------------------------------------------------------------
  6803. void CTFPlayerShared::CompleteDisguise( void )
  6804. {
  6805. AddCond( TF_COND_DISGUISED );
  6806. m_nDisguiseClass = m_nDesiredDisguiseClass;
  6807. m_nDisguiseTeam = m_nDesiredDisguiseTeam;
  6808. if ( m_nDisguiseClass == TF_CLASS_SPY )
  6809. {
  6810. m_nMaskClass = rand()%9+1;
  6811. }
  6812. RemoveCond( TF_COND_DISGUISING );
  6813. #ifdef GAME_DLL
  6814. // Update the player model and skin.
  6815. m_pOuter->UpdateModel();
  6816. m_pOuter->ClearExpression();
  6817. FindDisguiseTarget();
  6818. if ( GetDisguiseTarget() )
  6819. {
  6820. m_iDisguiseHealth = GetDisguiseTarget()->GetHealth();
  6821. if ( m_iDisguiseHealth <= 0 || !GetDisguiseTarget()->IsAlive() )
  6822. {
  6823. // If we disguised as an enemy who is currently dead, just set us to full health.
  6824. m_iDisguiseHealth = GetDisguiseMaxHealth();
  6825. }
  6826. }
  6827. else
  6828. {
  6829. int iMaxHealth = m_pOuter->GetMaxHealth();
  6830. m_iDisguiseHealth = (int)random->RandomInt( iMaxHealth / 2, iMaxHealth );
  6831. }
  6832. // In Medieval mode, don't force primary weapon because most classes just have melee weapons
  6833. DetermineDisguiseWeapon( !TFGameRules()->IsInMedievalMode() );
  6834. DetermineDisguiseWearables();
  6835. #endif
  6836. m_pOuter->TeamFortress_SetSpeed();
  6837. m_flDisguiseCompleteTime = 0.0f;
  6838. }
  6839. //-----------------------------------------------------------------------------
  6840. // Purpose:
  6841. //-----------------------------------------------------------------------------
  6842. void CTFPlayerShared::SetDisguiseHealth( int iDisguiseHealth )
  6843. {
  6844. m_iDisguiseHealth = iDisguiseHealth;
  6845. }
  6846. //-----------------------------------------------------------------------------
  6847. // Purpose:
  6848. //-----------------------------------------------------------------------------
  6849. int CTFPlayerShared::GetDisguiseMaxHealth( void )
  6850. {
  6851. TFPlayerClassData_t *pClass = g_pTFPlayerClassDataMgr->Get( GetDisguiseClass() );
  6852. if ( pClass )
  6853. {
  6854. return pClass->m_nMaxHealth;
  6855. }
  6856. else
  6857. {
  6858. return m_pOuter->GetMaxHealth();
  6859. }
  6860. }
  6861. //-----------------------------------------------------------------------------
  6862. // Purpose:
  6863. //-----------------------------------------------------------------------------
  6864. void CTFPlayerShared::RemoveDisguise( void )
  6865. {
  6866. if ( GetDisguiseTeam() != m_pOuter->GetTeamNumber() )
  6867. {
  6868. if ( InCond( TF_COND_TELEPORTED ) )
  6869. {
  6870. RemoveCond( TF_COND_TELEPORTED );
  6871. }
  6872. }
  6873. RemoveCond( TF_COND_DISGUISED );
  6874. RemoveCond( TF_COND_DISGUISING );
  6875. AddCond( TF_COND_DISGUISE_WEARINGOFF, 0.5f );
  6876. }
  6877. #ifdef GAME_DLL
  6878. //-----------------------------------------------------------------------------
  6879. // Purpose:
  6880. //-----------------------------------------------------------------------------
  6881. void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
  6882. {
  6883. Assert( m_pOuter->GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY );
  6884. const char* strDisguiseWeapon = NULL;
  6885. CTFPlayer *pDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
  6886. TFPlayerClassData_t *pData = GetPlayerClassData( m_nDisguiseClass );
  6887. if ( pDisguiseTarget && (pDisguiseTarget->GetPlayerClass()->GetClassIndex() != m_nDisguiseClass) )
  6888. {
  6889. pDisguiseTarget = NULL;
  6890. }
  6891. // Determine which slot we have active.
  6892. int iCurrentSlot = 0;
  6893. if ( m_pOuter->GetActiveTFWeapon() && !bForcePrimary )
  6894. {
  6895. iCurrentSlot = m_pOuter->GetActiveTFWeapon()->GetSlot();
  6896. if ( (iCurrentSlot == 3) && // Cig Case, so they are using the menu not a key bind to disguise.
  6897. m_pOuter->GetLastWeapon() )
  6898. {
  6899. iCurrentSlot = m_pOuter->GetLastWeapon()->GetSlot();
  6900. }
  6901. }
  6902. CTFWeaponBase *pItemWeapon = NULL;
  6903. if ( pDisguiseTarget )
  6904. {
  6905. CTFWeaponBase *pLastDisguiseWeapon = m_hDisguiseWeapon;
  6906. CTFWeaponBase *pFirstValidWeapon = NULL;
  6907. // Cycle through the target's weapons and see if we have a match.
  6908. // Note that it's possible the disguise target doesn't have a weapon in the slot we want,
  6909. // for example if they have replaced it with an unlockable that isn't a weapon (wearable).
  6910. for ( int i=0; i<m_pOuter->m_hDisguiseWeaponList.Count(); ++i )
  6911. {
  6912. CTFWeaponBase *pWeapon = m_pOuter->m_hDisguiseWeaponList[i];
  6913. if ( !pWeapon )
  6914. continue;
  6915. if ( !pFirstValidWeapon )
  6916. {
  6917. pFirstValidWeapon = pWeapon;
  6918. }
  6919. // skip passtime gun
  6920. if ( pWeapon->GetWeaponID() == TF_WEAPON_PASSTIME_GUN )
  6921. {
  6922. continue;
  6923. }
  6924. if ( pWeapon->GetSlot() == iCurrentSlot )
  6925. {
  6926. pItemWeapon = pWeapon;
  6927. break;
  6928. }
  6929. }
  6930. if ( !pItemWeapon )
  6931. {
  6932. if ( pLastDisguiseWeapon )
  6933. {
  6934. pItemWeapon = pLastDisguiseWeapon;
  6935. }
  6936. else if ( pFirstValidWeapon )
  6937. {
  6938. pItemWeapon = pFirstValidWeapon;
  6939. }
  6940. }
  6941. if ( pItemWeapon )
  6942. {
  6943. strDisguiseWeapon = pItemWeapon->GetClassname();
  6944. }
  6945. }
  6946. if ( !pItemWeapon && pData )
  6947. {
  6948. // We have not found our item yet, so cycle through the class's default weapons
  6949. // to find a match.
  6950. for ( int i=0; i<TF_PLAYER_WEAPON_COUNT; ++i )
  6951. {
  6952. if ( pData->m_aWeapons[i] == TF_WEAPON_NONE )
  6953. continue;
  6954. const char *pWpnName = WeaponIdToAlias( pData->m_aWeapons[i] );
  6955. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pWpnName );
  6956. Assert( hWpnInfo != GetInvalidWeaponInfoHandle() );
  6957. CTFWeaponInfo *pWeaponInfo = dynamic_cast<CTFWeaponInfo*>( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  6958. if ( pWeaponInfo->iSlot == iCurrentSlot )
  6959. {
  6960. strDisguiseWeapon = pWeaponInfo->szClassName;
  6961. }
  6962. }
  6963. }
  6964. if ( strDisguiseWeapon )
  6965. {
  6966. // Remove the old disguise weapon, if any.
  6967. RemoveDisguiseWeapon();
  6968. CEconItemView *pItem = NULL;
  6969. if ( pItemWeapon )
  6970. {
  6971. // We are copying a generated, non-base item.
  6972. CAttributeContainer *pContainer = pItemWeapon->GetAttributeContainer();
  6973. if ( pContainer )
  6974. {
  6975. pItem = pContainer->GetItem();
  6976. }
  6977. }
  6978. // We may need a sub-type if we're a builder. Otherwise we'll always appear as a engineer's workbox.
  6979. int iSubType = 0;
  6980. if ( Q_strcmp( strDisguiseWeapon, "tf_weapon_builder" ) == 0 )
  6981. {
  6982. return; // Temporary.
  6983. }
  6984. m_hDisguiseWeapon.Set( dynamic_cast<CTFWeaponBase*>(m_pOuter->GiveNamedItem( strDisguiseWeapon, iSubType, pItem, true )) );
  6985. if ( m_hDisguiseWeapon )
  6986. {
  6987. m_hDisguiseWeapon->SetTouch( NULL );// no touch
  6988. m_hDisguiseWeapon->SetOwner( dynamic_cast<CBaseCombatCharacter*>(m_pOuter) );
  6989. m_hDisguiseWeapon->SetOwnerEntity( m_pOuter );
  6990. m_hDisguiseWeapon->SetParent( m_pOuter );
  6991. m_hDisguiseWeapon->FollowEntity( m_pOuter, true );
  6992. m_hDisguiseWeapon->m_iState = WEAPON_IS_ACTIVE;
  6993. m_hDisguiseWeapon->m_bDisguiseWeapon = true;
  6994. m_hDisguiseWeapon->SetContextThink( &CTFWeaponBase::DisguiseWeaponThink, gpGlobals->curtime + 0.5, "DisguiseWeaponThink" );
  6995. // Ammo/clip state is displayed to attached medics
  6996. m_iDisguiseAmmo = 0;
  6997. if ( !m_hDisguiseWeapon->IsMeleeWeapon() )
  6998. {
  6999. // Use the player we're disguised as if possible
  7000. if ( pDisguiseTarget )
  7001. {
  7002. CTFWeaponBase *pWeapon = pDisguiseTarget->GetActiveTFWeapon();
  7003. if ( pWeapon && pWeapon->GetWeaponID() == m_hDisguiseWeapon->GetWeaponID() )
  7004. {
  7005. m_iDisguiseAmmo = pWeapon->UsesClipsForAmmo1() ?
  7006. pWeapon->Clip1() :
  7007. pDisguiseTarget->GetAmmoCount( pWeapon->GetPrimaryAmmoType() );
  7008. }
  7009. }
  7010. // Otherwise display a faked ammo count
  7011. if ( !m_iDisguiseAmmo )
  7012. {
  7013. int nMaxCount = m_hDisguiseWeapon->UsesClipsForAmmo1() ?
  7014. m_hDisguiseWeapon->GetMaxClip1() :
  7015. m_pOuter->GetMaxAmmo( m_hDisguiseWeapon->GetPrimaryAmmoType(), m_nDisguiseClass );
  7016. m_iDisguiseAmmo = (int)random->RandomInt( 1, nMaxCount );
  7017. }
  7018. }
  7019. }
  7020. }
  7021. }
  7022. void CTFPlayerShared::DetermineDisguiseWearables()
  7023. {
  7024. CTFPlayer *pDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
  7025. if ( !pDisguiseTarget )
  7026. return;
  7027. // Remove any existing disguise wearables.
  7028. RemoveDisguiseWearables();
  7029. if ( GetDisguiseClass() != pDisguiseTarget->GetPlayerClass()->GetClassIndex() )
  7030. return;
  7031. // Equip us with copies of our disguise target's wearables.
  7032. int iPlayerSkinOverride = 0;
  7033. for ( int i=0; i<pDisguiseTarget->GetNumWearables(); ++i )
  7034. {
  7035. CTFWearable *pWearable = dynamic_cast<CTFWearable*>( pDisguiseTarget->GetWearable( i ) );
  7036. if ( pWearable )
  7037. {
  7038. if ( pWearable->IsDisguiseWearable() )
  7039. continue; // Never copy a target's disguise wearables.
  7040. CEconItemView *pScriptItem = pWearable->GetAttributeContainer()->GetItem();
  7041. if ( pScriptItem && pScriptItem->IsValid() && pScriptItem->GetStaticData()->GetItemClass() )
  7042. {
  7043. CEconEntity *pNewItem = dynamic_cast<CEconEntity*>( m_pOuter->GiveNamedItem( pScriptItem->GetStaticData()->GetItemClass(), 0, pScriptItem ) );
  7044. CTFWearable *pNewWearable = dynamic_cast<CTFWearable*>( pNewItem );
  7045. Assert( pNewWearable );
  7046. if ( pNewWearable )
  7047. {
  7048. pNewWearable->SetDisguiseWearable( true );
  7049. pNewWearable->GiveTo( m_pOuter );
  7050. // copy over the level for levelable items
  7051. CTFWearableLevelableItem *pLevelableItem = dynamic_cast<CTFWearableLevelableItem*>( pWearable );
  7052. CTFWearableLevelableItem *pNewLevelableItem = dynamic_cast<CTFWearableLevelableItem*>( pNewWearable );
  7053. if ( pLevelableItem && pNewLevelableItem )
  7054. {
  7055. int nBodyGroup = pNewLevelableItem->FindBodygroupByName( LEVELABLE_ITEM_BODYGROUP_NAME );
  7056. if ( nBodyGroup != -1 )
  7057. {
  7058. pNewLevelableItem->SetBodygroup( nBodyGroup, pLevelableItem->GetLevel() );
  7059. }
  7060. }
  7061. // find the first skin override item
  7062. if ( iPlayerSkinOverride == 0 )
  7063. {
  7064. CALL_ATTRIB_HOOK_INT_ON_OTHER( pNewWearable, iPlayerSkinOverride, player_skin_override );
  7065. }
  7066. }
  7067. }
  7068. }
  7069. }
  7070. m_nDisguiseSkinOverride = iPlayerSkinOverride;
  7071. }
  7072. void CTFPlayerShared::RemoveDisguiseWearables()
  7073. {
  7074. bool bFoundDisguiseWearable = true;
  7075. while ( bFoundDisguiseWearable )
  7076. {
  7077. int i = 0;
  7078. for ( ; i<m_pOuter->GetNumWearables(); ++i )
  7079. {
  7080. CTFWearable *pWearable = dynamic_cast<CTFWearable*>( m_pOuter->GetWearable( i ) );
  7081. if ( pWearable && pWearable->IsDisguiseWearable() )
  7082. {
  7083. // Every time we do this the list changes, so we have to loop through again.
  7084. pWearable->RemoveFrom( m_pOuter );
  7085. break;
  7086. }
  7087. }
  7088. if ( i == m_pOuter->GetNumWearables() )
  7089. {
  7090. bFoundDisguiseWearable = false;
  7091. }
  7092. }
  7093. }
  7094. #endif // GAME_DLL
  7095. //-----------------------------------------------------------------------------
  7096. // Purpose:
  7097. //-----------------------------------------------------------------------------
  7098. void CTFPlayerShared::ProcessDisguiseImpulse( CTFPlayer *pPlayer )
  7099. {
  7100. // Get the player owning the weapon.
  7101. if ( !pPlayer )
  7102. return;
  7103. if ( pPlayer->GetImpulse() > 200 )
  7104. {
  7105. char szImpulse[6];
  7106. Q_snprintf( szImpulse, sizeof( szImpulse ), "%d", pPlayer->GetImpulse() );
  7107. char szTeam[3];
  7108. Q_snprintf( szTeam, sizeof( szTeam ), "%c", szImpulse[1] );
  7109. char szClass[3];
  7110. Q_snprintf( szClass, sizeof( szClass ), "%c", szImpulse[2] );
  7111. // 'Your Eternal Reward' handling
  7112. bool bSwitchWeaponOnly = false;
  7113. if ( pPlayer->CanDisguise_OnKill() && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  7114. {
  7115. // Only trying to change the disguise weapon via 'lastdisguise'
  7116. if ( Q_atoi( szClass ) == pPlayer->m_Shared.GetDisguiseClass() && Q_atoi( szTeam ) == pPlayer->m_Shared.GetDisguiseTeam() )
  7117. {
  7118. bSwitchWeaponOnly = true;
  7119. }
  7120. }
  7121. if ( pPlayer->CanDisguise() || bSwitchWeaponOnly )
  7122. {
  7123. // intercepting the team value and reassigning what gets passed into Disguise()
  7124. // because the team numbers in the client menu don't match the #define values for the teams
  7125. pPlayer->m_Shared.Disguise( Q_atoi( szTeam ), Q_atoi( szClass ) );
  7126. // Switch from the PDA to our previous weapon
  7127. if ( GetActiveTFWeapon() && GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_PDA_SPY )
  7128. {
  7129. pPlayer->SelectLastItem();
  7130. }
  7131. }
  7132. }
  7133. }
  7134. bool CTFPlayerShared::CanRecieveMedigunChargeEffect( medigun_charge_types eType ) const
  7135. {
  7136. bool bCanRecieve = true;
  7137. const CTFItem *pItem = m_pOuter->GetItem();
  7138. if ( pItem && pItem->GetItemID() == TF_ITEM_CAPTURE_FLAG )
  7139. {
  7140. bCanRecieve = false;
  7141. // The "flag" in Player Destruction doesn't block uber
  7142. const CCaptureFlag* pFlag = static_cast< const CCaptureFlag* >( pItem );
  7143. if ( pFlag->GetType() == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  7144. {
  7145. bCanRecieve = true;
  7146. }
  7147. if ( TFGameRules()->IsMannVsMachineMode() )
  7148. {
  7149. // allow bot flag carriers to be ubered
  7150. bCanRecieve = true;
  7151. }
  7152. if ( ( eType == MEDIGUN_CHARGE_MEGAHEAL )
  7153. || ( eType == MEDIGUN_CHARGE_BULLET_RESIST )
  7154. || ( eType == MEDIGUN_CHARGE_BLAST_RESIST )
  7155. || ( eType == MEDIGUN_CHARGE_FIRE_RESIST ) )
  7156. {
  7157. bCanRecieve = true;
  7158. }
  7159. }
  7160. if( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  7161. {
  7162. bCanRecieve &= ! HasPasstimeBall();
  7163. }
  7164. return bCanRecieve;
  7165. }
  7166. #ifdef GAME_DLL
  7167. //-----------------------------------------------------------------------------
  7168. // Purpose: Heal players.
  7169. // pPlayer is person who healed us
  7170. //-----------------------------------------------------------------------------
  7171. void CTFPlayerShared::Heal( CBaseEntity *pHealer, float flAmount, float flOverhealBonus, float flOverhealDecayMult, bool bDispenserHeal /* = false */, CTFPlayer *pHealScorer /* = NULL */ )
  7172. {
  7173. // If already healing, stop healing
  7174. float flHealAccum = 0;
  7175. if ( FindHealerIndex(pHealer) != m_aHealers.InvalidIndex() )
  7176. {
  7177. flHealAccum = StopHealing( pHealer );
  7178. }
  7179. healers_t newHealer;
  7180. newHealer.pHealer = pHealer;
  7181. newHealer.flAmount = flAmount;
  7182. newHealer.flHealAccum = flHealAccum;
  7183. newHealer.iKillsWhileBeingHealed = 0;
  7184. newHealer.flOverhealBonus = flOverhealBonus;
  7185. newHealer.flOverhealDecayMult = flOverhealDecayMult;
  7186. newHealer.bDispenserHeal = bDispenserHeal;
  7187. newHealer.flHealedLastSecond = 0;
  7188. if ( pHealScorer )
  7189. {
  7190. newHealer.pHealScorer = pHealScorer;
  7191. }
  7192. else
  7193. {
  7194. //Assert( pHealer->IsPlayer() );
  7195. newHealer.pHealScorer = pHealer;
  7196. }
  7197. m_aHealers.AddToTail( newHealer );
  7198. AddCond( TF_COND_HEALTH_BUFF, PERMANENT_CONDITION, pHealer );
  7199. RecalculateChargeEffects();
  7200. m_nNumHealers = m_aHealers.Count();
  7201. if ( pHealer && pHealer->IsPlayer() )
  7202. {
  7203. CTFPlayer *pPlayer = ToTFPlayer( pHealer );
  7204. Assert(pPlayer);
  7205. pPlayer->m_AchievementData.AddTargetToHistory( m_pOuter );
  7206. pPlayer->TeamFortress_SetSpeed();
  7207. }
  7208. }
  7209. //-----------------------------------------------------------------------------
  7210. // Purpose: Heal players.
  7211. // pPlayer is person who healed us
  7212. //-----------------------------------------------------------------------------
  7213. float CTFPlayerShared::StopHealing( CBaseEntity *pHealer )
  7214. {
  7215. int iIndex = FindHealerIndex(pHealer);
  7216. if ( iIndex == m_aHealers.InvalidIndex() )
  7217. return 0;
  7218. float flHealingDone = 0.f;
  7219. if ( iIndex != m_aHealers.InvalidIndex() )
  7220. {
  7221. flHealingDone = m_aHealers[iIndex].flHealAccum;
  7222. m_aHealers.Remove( iIndex );
  7223. }
  7224. if ( !m_aHealers.Count() )
  7225. {
  7226. RemoveCond( TF_COND_HEALTH_BUFF );
  7227. }
  7228. RecalculateChargeEffects();
  7229. m_nNumHealers = m_aHealers.Count();
  7230. return flHealingDone;
  7231. }
  7232. //-----------------------------------------------------------------------------
  7233. // Purpose:
  7234. //-----------------------------------------------------------------------------
  7235. void CTFPlayerShared::RecalculateChargeEffects( bool bInstantRemove )
  7236. {
  7237. struct medic_charges_t
  7238. {
  7239. bool bActive;
  7240. CTFPlayer *pProvider;
  7241. };
  7242. medic_charges_t aCharges[MEDIGUN_NUM_CHARGE_TYPES];
  7243. for ( int i = 0; i < ARRAYSIZE( aCharges ); i++ )
  7244. {
  7245. aCharges[i].bActive = m_pOuter->m_bInPowerPlay;
  7246. aCharges[i].pProvider = NULL;
  7247. }
  7248. medigun_charge_types iMyCharge = m_pOuter->GetChargeEffectBeingProvided();
  7249. if ( iMyCharge != MEDIGUN_CHARGE_INVALID )
  7250. {
  7251. Assert( iMyCharge >= 0 && iMyCharge < MEDIGUN_NUM_CHARGE_TYPES );
  7252. aCharges[iMyCharge].bActive = true;
  7253. aCharges[iMyCharge].pProvider = m_pOuter;
  7254. }
  7255. // Loop through our medics and get all their charges
  7256. for ( int i = 0; i < m_aHealers.Count(); i++ )
  7257. {
  7258. if ( !m_aHealers[i].pHealer )
  7259. continue;
  7260. CTFPlayer *pPlayer = ToTFPlayer( m_aHealers[i].pHealer );
  7261. if ( !pPlayer )
  7262. continue;
  7263. medigun_charge_types iCharge = pPlayer->GetChargeEffectBeingProvided();
  7264. if ( iCharge != MEDIGUN_CHARGE_INVALID )
  7265. {
  7266. Assert( iCharge >= 0 && iCharge < MEDIGUN_NUM_CHARGE_TYPES );
  7267. aCharges[iCharge].bActive = true;
  7268. aCharges[iCharge].pProvider = pPlayer;
  7269. }
  7270. }
  7271. if ( !CanRecieveMedigunChargeEffect( iMyCharge ) )
  7272. {
  7273. aCharges[MEDIGUN_CHARGE_INVULN].bActive = false;
  7274. }
  7275. SetChargeEffect( MEDIGUN_CHARGE_INVULN, aCharges[MEDIGUN_CHARGE_INVULN].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_INVULN ], tf_invuln_time.GetFloat(), aCharges[MEDIGUN_CHARGE_INVULN].pProvider );
  7276. SetChargeEffect( MEDIGUN_CHARGE_CRITICALBOOST, aCharges[MEDIGUN_CHARGE_CRITICALBOOST].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_CRITICALBOOST ], 0.0f, aCharges[MEDIGUN_CHARGE_CRITICALBOOST].pProvider );
  7277. SetChargeEffect( MEDIGUN_CHARGE_MEGAHEAL, aCharges[MEDIGUN_CHARGE_MEGAHEAL].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_MEGAHEAL ], 0.0f, aCharges[MEDIGUN_CHARGE_MEGAHEAL].pProvider );
  7278. SetChargeEffect( MEDIGUN_CHARGE_BULLET_RESIST, aCharges[MEDIGUN_CHARGE_BULLET_RESIST].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_BULLET_RESIST ], 0.0f, aCharges[MEDIGUN_CHARGE_BULLET_RESIST].pProvider );
  7279. SetChargeEffect( MEDIGUN_CHARGE_BLAST_RESIST, aCharges[MEDIGUN_CHARGE_BLAST_RESIST].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_BLAST_RESIST ], 0.0f, aCharges[MEDIGUN_CHARGE_BLAST_RESIST].pProvider );
  7280. SetChargeEffect( MEDIGUN_CHARGE_FIRE_RESIST, aCharges[MEDIGUN_CHARGE_FIRE_RESIST].bActive, bInstantRemove, g_MedigunEffects[ MEDIGUN_CHARGE_FIRE_RESIST ], 0.0f, aCharges[MEDIGUN_CHARGE_FIRE_RESIST].pProvider );
  7281. }
  7282. //-----------------------------------------------------------------------------
  7283. // Purpose:
  7284. //-----------------------------------------------------------------------------
  7285. void CTFPlayerShared::TestAndExpireChargeEffect( medigun_charge_types iCharge )
  7286. {
  7287. const MedigunEffects_t& effects = g_MedigunEffects[iCharge];
  7288. if ( InCond( effects.eCondition ) )
  7289. {
  7290. bool bRemoveEffect = false;
  7291. bool bGameInWinState = TFGameRules()->State_Get() == GR_STATE_TEAM_WIN;
  7292. bool bPlayerOnWinningTeam = TFGameRules()->GetWinningTeam() == m_pOuter->GetTeamNumber();
  7293. // Lose all charge effects in post-win state if we're the losing team
  7294. if ( bGameInWinState && !bPlayerOnWinningTeam )
  7295. {
  7296. bRemoveEffect = true;
  7297. }
  7298. if ( m_flChargeEffectOffTime[iCharge] )
  7299. {
  7300. if ( gpGlobals->curtime > m_flChargeEffectOffTime[iCharge] )
  7301. {
  7302. bRemoveEffect = true;
  7303. }
  7304. if (iCharge == MEDIGUN_CHARGE_CRITICALBOOST && ( bGameInWinState && bPlayerOnWinningTeam ) )
  7305. {
  7306. bRemoveEffect = false;
  7307. m_flChargeEffectOffTime[iCharge] = 0;
  7308. }
  7309. if ( GetRevengeCrits() > 0 && effects.eCondition == TF_COND_CRITBOOSTED )
  7310. {
  7311. // Don't remove while we have a weapon deployed that can consume revenge crits
  7312. CTFWeaponBase *pWeapon = m_pOuter->GetActiveTFWeapon();
  7313. if ( pWeapon && pWeapon->CanHaveRevengeCrits() )
  7314. bRemoveEffect = false;
  7315. }
  7316. }
  7317. // Check healers for possible usercommand invuln exploit
  7318. FOR_EACH_VEC( m_aHealers, i )
  7319. {
  7320. CTFPlayer *pTFHealer = ToTFPlayer( m_aHealers[i].pHealer );
  7321. if ( !pTFHealer )
  7322. continue;
  7323. CTFPlayer *pTFProvider = ToTFPlayer( GetConditionProvider( effects.eCondition ) );
  7324. if ( !pTFProvider )
  7325. continue;
  7326. if ( pTFProvider == pTFHealer && pTFHealer->GetTimeSinceLastUserCommand() > weapon_medigun_chargerelease_rate.GetFloat() + 1.f )
  7327. {
  7328. // Force remove uber and detach the medigun
  7329. bRemoveEffect = true;
  7330. pTFHealer->Weapon_Switch( pTFHealer->Weapon_GetSlot( TF_WPN_TYPE_MELEE ) );
  7331. }
  7332. }
  7333. if ( bRemoveEffect )
  7334. {
  7335. m_flChargeEffectOffTime[iCharge] = 0;
  7336. RemoveCond( effects.eCondition );
  7337. if ( effects.eWearingOffCondition != TF_COND_LAST )
  7338. {
  7339. RemoveCond( effects.eWearingOffCondition );
  7340. }
  7341. }
  7342. }
  7343. else if ( m_bChargeSoundEffectsOn[iCharge] )
  7344. {
  7345. if ( effects.pszChargeOnSound[0] )
  7346. {
  7347. m_pOuter->StopSound( effects.pszChargeOnSound );
  7348. }
  7349. m_bChargeSoundEffectsOn[iCharge] = false;
  7350. }
  7351. }
  7352. //-----------------------------------------------------------------------------
  7353. // Purpose: We've started a new charge effect
  7354. //-----------------------------------------------------------------------------
  7355. void CTFPlayerShared::SendNewInvulnGameEvent( void )
  7356. {
  7357. // for each medic healing me
  7358. for ( int i=0;i<m_aHealers.Count();i++ )
  7359. {
  7360. CTFPlayer *pMedic = ToTFPlayer( GetHealerByIndex(i) );
  7361. if ( !pMedic )
  7362. continue;
  7363. // ACHIEVEMENT_TF_MEDIC_CHARGE_FRIENDS
  7364. IGameEvent *event = gameeventmanager->CreateEvent( "player_invulned" );
  7365. if ( event )
  7366. {
  7367. event->SetInt( "userid", m_pOuter->GetUserID() );
  7368. event->SetInt( "medic_userid", pMedic->GetUserID() );
  7369. gameeventmanager->FireEvent( event );
  7370. }
  7371. }
  7372. }
  7373. //-----------------------------------------------------------------------------
  7374. // Purpose:
  7375. //-----------------------------------------------------------------------------
  7376. void CTFPlayerShared::SetChargeEffect( medigun_charge_types iCharge, bool bState, bool bInstant, const MedigunEffects_t& effects, float flWearOffTime, CTFPlayer *pProvider /*= NULL*/ )
  7377. {
  7378. if ( effects.eCondition == TF_COND_CRITBOOSTED )
  7379. {
  7380. // Don't remove while we have a weapon deployed that can consume revenge crits
  7381. CTFWeaponBase *pWeapon = m_pOuter->GetActiveTFWeapon();
  7382. if ( pWeapon )
  7383. {
  7384. if ( pWeapon->CanHaveRevengeCrits() && GetRevengeCrits() > 0 )
  7385. {
  7386. return;
  7387. }
  7388. if ( pWeapon->HasLastShotCritical() )
  7389. {
  7390. return;
  7391. }
  7392. }
  7393. }
  7394. bool bCurrentState = InCond( effects.eCondition );
  7395. if ( bCurrentState == bState )
  7396. {
  7397. if ( bState && m_flChargeEffectOffTime[iCharge] )
  7398. {
  7399. m_flChargeEffectOffTime[iCharge] = 0;
  7400. if ( effects.eWearingOffCondition != TF_COND_LAST )
  7401. {
  7402. RemoveCond( effects.eWearingOffCondition );
  7403. }
  7404. SendNewInvulnGameEvent();
  7405. }
  7406. return;
  7407. }
  7408. // Avoid infinite duration, because... the internet.
  7409. float flMaxDuration = ( pProvider && pProvider->IsBot() ) ? PERMANENT_CONDITION : weapon_medigun_chargerelease_rate.GetFloat() + ( ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() ) ? 8.f : 1.f );
  7410. if ( bState )
  7411. {
  7412. if ( m_flChargeEffectOffTime[iCharge] )
  7413. {
  7414. m_pOuter->StopSound( effects.pszChargeOffSound );
  7415. m_flChargeEffectOffTime[iCharge] = 0;
  7416. if ( effects.eWearingOffCondition != TF_COND_LAST )
  7417. {
  7418. RemoveCond( effects.eWearingOffCondition );
  7419. }
  7420. }
  7421. // Invulnerable turning on
  7422. AddCond( effects.eCondition, flMaxDuration, pProvider );
  7423. SendNewInvulnGameEvent();
  7424. CSingleUserRecipientFilter filter( m_pOuter );
  7425. m_pOuter->EmitSound( filter, m_pOuter->entindex(), effects.pszChargeOnSound );
  7426. m_bChargeOffSounded = false;
  7427. m_bChargeSoundEffectsOn[iCharge] = true;
  7428. }
  7429. else
  7430. {
  7431. if ( m_bChargeSoundEffectsOn[iCharge] )
  7432. {
  7433. m_pOuter->StopSound( effects.pszChargeOnSound );
  7434. m_bChargeSoundEffectsOn[iCharge] = false;
  7435. }
  7436. if ( !m_flChargeEffectOffTime[iCharge] && !m_bChargeOffSounded )
  7437. {
  7438. // Make sure we don't have duplicate Off sounds playing
  7439. m_pOuter->StopSound( effects.pszChargeOffSound );
  7440. CSingleUserRecipientFilter filter( m_pOuter );
  7441. m_pOuter->EmitSound( filter, m_pOuter->entindex(), effects.pszChargeOffSound );
  7442. m_bChargeOffSounded = true;
  7443. }
  7444. if ( bInstant )
  7445. {
  7446. m_flChargeEffectOffTime[iCharge] = 0;
  7447. RemoveCond( effects.eCondition );
  7448. if ( effects.eWearingOffCondition != TF_COND_LAST )
  7449. {
  7450. RemoveCond( effects.eWearingOffCondition );
  7451. }
  7452. }
  7453. else
  7454. {
  7455. // We're already in the process of turning it off
  7456. if ( m_flChargeEffectOffTime[iCharge] )
  7457. return;
  7458. if ( effects.eWearingOffCondition != TF_COND_LAST )
  7459. {
  7460. AddCond( effects.eWearingOffCondition, PERMANENT_CONDITION, pProvider );
  7461. }
  7462. m_flChargeEffectOffTime[iCharge] = gpGlobals->curtime + flWearOffTime;
  7463. }
  7464. }
  7465. }
  7466. //-----------------------------------------------------------------------------
  7467. // Purpose: Collect currency packs in a radius around the scout
  7468. //-----------------------------------------------------------------------------
  7469. void CTFPlayerShared::RadiusCurrencyCollectionCheck( void )
  7470. {
  7471. if ( m_pOuter->GetTeamNumber() != TF_TEAM_PVE_DEFENDERS && TFGameRules()->IsMannVsMachineMode() )
  7472. return;
  7473. if ( !m_pOuter->IsAlive() )
  7474. return;
  7475. if ( m_flRadiusCurrencyCollectionTime > gpGlobals->curtime )
  7476. return;
  7477. bool bScout = m_pOuter->GetPlayerClass()->GetClassIndex() == TF_CLASS_SCOUT;
  7478. const int nRadiusSqr = bScout ? 288 * 288 : 72 * 72;
  7479. Vector vecPos = m_pOuter->GetAbsOrigin();
  7480. // NDebugOverlay::Sphere( vecPos, nRadius, 0, 255, 0, 40, 5 );
  7481. for ( int i = 0; i < ICurrencyPackAutoList::AutoList().Count(); ++i )
  7482. {
  7483. CCurrencyPack *pCurrencyPack = static_cast< CCurrencyPack* >( ICurrencyPackAutoList::AutoList()[i] );
  7484. if ( !pCurrencyPack )
  7485. continue;
  7486. if ( !pCurrencyPack->AffectedByRadiusCollection() )
  7487. continue;
  7488. if ( ( vecPos - pCurrencyPack->GetAbsOrigin() ).LengthSqr() > nRadiusSqr )
  7489. continue;
  7490. if ( pCurrencyPack->IsClaimed() )
  7491. continue;
  7492. if ( m_pOuter->FVisible( pCurrencyPack, MASK_OPAQUE ) == false )
  7493. continue;
  7494. if ( !pCurrencyPack->ValidTouch( m_pOuter ) )
  7495. continue;
  7496. // Currencypack's seek classes with a large collection radius
  7497. if ( bScout )
  7498. {
  7499. bool bFound = false;
  7500. FOR_EACH_VEC( m_CurrencyPacks, i )
  7501. {
  7502. pulledcurrencypacks_t packinfo = m_CurrencyPacks[i];
  7503. if ( packinfo.hPack == pCurrencyPack )
  7504. bFound = true;
  7505. }
  7506. if ( !bFound )
  7507. {
  7508. // Mark as claimed to prevent other players from grabbing
  7509. pCurrencyPack->SetClaimed();
  7510. pulledcurrencypacks_t packinfo;
  7511. packinfo.hPack = pCurrencyPack;
  7512. packinfo.flTime = gpGlobals->curtime + 1.f;
  7513. m_CurrencyPacks.AddToTail( packinfo );
  7514. }
  7515. }
  7516. else
  7517. {
  7518. pCurrencyPack->Touch( m_pOuter );
  7519. }
  7520. }
  7521. FOR_EACH_VEC_BACK( m_CurrencyPacks, i )
  7522. {
  7523. if ( m_CurrencyPacks[i].hPack )
  7524. {
  7525. // If the timeout hits, force a touch
  7526. if ( m_CurrencyPacks[i].flTime <= gpGlobals->curtime )
  7527. {
  7528. m_CurrencyPacks[i].hPack->Touch( m_pOuter );
  7529. }
  7530. else
  7531. {
  7532. // Seek the player
  7533. const float flForce = 550.0f;
  7534. Vector vToPlayer = m_pOuter->GetAbsOrigin() - m_CurrencyPacks[i].hPack->GetAbsOrigin();
  7535. vToPlayer.z = 0.0f;
  7536. vToPlayer.NormalizeInPlace();
  7537. vToPlayer.z = 0.25f;
  7538. Vector vPush = flForce * vToPlayer;
  7539. m_CurrencyPacks[i].hPack->RemoveFlag( FL_ONGROUND );
  7540. m_CurrencyPacks[i].hPack->ApplyAbsVelocityImpulse( vPush );
  7541. }
  7542. }
  7543. else
  7544. {
  7545. // Automatic clean-up
  7546. m_CurrencyPacks.Remove( i );
  7547. }
  7548. }
  7549. m_flRadiusCurrencyCollectionTime = bScout ? gpGlobals->curtime + 0.15f : gpGlobals->curtime + 0.25f;
  7550. }
  7551. //-----------------------------------------------------------------------------
  7552. // Purpose: Collect objects in a radius around the player
  7553. //-----------------------------------------------------------------------------
  7554. void CTFPlayerShared::RadiusHealthkitCollectionCheck( void )
  7555. {
  7556. if ( GetCarryingRuneType() != RUNE_PLAGUE )
  7557. return;
  7558. if ( !m_pOuter->IsAlive() )
  7559. return;
  7560. if ( m_flRadiusCurrencyCollectionTime > gpGlobals->curtime )
  7561. return;
  7562. const int nRadiusSqr = 600 * 600;
  7563. const Vector& vecPos = m_pOuter->WorldSpaceCenter();
  7564. // NDebugOverlay::Sphere( vecPos, 600, 0, 255, 0, false, 2.f );
  7565. for ( int i = 0; i < IHealthKitAutoList::AutoList().Count(); ++i )
  7566. {
  7567. CHealthKit *pHealthKit = static_cast<CHealthKit*>( IHealthKitAutoList::AutoList()[i] );
  7568. if ( !pHealthKit )
  7569. continue;
  7570. if ( ( vecPos - pHealthKit->GetAbsOrigin() ).LengthSqr() > nRadiusSqr )
  7571. continue;
  7572. if ( !pHealthKit->ValidTouch( m_pOuter ) )
  7573. continue;
  7574. if ( pHealthKit->IsEffectActive( EF_NODRAW ) )
  7575. continue;
  7576. pHealthKit->ItemTouch( m_pOuter );
  7577. }
  7578. m_flRadiusCurrencyCollectionTime = gpGlobals->curtime + 0.15f;
  7579. }
  7580. //-----------------------------------------------------------------------------
  7581. // Purpose: Scan for and reveal spies in a radius around the player
  7582. //-----------------------------------------------------------------------------
  7583. void CTFPlayerShared::RadiusSpyScan( void )
  7584. {
  7585. if ( m_pOuter->GetTeamNumber() != TF_TEAM_PVE_DEFENDERS )
  7586. return;
  7587. if ( !m_pOuter->IsAlive() )
  7588. return;
  7589. if ( m_flRadiusSpyScanTime <= gpGlobals->curtime )
  7590. {
  7591. // bool bRevealed = false;
  7592. const int iRange = 750;
  7593. CUtlVector<CTFPlayer *> vecPlayers;
  7594. CollectPlayers( &vecPlayers, TF_TEAM_PVE_INVADERS, true );
  7595. FOR_EACH_VEC( vecPlayers, i )
  7596. {
  7597. if ( !vecPlayers[i] )
  7598. continue;
  7599. if ( vecPlayers[i]->GetPlayerClass()->GetClassIndex() != TF_CLASS_SPY )
  7600. continue;
  7601. if ( !vecPlayers[i]->m_Shared.InCond( TF_COND_STEALTHED ) )
  7602. continue;
  7603. if ( m_pOuter->FVisible( vecPlayers[i], MASK_OPAQUE ) == false )
  7604. continue;
  7605. Vector vDist = vecPlayers[i]->GetAbsOrigin() - m_pOuter->GetAbsOrigin();
  7606. if ( vDist.LengthSqr() <= iRange * iRange )
  7607. {
  7608. vecPlayers[i]->m_Shared.OnSpyTouchedByEnemy();
  7609. // bRevealed = true;
  7610. }
  7611. }
  7612. // if ( bRevealed )
  7613. // {
  7614. // bRevealed = false;
  7615. // CSingleUserRecipientFilter filter( m_pOuter );
  7616. // m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Recon.Ping" );
  7617. // }
  7618. m_flRadiusSpyScanTime = gpGlobals->curtime + 0.3f;
  7619. }
  7620. }
  7621. //-----------------------------------------------------------------------------
  7622. void CTFPlayerShared::ApplyAttributeToPlayer( const char* pszAttribName, float flValue )
  7623. {
  7624. const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
  7625. m_pOuter->GetAttributeList()->SetRuntimeAttributeValue( pDef, flValue );
  7626. m_pOuter->TeamFortress_SetSpeed();
  7627. }
  7628. //-----------------------------------------------------------------------------
  7629. void CTFPlayerShared::RemoveAttributeFromPlayer( const char* pszAttribName )
  7630. {
  7631. const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
  7632. m_pOuter->GetAttributeList()->RemoveAttribute( pDef );
  7633. }
  7634. //-----------------------------------------------------------------------------
  7635. // Purpose:
  7636. //-----------------------------------------------------------------------------
  7637. void CTFPlayerShared::AddTmpDamageBonus( float flBonus, float flExpiration )
  7638. {
  7639. AddCond( TF_COND_TMPDAMAGEBONUS, flExpiration );
  7640. m_flTmpDamageBonusAmount += flBonus;
  7641. }
  7642. //-----------------------------------------------------------------------------
  7643. // Purpose:
  7644. //-----------------------------------------------------------------------------
  7645. int CTFPlayerShared::FindHealerIndex( CBaseEntity *pHealer )
  7646. {
  7647. for ( int i = 0; i < m_aHealers.Count(); i++ )
  7648. {
  7649. if ( m_aHealers[i].pHealer == pHealer )
  7650. return i;
  7651. }
  7652. return m_aHealers.InvalidIndex();
  7653. }
  7654. //-----------------------------------------------------------------------------
  7655. // Purpose:
  7656. //-----------------------------------------------------------------------------
  7657. CBaseEntity *CTFPlayerShared::GetHealerByIndex( int index )
  7658. {
  7659. int iNumHealers = m_aHealers.Count();
  7660. if ( index < 0 || index >= iNumHealers )
  7661. return NULL;
  7662. return m_aHealers[index].pHealer;
  7663. }
  7664. //-----------------------------------------------------------------------------
  7665. // Purpose:
  7666. //-----------------------------------------------------------------------------
  7667. bool CTFPlayerShared::HealerIsDispenser( int index )
  7668. {
  7669. int iNumHealers = m_aHealers.Count();
  7670. if ( index < 0 || index >= iNumHealers )
  7671. return false;
  7672. return m_aHealers[index].bDispenserHeal;
  7673. }
  7674. //-----------------------------------------------------------------------------
  7675. // Purpose: Returns the first healer in the healer array. Note that this
  7676. // is an arbitrary healer.
  7677. //-----------------------------------------------------------------------------
  7678. EHANDLE CTFPlayerShared::GetFirstHealer()
  7679. {
  7680. if ( m_aHealers.Count() > 0 )
  7681. return m_aHealers.Head().pHealer;
  7682. return NULL;
  7683. }
  7684. //-----------------------------------------------------------------------------
  7685. // Purpose: External code has decided that the trigger event for an achievement
  7686. // has occurred. Go through our data and give it to the right people.
  7687. //-----------------------------------------------------------------------------
  7688. void CTFPlayerShared::CheckForAchievement( int iAchievement )
  7689. {
  7690. if ( iAchievement == ACHIEVEMENT_TF_MEDIC_SAVE_TEAMMATE ||
  7691. (iAchievement == ACHIEVEMENT_TF_MEDIC_CHARGE_BLOCKER && InCond( TF_COND_INVULNERABLE ) ) )
  7692. {
  7693. // ACHIEVEMENT_TF_MEDIC_SAVE_TEAMMATE : We were just saved from death by invuln. See if any medics deployed
  7694. // their charge on us recently, and if so, give them the achievement.
  7695. // ACHIEVEMENT_TF_MEDIC_CHARGE_BLOCKER: We just blocked a capture, and we're invuln. Whoever's invulning us gets the achievement.
  7696. for ( int i = 0; i < m_aHealers.Count(); i++ )
  7697. {
  7698. CTFPlayer *pPlayer = ToTFPlayer( m_aHealers[i].pHealer );
  7699. if ( !pPlayer )
  7700. continue;
  7701. if ( !pPlayer->IsPlayerClass(TF_CLASS_MEDIC) )
  7702. continue;
  7703. CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon();
  7704. if ( !pWpn )
  7705. continue;
  7706. CWeaponMedigun *pMedigun = dynamic_cast<CWeaponMedigun*>(pWpn);
  7707. if ( pMedigun && pMedigun->IsReleasingCharge() )
  7708. {
  7709. // Save teammate requires us to have deployed the charge within the last second
  7710. if ( iAchievement != ACHIEVEMENT_TF_MEDIC_SAVE_TEAMMATE || (gpGlobals->curtime - pMedigun->GetReleaseStartedAt()) < 1.0 )
  7711. {
  7712. pPlayer->AwardAchievement( iAchievement );
  7713. }
  7714. }
  7715. }
  7716. }
  7717. }
  7718. #endif // GAME_DLL
  7719. //-----------------------------------------------------------------------------
  7720. // Purpose: Get all of our conditions in a nice CBitVec
  7721. //-----------------------------------------------------------------------------
  7722. void CTFPlayerShared::GetConditionsBits( CBitVec< TF_COND_LAST >& vbConditions ) const
  7723. {
  7724. vbConditions.Set( 0u, (uint32)m_nPlayerCond );
  7725. vbConditions.Set( 1u, (uint32)m_nPlayerCondEx );
  7726. vbConditions.Set( 2u, (uint32)m_nPlayerCondEx2 );
  7727. vbConditions.Set( 3u, (uint32)m_nPlayerCondEx3 );
  7728. COMPILE_TIME_ASSERT( 32 + 32 + 32 + 32 > TF_COND_LAST );
  7729. }
  7730. //-----------------------------------------------------------------------------
  7731. // Purpose:
  7732. //-----------------------------------------------------------------------------
  7733. CTFWeaponBase *CTFPlayerShared::GetActiveTFWeapon() const
  7734. {
  7735. return m_pOuter->GetActiveTFWeapon();
  7736. }
  7737. //-----------------------------------------------------------------------------
  7738. // Purpose: Team check.
  7739. //-----------------------------------------------------------------------------
  7740. bool CTFPlayerShared::IsAlly( CBaseEntity *pEntity )
  7741. {
  7742. return ( pEntity->GetTeamNumber() == m_pOuter->GetTeamNumber() );
  7743. }
  7744. //-----------------------------------------------------------------------------
  7745. // Purpose:
  7746. //-----------------------------------------------------------------------------
  7747. int CTFPlayerShared::GetDesiredPlayerClassIndex( void )
  7748. {
  7749. return m_iDesiredPlayerClass;
  7750. }
  7751. //-----------------------------------------------------------------------------
  7752. // Purpose:
  7753. //-----------------------------------------------------------------------------
  7754. void CTFPlayerShared::SetJumping( bool bJumping )
  7755. {
  7756. m_bJumping = bJumping;
  7757. }
  7758. void CTFPlayerShared::SetAirDash( int iAirDash )
  7759. {
  7760. m_iAirDash = iAirDash;
  7761. }
  7762. //-----------------------------------------------------------------------------
  7763. // Purpose:
  7764. //-----------------------------------------------------------------------------
  7765. float CTFPlayerShared::GetCritMult( void )
  7766. {
  7767. float flRemapCritMul = RemapValClamped( m_iCritMult, 0, 255, 1.0, 4.0 );
  7768. /*#ifdef CLIENT_DLL
  7769. Msg("CLIENT: Crit mult %.2f - %d\n",flRemapCritMul, m_iCritMult);
  7770. #else
  7771. Msg("SERVER: Crit mult %.2f - %d\n", flRemapCritMul, m_iCritMult );
  7772. #endif*/
  7773. return flRemapCritMul;
  7774. }
  7775. #ifdef GAME_DLL
  7776. //-----------------------------------------------------------------------------
  7777. // Purpose:
  7778. //-----------------------------------------------------------------------------
  7779. void CTFPlayerShared::UpdateCritMult( void )
  7780. {
  7781. const float flMinMult = 1.0;
  7782. const float flMaxMult = TF_DAMAGE_CRITMOD_MAXMULT;
  7783. if ( m_DamageEvents.Count() == 0 )
  7784. {
  7785. m_iCritMult = RemapValClamped( flMinMult, 1.0, 4.0, 0, 255 );
  7786. return;
  7787. }
  7788. //Msg( "Crit mult update for %s\n", m_pOuter->GetPlayerName() );
  7789. //Msg( " Entries: %d\n", m_DamageEvents.Count() );
  7790. // Go through the damage multipliers and remove expired ones, while summing damage of the others
  7791. float flTotalDamage = 0;
  7792. for ( int i = m_DamageEvents.Count() - 1; i >= 0; i-- )
  7793. {
  7794. float flDelta = gpGlobals->curtime - m_DamageEvents[i].flTime;
  7795. if ( flDelta > tf_damage_events_track_for.GetFloat() )
  7796. {
  7797. //Msg( " Discarded (%d: time %.2f, now %.2f)\n", i, m_DamageEvents[i].flTime, gpGlobals->curtime );
  7798. m_DamageEvents.Remove(i);
  7799. continue;
  7800. }
  7801. // Ignore damage we've just done. We do this so that we have time to get those damage events
  7802. // to the client in time for using them in prediction in this code.
  7803. if ( flDelta < TF_DAMAGE_CRITMOD_MINTIME )
  7804. {
  7805. //Msg( " Ignored (%d: time %.2f, now %.2f)\n", i, m_DamageEvents[i].flTime, gpGlobals->curtime );
  7806. continue;
  7807. }
  7808. if ( flDelta > TF_DAMAGE_CRITMOD_MAXTIME )
  7809. continue;
  7810. //Msg( " Added %.2f (%d: time %.2f, now %.2f)\n", m_DamageEvents[i].flDamage, i, m_DamageEvents[i].flTime, gpGlobals->curtime );
  7811. flTotalDamage += m_DamageEvents[i].flDamage * m_DamageEvents[i].flDamageCritScaleMultiplier;
  7812. }
  7813. float flMult = RemapValClamped( flTotalDamage, 0, TF_DAMAGE_CRITMOD_DAMAGE, flMinMult, flMaxMult );
  7814. // Msg( " TotalDamage: %.2f -> Mult %.2f\n", flTotalDamage, flMult );
  7815. m_iCritMult = (int)RemapValClamped( flMult, flMinMult, flMaxMult, 0, 255 );
  7816. }
  7817. #define CRIT_DAMAGE_TIME 0.1f
  7818. //-----------------------------------------------------------------------------
  7819. // Purpose:
  7820. //-----------------------------------------------------------------------------
  7821. void CTFPlayerShared::RecordDamageEvent( const CTakeDamageInfo &info, bool bKill, int nVictimPrevHealth )
  7822. {
  7823. if ( m_DamageEvents.Count() >= MAX_DAMAGE_EVENTS )
  7824. {
  7825. // Remove the oldest event
  7826. m_DamageEvents.Remove( m_DamageEvents.Count()-1 );
  7827. }
  7828. // Don't count critical damage toward the critical multiplier.
  7829. float flDamage = info.GetDamage() - info.GetDamageBonus();
  7830. float flDamageCriticalScale = info.GetDamageType() & DMG_DONT_COUNT_DAMAGE_TOWARDS_CRIT_RATE
  7831. ? 0.0f
  7832. : 1.0f;
  7833. // cap the damage at our current health amount since it's going to kill us
  7834. if ( bKill && flDamage > nVictimPrevHealth )
  7835. {
  7836. flDamage = nVictimPrevHealth;
  7837. }
  7838. // Don't allow explosions to stack up damage toward the critical modifier.
  7839. bool bOverride = false;
  7840. if ( info.GetDamageType() & DMG_BLAST )
  7841. {
  7842. int nDamageCount = m_DamageEvents.Count();
  7843. for ( int iDamage = 0; iDamage < nDamageCount; ++iDamage )
  7844. {
  7845. // Was the older event I am checking against an explosion as well?
  7846. if ( m_DamageEvents[iDamage].nDamageType & DMG_BLAST )
  7847. {
  7848. // Did it happen very recently?
  7849. if ( ( gpGlobals->curtime - m_DamageEvents[iDamage].flTime ) < CRIT_DAMAGE_TIME )
  7850. {
  7851. if ( bKill )
  7852. {
  7853. m_DamageEvents[iDamage].nKills++;
  7854. if ( m_pOuter->IsPlayerClass( TF_CLASS_DEMOMAN ) )
  7855. {
  7856. // Make sure the previous & the current are stickybombs, and go with it.
  7857. if ( m_DamageEvents[iDamage].nDamageType == info.GetDamageType() &&
  7858. m_DamageEvents[iDamage].nDamageType == g_aWeaponDamageTypes[TF_WEAPON_PIPEBOMBLAUNCHER] )
  7859. {
  7860. if ( TFGameRules()->IsMannVsMachineMode() && m_DamageEvents[iDamage].nKills >= 10 )
  7861. {
  7862. m_pOuter->AwardAchievement( ACHIEVEMENT_TF_MVM_DEMO_GROUP_KILL );
  7863. }
  7864. else if ( m_DamageEvents[iDamage].nKills >= 3 )
  7865. {
  7866. m_pOuter->AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_KILL3_WITH_DETONATION );
  7867. }
  7868. }
  7869. }
  7870. }
  7871. // Take the max damage done in the time frame.
  7872. if ( flDamage > m_DamageEvents[iDamage].flDamage )
  7873. {
  7874. m_DamageEvents[iDamage].flDamage = flDamage;
  7875. m_DamageEvents[iDamage].flDamageCritScaleMultiplier = flDamageCriticalScale;
  7876. m_DamageEvents[iDamage].flTime = gpGlobals->curtime;
  7877. m_DamageEvents[iDamage].nDamageType = info.GetDamageType();
  7878. // Msg( "Update Damage Event: D:%f, T:%f\n", m_DamageEvents[iDamage].flDamage, m_DamageEvents[iDamage].flTime );
  7879. }
  7880. bOverride = true;
  7881. }
  7882. }
  7883. }
  7884. }
  7885. // We overrode a value, don't add this to the list.
  7886. if ( bOverride )
  7887. return;
  7888. int iIndex = m_DamageEvents.AddToTail();
  7889. m_DamageEvents[iIndex].flDamage = flDamage;
  7890. m_DamageEvents[iIndex].flDamageCritScaleMultiplier = flDamageCriticalScale;
  7891. m_DamageEvents[iIndex].nDamageType = info.GetDamageType();
  7892. m_DamageEvents[iIndex].flTime = gpGlobals->curtime;
  7893. m_DamageEvents[iIndex].nKills = bKill;
  7894. // Msg( "Damage Event: D:%f, T:%f\n", m_DamageEvents[iIndex].flDamage, m_DamageEvents[iIndex].flTime );
  7895. if ( TFGameRules()->IsMannVsMachineMode() && m_pOuter->IsPlayerClass( TF_CLASS_SNIPER ) )
  7896. {
  7897. int nKillCount = 0;
  7898. int nDamageCount = m_DamageEvents.Count();
  7899. for ( int iDamage = 0; iDamage < nDamageCount; ++iDamage )
  7900. {
  7901. // Did it happen very recently?
  7902. if ( ( gpGlobals->curtime - m_DamageEvents[iDamage].flTime ) < CRIT_DAMAGE_TIME )
  7903. {
  7904. nKillCount += m_DamageEvents[iDamage].nKills;
  7905. }
  7906. }
  7907. if ( nKillCount >= 4 )
  7908. {
  7909. m_pOuter->AwardAchievement( ACHIEVEMENT_TF_MVM_SNIPER_KILL_GROUP );
  7910. }
  7911. }
  7912. }
  7913. //-----------------------------------------------------------------------------
  7914. // Purpose:
  7915. //-----------------------------------------------------------------------------
  7916. void CTFPlayerShared::AddTempCritBonus( float flAmount )
  7917. {
  7918. if ( m_DamageEvents.Count() >= MAX_DAMAGE_EVENTS )
  7919. {
  7920. // Remove the oldest event
  7921. m_DamageEvents.Remove( m_DamageEvents.Count()-1 );
  7922. }
  7923. int iIndex = m_DamageEvents.AddToTail();
  7924. m_DamageEvents[iIndex].flDamage = RemapValClamped( flAmount, 0, 1, 0, TF_DAMAGE_CRITMOD_DAMAGE ) / (TF_DAMAGE_CRITMOD_MAXMULT - 1.0);
  7925. m_DamageEvents[iIndex].flDamageCritScaleMultiplier = 1.0f;
  7926. m_DamageEvents[iIndex].nDamageType = DMG_GENERIC;
  7927. m_DamageEvents[iIndex].flTime = gpGlobals->curtime;
  7928. m_DamageEvents[iIndex].nKills = 0;
  7929. }
  7930. //-----------------------------------------------------------------------------
  7931. // Purpose:
  7932. //-----------------------------------------------------------------------------
  7933. int CTFPlayerShared::GetNumKillsInTime( float flTime )
  7934. {
  7935. if ( tf_damage_events_track_for.GetFloat() < flTime )
  7936. {
  7937. Warning("Player asking for damage events for time %.0f, but tf_damage_events_track_for is only tracking events for %.0f\n", flTime, tf_damage_events_track_for.GetFloat() );
  7938. }
  7939. int iKills = 0;
  7940. for ( int i = m_DamageEvents.Count() - 1; i >= 0; i-- )
  7941. {
  7942. float flDelta = gpGlobals->curtime - m_DamageEvents[i].flTime;
  7943. if ( flDelta < flTime )
  7944. {
  7945. iKills += m_DamageEvents[i].nKills;
  7946. }
  7947. }
  7948. return iKills;
  7949. }
  7950. //-----------------------------------------------------------------------------
  7951. // Purpose:
  7952. //-----------------------------------------------------------------------------
  7953. bool CTFPlayerShared::AddToSpyCloakMeter( float val, bool bForce )
  7954. {
  7955. CTFWeaponInvis *pWpn = (CTFWeaponInvis *) m_pOuter->Weapon_OwnsThisID( TF_WEAPON_INVIS );
  7956. if ( !pWpn )
  7957. return false;
  7958. // STAGING_SPY
  7959. // Special cloaks only get cloak if not active and receive a smaller portion
  7960. int iNoCloakedPickup = 0;
  7961. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWpn, iNoCloakedPickup, NoCloakWhenCloaked );
  7962. if ( !bForce )
  7963. {
  7964. if ( InCond( TF_COND_STEALTHED ) && iNoCloakedPickup )
  7965. {
  7966. return false;
  7967. }
  7968. else
  7969. {
  7970. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, val, ReducedCloakFromAmmo );
  7971. }
  7972. }
  7973. bool bResult = ( val > 0 && m_flCloakMeter < 100.0f );
  7974. m_flCloakMeter = clamp( m_flCloakMeter + val, 0.0f, 100.0f );
  7975. return bResult;
  7976. }
  7977. #endif
  7978. #ifdef GAME_DLL
  7979. //-----------------------------------------------------------------------------
  7980. // Purpose: Stun & Snare Application
  7981. //-----------------------------------------------------------------------------
  7982. void CTFPlayerShared::StunPlayer( float flTime, float flReductionAmount, int iStunFlags, CTFPlayer* pAttacker )
  7983. {
  7984. // Insanity prevention
  7985. if ( ( m_PlayerStuns.Count() + 1 ) >= 250 )
  7986. return;
  7987. if ( InCond( TF_COND_PHASE ) || InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  7988. return;
  7989. if ( InCond( TF_COND_MEGAHEAL ) )
  7990. return;
  7991. if ( InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) && !InCond( TF_COND_MVM_BOT_STUN_RADIOWAVE ) )
  7992. return;
  7993. if ( pAttacker && TFGameRules() && TFGameRules()->IsTruceActive() && pAttacker->IsTruceValidForEnt() )
  7994. {
  7995. if ( ( pAttacker->GetTeamNumber() == TF_TEAM_RED ) || ( pAttacker->GetTeamNumber() == TF_TEAM_BLUE ) )
  7996. return;
  7997. }
  7998. float flRemapAmount = RemapValClamped( flReductionAmount, 0.0, 1.0, 0, 255 );
  7999. int iOldStunFlags = GetStunFlags();
  8000. // Already stunned
  8001. bool bStomp = false;
  8002. if ( InCond( TF_COND_STUNNED ) )
  8003. {
  8004. if ( GetActiveStunInfo() )
  8005. {
  8006. // Is it stronger than the active?
  8007. if ( flRemapAmount > GetActiveStunInfo()->flStunAmount || iStunFlags & TF_STUN_CONTROLS || iStunFlags & TF_STUN_LOSER_STATE )
  8008. {
  8009. bStomp = true;
  8010. }
  8011. // It's weaker. Would it expire before the active?
  8012. else if ( gpGlobals->curtime + flTime < GetActiveStunInfo()->flExpireTime )
  8013. {
  8014. // Ignore
  8015. return;
  8016. }
  8017. }
  8018. }
  8019. else if ( GetActiveStunInfo() )
  8020. {
  8021. // Something yanked our TF_COND_STUNNED in an unexpected way
  8022. if ( !HushAsserts() )
  8023. Assert( !"Something yanked out TF_COND_STUNNED." );
  8024. m_PlayerStuns.RemoveAll();
  8025. return;
  8026. }
  8027. // Add it to the stack
  8028. stun_struct_t stunEvent =
  8029. {
  8030. pAttacker, // hPlayer
  8031. flTime, // flDuration
  8032. gpGlobals->curtime + flTime, // flExpireTime
  8033. gpGlobals->curtime + flTime, // flStartFadeTime
  8034. flRemapAmount, // flStunAmount
  8035. iStunFlags // iStunFlags
  8036. };
  8037. // Should this become the active stun?
  8038. if ( bStomp || !GetActiveStunInfo() )
  8039. {
  8040. // If stomping, see if the stun we're replacing has a stronger slow.
  8041. // This can happen when stuns use TF_STUN_CONTROLS or TF_STUN_LOSER_STATE.
  8042. float flOldStun = GetActiveStunInfo() ? GetActiveStunInfo()->flStunAmount : 0.f;
  8043. m_iStunIndex = m_PlayerStuns.AddToTail( stunEvent );
  8044. if ( flOldStun > flRemapAmount )
  8045. {
  8046. GetActiveStunInfo()->flStunAmount = flOldStun;
  8047. }
  8048. }
  8049. else
  8050. {
  8051. // Done for now
  8052. m_PlayerStuns.AddToTail( stunEvent );
  8053. return;
  8054. }
  8055. // Add in extra time when TF_STUN_CONTROLS
  8056. if ( GetActiveStunInfo()->iStunFlags & TF_STUN_CONTROLS )
  8057. {
  8058. if ( !InCond( TF_COND_HALLOWEEN_KART ) )
  8059. {
  8060. GetActiveStunInfo()->flExpireTime += CONTROL_STUN_ANIM_TIME;
  8061. }
  8062. }
  8063. GetActiveStunInfo()->flStartFadeTime = gpGlobals->curtime + GetActiveStunInfo()->flDuration;
  8064. // Update old system for networking
  8065. UpdateLegacyStunSystem();
  8066. if ( GetActiveStunInfo()->iStunFlags & TF_STUN_CONTROLS || GetActiveStunInfo()->iStunFlags & TF_STUN_LOSER_STATE )
  8067. {
  8068. m_pOuter->m_angTauntCamera = m_pOuter->EyeAngles();
  8069. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_STUNNED );
  8070. if ( pAttacker )
  8071. {
  8072. pAttacker->SpeakConceptIfAllowed( MP_CONCEPT_STUNNED_TARGET );
  8073. }
  8074. }
  8075. if ( !( GetActiveStunInfo()->iStunFlags & TF_STUN_NO_EFFECTS ) )
  8076. {
  8077. m_pOuter->StunSound( pAttacker, GetActiveStunInfo()->iStunFlags, iOldStunFlags );
  8078. }
  8079. // Event for achievements.
  8080. IGameEvent *event = gameeventmanager->CreateEvent( "player_stunned" );
  8081. if ( event )
  8082. {
  8083. if ( pAttacker )
  8084. {
  8085. event->SetInt( "stunner", pAttacker->GetUserID() );
  8086. }
  8087. event->SetInt( "victim", m_pOuter->GetUserID() );
  8088. event->SetBool( "victim_capping", m_pOuter->IsCapturingPoint() );
  8089. event->SetBool( "big_stun", ( GetActiveStunInfo()->iStunFlags & TF_STUN_SPECIAL_SOUND ) != 0 );
  8090. gameeventmanager->FireEvent( event );
  8091. }
  8092. // Clear off all taunts, expressions, and scenes.
  8093. if ( ( GetActiveStunInfo()->iStunFlags & TF_STUN_CONTROLS) == TF_STUN_CONTROLS || ( GetActiveStunInfo()->iStunFlags & TF_STUN_LOSER_STATE) == TF_STUN_LOSER_STATE )
  8094. {
  8095. m_pOuter->StopTaunt();
  8096. m_pOuter->ClearExpression();
  8097. m_pOuter->ClearWeaponFireScene();
  8098. }
  8099. AddCond( TF_COND_STUNNED, -1.f, pAttacker );
  8100. }
  8101. #endif // GAME_DLL
  8102. //-----------------------------------------------------------------------------
  8103. // Purpose: Returns the intensity of the current stun effect, if we have the type of stun indicated.
  8104. //-----------------------------------------------------------------------------
  8105. float CTFPlayerShared::GetAmountStunned( int iStunFlags )
  8106. {
  8107. if ( GetActiveStunInfo() )
  8108. {
  8109. if ( InCond( TF_COND_STUNNED ) && ( iStunFlags & GetActiveStunInfo()->iStunFlags ) && ( GetActiveStunInfo()->flExpireTime > gpGlobals->curtime ) )
  8110. return MIN( MAX( GetActiveStunInfo()->flStunAmount, 0 ), 255 ) * ( 1.f/255.f );
  8111. }
  8112. return 0.f;
  8113. }
  8114. //-----------------------------------------------------------------------------
  8115. // Purpose: Indicates that our controls are stunned.
  8116. //-----------------------------------------------------------------------------
  8117. bool CTFPlayerShared::IsControlStunned( void )
  8118. {
  8119. if ( GetActiveStunInfo() )
  8120. {
  8121. if ( InCond( TF_COND_STUNNED ) && ( m_iStunFlags & TF_STUN_CONTROLS ) )
  8122. return true;
  8123. }
  8124. return false;
  8125. }
  8126. //-----------------------------------------------------------------------------
  8127. // Purpose: Indicates that our controls are stunned.
  8128. //-----------------------------------------------------------------------------
  8129. bool CTFPlayerShared::IsLoserStateStunned( void ) const
  8130. {
  8131. if ( GetActiveStunInfo() )
  8132. {
  8133. if ( InCond( TF_COND_STUNNED ) && ( m_iStunFlags & TF_STUN_LOSER_STATE ) )
  8134. return true;
  8135. }
  8136. return false;
  8137. }
  8138. //-----------------------------------------------------------------------------
  8139. // Purpose: Indicates that our movement is slowed, but our controls are still free.
  8140. //-----------------------------------------------------------------------------
  8141. bool CTFPlayerShared::IsSnared( void )
  8142. {
  8143. if ( InCond( TF_COND_STUNNED ) && !IsControlStunned() )
  8144. return true;
  8145. else
  8146. return false;
  8147. }
  8148. //=============================================================================
  8149. //
  8150. // Shared player code that isn't CTFPlayerShared
  8151. //
  8152. //-----------------------------------------------------------------------------
  8153. struct penetrated_target_list
  8154. {
  8155. CBaseEntity *pTarget;
  8156. float flDistanceFraction;
  8157. };
  8158. //-----------------------------------------------------------------------------
  8159. class CBulletPenetrateEnum : public IEntityEnumerator
  8160. {
  8161. public:
  8162. CBulletPenetrateEnum( Ray_t *pRay, CBaseEntity *pShooter, int nCustomDamageType, bool bIgnoreTeammates = true )
  8163. {
  8164. m_pRay = pRay;
  8165. m_pShooter = pShooter;
  8166. m_nCustomDamageType = nCustomDamageType;
  8167. m_bIgnoreTeammates = bIgnoreTeammates;
  8168. }
  8169. // We need to sort the penetrated targets into order, with the closest target first
  8170. class PenetratedTargetLess
  8171. {
  8172. public:
  8173. bool Less( const penetrated_target_list &src1, const penetrated_target_list &src2, void *pCtx )
  8174. {
  8175. return src1.flDistanceFraction < src2.flDistanceFraction;
  8176. }
  8177. };
  8178. virtual bool EnumEntity( IHandleEntity *pHandleEntity )
  8179. {
  8180. trace_t tr;
  8181. CBaseEntity *pEnt = static_cast<CBaseEntity*>(pHandleEntity);
  8182. // Ignore collisions with the shooter
  8183. if ( pEnt == m_pShooter )
  8184. return true;
  8185. if ( pEnt->IsCombatCharacter() || pEnt->IsBaseObject() )
  8186. {
  8187. if ( m_bIgnoreTeammates && pEnt->GetTeam() == m_pShooter->GetTeam() )
  8188. return true;
  8189. enginetrace->ClipRayToEntity( *m_pRay, MASK_SOLID | CONTENTS_HITBOX, pHandleEntity, &tr );
  8190. if (tr.fraction < 1.0f)
  8191. {
  8192. penetrated_target_list newEntry;
  8193. newEntry.pTarget = pEnt;
  8194. newEntry.flDistanceFraction = tr.fraction;
  8195. m_Targets.Insert( newEntry );
  8196. return true;
  8197. }
  8198. }
  8199. return true;
  8200. }
  8201. public:
  8202. Ray_t *m_pRay;
  8203. int m_nCustomDamageType;
  8204. CBaseEntity *m_pShooter;
  8205. bool m_bIgnoreTeammates;
  8206. CUtlSortVector<penetrated_target_list, PenetratedTargetLess> m_Targets;
  8207. };
  8208. CTargetOnlyFilter::CTargetOnlyFilter( CBaseEntity *pShooter, CBaseEntity *pTarget )
  8209. : CTraceFilterSimple( pShooter, COLLISION_GROUP_NONE )
  8210. {
  8211. m_pShooter = pShooter;
  8212. m_pTarget = pTarget;
  8213. }
  8214. bool CTargetOnlyFilter::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  8215. {
  8216. CBaseEntity *pEnt = static_cast<CBaseEntity*>(pHandleEntity);
  8217. if ( pEnt && pEnt == m_pTarget )
  8218. return true;
  8219. else if ( !pEnt || pEnt != m_pTarget )
  8220. {
  8221. // If we hit a solid piece of the world, we're done.
  8222. if ( pEnt->IsBSPModel() && pEnt->IsSolid() )
  8223. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  8224. return false;
  8225. }
  8226. else
  8227. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  8228. }
  8229. //-----------------------------------------------------------------------------
  8230. // Purpose:
  8231. // Input: info
  8232. // bDoEffects - effects (blood, etc.) should only happen client-side.
  8233. //-----------------------------------------------------------------------------
  8234. void CTFPlayer::MaybeDrawRailgunBeam( IRecipientFilter *pFilter, CTFWeaponBase *pWeapon, const Vector& vStartPos, const Vector& vEndPos )
  8235. {
  8236. #ifdef GAME_DLL
  8237. Assert( pFilter );
  8238. #else // !GAME_DLL
  8239. Assert( !pFilter );
  8240. #endif
  8241. Assert( pWeapon );
  8242. int iShouldFireTracer = 0;
  8243. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iShouldFireTracer, sniper_fires_tracer );
  8244. if ( !iShouldFireTracer )
  8245. {
  8246. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iShouldFireTracer, sniper_fires_tracer_HIDDEN );
  8247. }
  8248. // Check for heatmaker
  8249. if ( !iShouldFireTracer )
  8250. {
  8251. iShouldFireTracer = m_Shared.InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) && pWeapon && WeaponID_IsSniperRifle( pWeapon->GetWeaponID() );
  8252. }
  8253. if ( iShouldFireTracer )
  8254. {
  8255. const char *pParticleSystemName = pWeapon->GetTeamNumber() == TF_TEAM_BLUE ? "dxhr_sniper_rail_blue" : "dxhr_sniper_rail_red";
  8256. CTFSniperRifle *pRifle = dynamic_cast< CTFSniperRifle* >( pWeapon );
  8257. if ( pRifle && ( pRifle->GetRifleType() == RIFLE_CLASSIC ) )
  8258. {
  8259. pParticleSystemName = "tfc_sniper_distortion_trail";
  8260. }
  8261. #ifdef GAME_DLL
  8262. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_WORLDORIGIN, vEndPos };
  8263. TE_TFParticleEffectComplex( *pFilter, 0.0f, pParticleSystemName, vStartPos, QAngle( 0, 0, 0 ), NULL, &controlPoint, pWeapon, PATTACH_CUSTOMORIGIN );
  8264. #else // !GAME_DLL
  8265. CSmartPtr<CNewParticleEffect> pEffect = pWeapon->ParticleProp()->Create( pParticleSystemName, PATTACH_CUSTOMORIGIN, 0 );
  8266. if ( pEffect.IsValid() && pEffect->IsValid() )
  8267. {
  8268. pEffect->SetSortOrigin( vStartPos );
  8269. pEffect->SetControlPoint( 0, vStartPos );
  8270. pEffect->SetControlPoint( 1, vEndPos );
  8271. }
  8272. #endif // GAME_DLL
  8273. }
  8274. }
  8275. void CTFPlayer::GetHorriblyHackedRailgunPosition( const Vector& vStart, Vector *out_pvStartPos )
  8276. {
  8277. Assert( out_pvStartPos != NULL );
  8278. // DO NOT LOOK BEHIND THE MAGIC CURTAIN
  8279. Vector vForward, vRight, vUp;
  8280. AngleVectors( EyeAngles(), &vForward, &vRight, &vUp );
  8281. *out_pvStartPos = vStart
  8282. + (vForward * 60.9f)
  8283. + (vRight * 13.1f)
  8284. + (vUp * -15.1f);
  8285. }
  8286. static bool OnOpposingTFTeams( int iTeam0, int iTeam1 )
  8287. {
  8288. // This logic is weird because we want to make sure that we're actually shooting someone on the
  8289. // other team, not just someone on a different team. This prevents weirdness where we count shooting
  8290. // the BSP as an enemy because they aren't on our team.
  8291. if ( iTeam0 == TF_TEAM_BLUE ) // if we're on the blue team...
  8292. return iTeam1 == TF_TEAM_RED; // ...and we shot someone on the red team, then we're opposing.
  8293. if ( iTeam0 == TF_TEAM_RED ) // if we're on the blue team...
  8294. return iTeam1 == TF_TEAM_BLUE; // ...and we shot someone on the red team, then we're opposing.
  8295. return iTeam0 != iTeam1; // if we're neither red nor blue, then anyone different from us is opposing
  8296. }
  8297. #ifdef GAME_DLL
  8298. extern void ExtinguishPlayer( CEconEntity *pExtinguisher, CTFPlayer *pOwner, CTFPlayer *pTarget, const char *pExtinguisherName );
  8299. //-----------------------------------------------------------------------------
  8300. // Purpose:
  8301. //-----------------------------------------------------------------------------
  8302. void CTFPlayer::ModifyDamageInfo( CTakeDamageInfo *pInfo, const CBaseEntity *pTarget )
  8303. {
  8304. if ( pInfo && pTarget )
  8305. {
  8306. // Increased damage vs sentry's target?
  8307. if ( IsPlayerClass( TF_CLASS_ENGINEER ) )
  8308. {
  8309. float flDamageMod = 1.f;
  8310. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetActiveWeapon(), flDamageMod, mult_dmg_bullet_vs_sentry_target );
  8311. if ( flDamageMod > 1.f )
  8312. {
  8313. CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( GetObjectOfType( OBJ_SENTRYGUN ) );
  8314. if ( pSentry && ( pSentry->GetTarget() == pTarget ) )
  8315. {
  8316. pInfo->SetDamage( pInfo->GetDamage() * flDamageMod );
  8317. }
  8318. }
  8319. }
  8320. }
  8321. }
  8322. #endif
  8323. //-----------------------------------------------------------------------------
  8324. // Purpose:
  8325. //-----------------------------------------------------------------------------
  8326. void CTFPlayer::FireBullet( CTFWeaponBase *pWpn, const FireBulletsInfo_t &info, bool bDoEffects, int nDamageType, int nCustomDamageType /*= TF_DMG_CUSTOM_NONE*/ )
  8327. {
  8328. // Fire a bullet (ignoring the shooter).
  8329. Vector vecStart = info.m_vecSrc;
  8330. Vector vecEnd = vecStart + info.m_vecDirShooting * info.m_flDistance;
  8331. trace_t trace;
  8332. ETFDmgCustom ePenetrateType = pWpn ? pWpn->GetPenetrateType() : TF_DMG_CUSTOM_NONE;
  8333. if ( ePenetrateType == TF_DMG_CUSTOM_NONE )
  8334. {
  8335. ePenetrateType = (ETFDmgCustom)nCustomDamageType;
  8336. }
  8337. Ray_t ray;
  8338. ray.Init( vecStart, vecEnd );
  8339. // Ignore teammates and their (physical) upgrade items when shooting in MvM
  8340. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  8341. {
  8342. CTraceFilterIgnoreFriendlyCombatItems traceFilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  8343. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID | CONTENTS_HITBOX, &traceFilter, &trace );
  8344. }
  8345. else
  8346. {
  8347. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID | CONTENTS_HITBOX, this, COLLISION_GROUP_NONE, &trace );
  8348. }
  8349. #ifndef CLIENT_DLL
  8350. CUtlVector<CBaseEntity *> vecTracedEntities;
  8351. bool bPenetratingShot = ( (ePenetrateType == TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS) || (ePenetrateType == TF_DMG_CUSTOM_PENETRATE_MY_TEAM) || (ePenetrateType == TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE) );
  8352. if ( bPenetratingShot && trace.m_pEnt )
  8353. {
  8354. if ( trace.m_pEnt->IsCombatCharacter() || trace.m_pEnt->IsBaseObject() )
  8355. {
  8356. // Penetrating shot: Strikes everything along the bullet's path.
  8357. CBulletPenetrateEnum bulletpenetrate( &ray, this, ePenetrateType, ePenetrateType == TF_DMG_CUSTOM_PENETRATE_MY_TEAM );
  8358. enginetrace->EnumerateEntities( ray, false, &bulletpenetrate );
  8359. FOR_EACH_VEC( bulletpenetrate.m_Targets, i )
  8360. {
  8361. vecTracedEntities.AddToTail( bulletpenetrate.m_Targets[i].pTarget );
  8362. }
  8363. }
  8364. else
  8365. {
  8366. // We traced into something we don't understand (sticky bomb? pumpkin bomb?) -- just apply our
  8367. // hit logic to whatever we traced first.
  8368. vecTracedEntities.AddToTail( trace.m_pEnt );
  8369. }
  8370. }
  8371. else
  8372. #endif
  8373. {
  8374. ePenetrateType = TF_DMG_CUSTOM_NONE;
  8375. }
  8376. #ifndef CLIENT_DLL
  8377. CTakeDamageInfo dmgInfo( this, info.m_pAttacker, info.m_flDamage, nDamageType );
  8378. dmgInfo.SetWeapon( GetActiveWeapon() );
  8379. dmgInfo.SetDamageCustom( nCustomDamageType );
  8380. int iPenetratedPlayerCount = 0;
  8381. int iEnemyPlayersHit = 0;
  8382. if ( bPenetratingShot )
  8383. {
  8384. int iChargedPenetration = 0;
  8385. CALL_ATTRIB_HOOK_INT( iChargedPenetration, sniper_penetrate_players_when_charged );
  8386. int iPenetrationLimit = 0;
  8387. CALL_ATTRIB_HOOK_INT( iPenetrationLimit, projectile_penetration );
  8388. // Damage every enemy player struck by the bullet along its path.
  8389. trace_t pen_trace;
  8390. FOR_EACH_VEC( vecTracedEntities, i )
  8391. {
  8392. // Limit the number of pen targets in MvM if we're not charge-based
  8393. if ( TFGameRules()->IsMannVsMachineMode() && iChargedPenetration == 0 )
  8394. {
  8395. // For sniper class, treat iPenetrationLimit as a bool
  8396. bool bIsSniper = IsPlayerClass( TF_CLASS_SNIPER );
  8397. if ( bIsSniper && iPenetrationLimit == 0 && iPenetratedPlayerCount > 0 )
  8398. break;
  8399. if ( !bIsSniper && iPenetratedPlayerCount > iPenetrationLimit )
  8400. break;
  8401. }
  8402. CBaseEntity *pTarget = vecTracedEntities[i];
  8403. if ( !pTarget )
  8404. continue;
  8405. trace_t *pTraceToUse = &pen_trace;
  8406. if ( ePenetrateType == TF_DMG_CUSTOM_PENETRATE_MY_TEAM )
  8407. {
  8408. // Skip friendlies if we're looking for the first enemy
  8409. if ( GetTeamNumber() == pTarget->GetTeamNumber() )
  8410. continue;
  8411. pTraceToUse = &trace;
  8412. }
  8413. else if ( ePenetrateType == TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE )
  8414. {
  8415. if ( GetTeamNumber() == pTarget->GetTeamNumber() )
  8416. {
  8417. if ( pTarget->IsPlayer() )
  8418. {
  8419. // skip friendlies that are not on burning
  8420. CTFPlayer *pTeammate = ToTFPlayer( pTarget );
  8421. if ( !pTeammate->m_Shared.InCond( TF_COND_BURNING ) )
  8422. continue;
  8423. }
  8424. }
  8425. pTraceToUse = &trace;
  8426. }
  8427. CTargetOnlyFilter penetrateFilter( this, pTarget );
  8428. UTIL_TraceLine( vecStart, vecEnd, (MASK_SOLID|CONTENTS_HITBOX), &penetrateFilter, pTraceToUse );
  8429. if ( pTraceToUse->m_pEnt == pTarget )
  8430. {
  8431. CTFPlayer *pTargetPlayer = NULL;
  8432. if ( pTarget->IsPlayer() )
  8433. pTargetPlayer = ToTFPlayer( pTarget );
  8434. // put out fire for burning teammate
  8435. if ( nCustomDamageType == TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE )
  8436. {
  8437. if ( pTargetPlayer && GetTeamNumber() == pTargetPlayer->GetTeamNumber() && pTargetPlayer->m_Shared.InCond( TF_COND_BURNING ) )
  8438. {
  8439. ExtinguishPlayer( GetActiveWeapon(), ToTFPlayer( GetActiveWeapon()->GetOwner() ), pTargetPlayer, GetActiveWeapon()->GetName() );
  8440. }
  8441. }
  8442. ModifyDamageInfo( &dmgInfo, pTarget );
  8443. CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, info.m_vecDirShooting, pTraceToUse->endpos, 1.0 );
  8444. dmgInfo.SetPlayerPenetrationCount( iPenetratedPlayerCount );
  8445. pTarget->DispatchTraceAttack( dmgInfo, info.m_vecDirShooting, pTraceToUse, GetActiveWeapon() ? GetActiveWeapon()->GetDmgAccumulator() : NULL );
  8446. const bool bIsPenetratingPlayer = pTargetPlayer != NULL;
  8447. if ( bIsPenetratingPlayer )
  8448. {
  8449. iPenetratedPlayerCount++;
  8450. float flPenetrationPenalty = 1.0f;
  8451. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flPenetrationPenalty, penetration_damage_penalty );
  8452. dmgInfo.SetDamage( dmgInfo.GetDamage() * flPenetrationPenalty );
  8453. }
  8454. // If we're only supposed to penetrate players and this thing isn't a player, stop here.
  8455. if ( !bIsPenetratingPlayer && (ePenetrateType == TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS) )
  8456. break;
  8457. }
  8458. else
  8459. {
  8460. // We hit something solid that said we should stop tracing.
  8461. break;
  8462. }
  8463. if( pTarget->IsPlayer() && OnOpposingTFTeams( GetTeamNumber(), pTarget->GetTeamNumber() ) )
  8464. {
  8465. iEnemyPlayersHit++;
  8466. }
  8467. // If we're penetrating team mates, but we've just hit an enemy, we're done.
  8468. if ( ePenetrateType == TF_DMG_CUSTOM_PENETRATE_MY_TEAM )
  8469. break;
  8470. // just hit an enemy or a burning teammate
  8471. if ( ePenetrateType == TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE )
  8472. break;
  8473. }
  8474. }
  8475. else
  8476. {
  8477. // Damage only the first entity encountered on the bullet's path.
  8478. if ( trace.m_pEnt )
  8479. {
  8480. ModifyDamageInfo( &dmgInfo, trace.m_pEnt );
  8481. CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, info.m_vecDirShooting, trace.endpos, 1.0 );
  8482. trace.m_pEnt->DispatchTraceAttack( dmgInfo, info.m_vecDirShooting, &trace );
  8483. if ( trace.m_pEnt->IsPlayer() && OnOpposingTFTeams( GetTeamNumber(), trace.m_pEnt->GetTeamNumber() ) )
  8484. {
  8485. iEnemyPlayersHit++;
  8486. }
  8487. }
  8488. }
  8489. if ( pWpn )
  8490. {
  8491. pWpn->OnBulletFire( iEnemyPlayersHit );
  8492. if ( iEnemyPlayersHit )
  8493. { // Guarantee that the bullet that hit an enemy trumps the player viewangles
  8494. // that are locked in for the duration of the server simulation ticks
  8495. m_iLockViewanglesTickNumber = gpGlobals->tickcount;
  8496. m_qangLockViewangles = pl.v_angle;
  8497. }
  8498. }
  8499. #endif
  8500. #ifdef GAME_DLL
  8501. #ifdef _DEBUG
  8502. if ( tf_debug_bullets.GetBool() )
  8503. {
  8504. NDebugOverlay::Line( vecStart, trace.endpos, 0,255,0, true, 30 );
  8505. }
  8506. #endif // _DEBUG
  8507. #endif
  8508. if ( trace.fraction < 1.0 )
  8509. {
  8510. // Verify we have an entity at the point of impact.
  8511. Assert( trace.m_pEnt );
  8512. #ifdef GAME_DLL
  8513. // We intentionally do this logic here outside our client-side "should we do effects?" logic. We send this
  8514. // to everyone except our local owner (ourself) as we'll do our own fire effects below.
  8515. Vector vMuzzleOrigin;
  8516. if ( pWpn )
  8517. {
  8518. Vector vStartPos;
  8519. GetHorriblyHackedRailgunPosition( trace.startpos, &vStartPos );
  8520. CBroadcastNonOwnerRecipientFilter filter( this );
  8521. MaybeDrawRailgunBeam( &filter, pWpn, vStartPos, trace.endpos );
  8522. }
  8523. #endif // GAME_DLL
  8524. if ( bDoEffects )
  8525. {
  8526. // If shot starts out of water and ends in water
  8527. if ( !( enginetrace->GetPointContents( trace.startpos ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) &&
  8528. ( enginetrace->GetPointContents( trace.endpos ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) )
  8529. {
  8530. // Water impact effects.
  8531. ImpactWaterTrace( trace, vecStart );
  8532. }
  8533. else
  8534. {
  8535. // Regular impact effects.
  8536. // don't decal your teammates or objects on your team
  8537. if ( trace.m_pEnt && trace.m_pEnt->GetTeamNumber() != GetTeamNumber() )
  8538. {
  8539. UTIL_ImpactTrace( &trace, nDamageType );
  8540. }
  8541. }
  8542. #ifdef CLIENT_DLL
  8543. if ( pWpn )
  8544. {
  8545. Vector vStartPos;
  8546. GetHorriblyHackedRailgunPosition( trace.startpos, &vStartPos );
  8547. MaybeDrawRailgunBeam( NULL, pWpn, vStartPos, trace.endpos );
  8548. }
  8549. static int tracerCount;
  8550. if ( ( ( info.m_iTracerFreq != 0 ) && ( tracerCount++ % info.m_iTracerFreq ) == 0 ) || (ePenetrateType == TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS) )
  8551. {
  8552. // if this is a local player, start at attachment on view model
  8553. // else start on attachment on weapon model
  8554. int iUseAttachment = TRACER_DONT_USE_ATTACHMENT;
  8555. int iAttachment = 1;
  8556. {
  8557. C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
  8558. if ( pWeapon )
  8559. {
  8560. iAttachment = pWeapon->LookupAttachment( "muzzle" );
  8561. }
  8562. }
  8563. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  8564. bool bInToolRecordingMode = clienttools->IsInRecordingMode();
  8565. // If we're using a viewmodel, override vecStart with the muzzle of that - just for the visual effect, not gameplay.
  8566. if ( ( pLocalPlayer != NULL ) && !pLocalPlayer->ShouldDrawThisPlayer() && !bInToolRecordingMode && pWpn )
  8567. {
  8568. C_BaseAnimating *pAttachEnt = pWpn->GetAppropriateWorldOrViewModel();
  8569. if ( pAttachEnt != NULL )
  8570. {
  8571. pAttachEnt->GetAttachment( iAttachment, vecStart );
  8572. }
  8573. }
  8574. else if ( !IsDormant() )
  8575. {
  8576. // fill in with third person weapon model index
  8577. C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
  8578. if( pWeapon )
  8579. {
  8580. int nModelIndex = pWeapon->GetModelIndex();
  8581. int nWorldModelIndex = pWeapon->GetWorldModelIndex();
  8582. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  8583. {
  8584. pWeapon->SetModelIndex( nWorldModelIndex );
  8585. }
  8586. pWeapon->GetAttachment( iAttachment, vecStart );
  8587. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  8588. {
  8589. pWeapon->SetModelIndex( nModelIndex );
  8590. }
  8591. }
  8592. }
  8593. if ( tf_useparticletracers.GetBool() )
  8594. {
  8595. const char *pszTracerEffect = GetTracerType();
  8596. if ( pszTracerEffect && pszTracerEffect[0] )
  8597. {
  8598. char szTracerEffect[128];
  8599. if ( nDamageType & DMG_CRITICAL )
  8600. {
  8601. Q_snprintf( szTracerEffect, sizeof(szTracerEffect), "%s_crit", pszTracerEffect );
  8602. pszTracerEffect = szTracerEffect;
  8603. }
  8604. UTIL_ParticleTracer( pszTracerEffect, vecStart, trace.endpos, entindex(), iUseAttachment, true );
  8605. }
  8606. }
  8607. else
  8608. {
  8609. UTIL_Tracer( vecStart, trace.endpos, entindex(), iUseAttachment, 5000, true, GetTracerType() );
  8610. }
  8611. }
  8612. #endif
  8613. }
  8614. }
  8615. }
  8616. #ifdef CLIENT_DLL
  8617. static ConVar tf_impactwatertimeenable( "tf_impactwatertimeenable", "0", FCVAR_CHEAT, "Draw impact debris effects." );
  8618. static ConVar tf_impactwatertime( "tf_impactwatertime", "1.0f", FCVAR_CHEAT, "Draw impact debris effects." );
  8619. #endif
  8620. //-----------------------------------------------------------------------------
  8621. // Purpose: Trace from the shooter to the point of impact (another player,
  8622. // world, etc.), but this time take into account water/slime surfaces.
  8623. // Input: trace - initial trace from player to point of impact
  8624. // vecStart - starting point of the trace
  8625. //-----------------------------------------------------------------------------
  8626. void CTFPlayer::ImpactWaterTrace( trace_t &trace, const Vector &vecStart )
  8627. {
  8628. #ifdef CLIENT_DLL
  8629. if ( tf_impactwatertimeenable.GetBool() )
  8630. {
  8631. if ( m_flWaterImpactTime > gpGlobals->curtime )
  8632. return;
  8633. }
  8634. #endif
  8635. trace_t traceWater;
  8636. UTIL_TraceLine( vecStart, trace.endpos, ( MASK_SHOT | CONTENTS_WATER | CONTENTS_SLIME ),
  8637. this, COLLISION_GROUP_NONE, &traceWater );
  8638. if( traceWater.fraction < 1.0f )
  8639. {
  8640. CEffectData data;
  8641. data.m_vOrigin = traceWater.endpos;
  8642. data.m_vNormal = traceWater.plane.normal;
  8643. data.m_flScale = random->RandomFloat( 8, 12 );
  8644. if ( traceWater.contents & CONTENTS_SLIME )
  8645. {
  8646. data.m_fFlags |= FX_WATER_IN_SLIME;
  8647. }
  8648. const char *pszEffectName = "tf_gunshotsplash";
  8649. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  8650. if ( pWeapon && ( TF_WEAPON_MINIGUN == pWeapon->GetWeaponID() ) )
  8651. {
  8652. // for the minigun, use a different, cheaper splash effect because it can create so many of them
  8653. pszEffectName = "tf_gunshotsplash_minigun";
  8654. }
  8655. DispatchEffect( pszEffectName, data );
  8656. #ifdef CLIENT_DLL
  8657. if ( tf_impactwatertimeenable.GetBool() )
  8658. {
  8659. m_flWaterImpactTime = gpGlobals->curtime + tf_impactwatertime.GetFloat();
  8660. }
  8661. #endif
  8662. }
  8663. }
  8664. //-----------------------------------------------------------------------------
  8665. // Purpose:
  8666. //-----------------------------------------------------------------------------
  8667. CTFWeaponBase *CTFPlayer::GetActiveTFWeapon( void ) const
  8668. {
  8669. CBaseCombatWeapon *pRet = GetActiveWeapon();
  8670. if ( pRet )
  8671. {
  8672. Assert( dynamic_cast< CTFWeaponBase* >( pRet ) != NULL );
  8673. return static_cast< CTFWeaponBase * >( pRet );
  8674. }
  8675. return NULL;
  8676. }
  8677. //-----------------------------------------------------------------------------
  8678. // Purpose: Return true if we are currently wielding a weapon that
  8679. // matches the given item def handle.
  8680. //-----------------------------------------------------------------------------
  8681. bool CTFPlayer::IsActiveTFWeapon( CEconItemDefinition *weaponHandle ) const
  8682. {
  8683. return ( GetActiveTFWeapon() &&
  8684. GetActiveTFWeapon()->GetAttributeContainer() &&
  8685. GetActiveTFWeapon()->GetAttributeContainer()->GetItem() &&
  8686. GetActiveTFWeapon()->GetAttributeContainer()->GetItem()->GetItemDefinition() == weaponHandle );
  8687. }
  8688. //-----------------------------------------------------------------------------
  8689. // Purpose: Return true if we are currently wielding a weapon that
  8690. // matches the given item def handle.
  8691. bool CTFPlayer::IsActiveTFWeapon( const CSchemaItemDefHandle &weaponHandle ) const
  8692. {
  8693. return ( GetActiveTFWeapon() &&
  8694. GetActiveTFWeapon()->GetAttributeContainer() &&
  8695. GetActiveTFWeapon()->GetAttributeContainer()->GetItem() &&
  8696. GetActiveTFWeapon()->GetAttributeContainer()->GetItem()->GetItemDefinition() == weaponHandle );
  8697. }
  8698. //-----------------------------------------------------------------------------
  8699. // Purpose: How much build resource ( metal ) does this player have
  8700. //-----------------------------------------------------------------------------
  8701. int CTFPlayer::GetBuildResources( void )
  8702. {
  8703. return GetAmmoCount( TF_AMMO_METAL );
  8704. }
  8705. //-----------------------------------------------------------------------------
  8706. // Purpose:
  8707. //-----------------------------------------------------------------------------
  8708. template < typename T >
  8709. class CScopedFlag
  8710. {
  8711. public:
  8712. CScopedFlag( T& ref_ )
  8713. : ref( ref_ )
  8714. {
  8715. Assert( !ref );
  8716. ref = true;
  8717. }
  8718. ~CScopedFlag()
  8719. {
  8720. Assert( ref );
  8721. ref = false;
  8722. }
  8723. private:
  8724. T& ref;
  8725. };
  8726. //-----------------------------------------------------------------------------
  8727. // Purpose:
  8728. //-----------------------------------------------------------------------------
  8729. float CTFPlayer::GetMovementForwardPull( void ) const
  8730. {
  8731. CTFWeaponBase *pWpn = GetActiveTFWeapon();
  8732. if ( pWpn && pWpn->IsFiring() )
  8733. {
  8734. float flFiringForwardPull = 0.0f;
  8735. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flFiringForwardPull, firing_forward_pull );
  8736. return flFiringForwardPull;
  8737. }
  8738. return 0.0f;
  8739. }
  8740. //-----------------------------------------------------------------------------
  8741. // Purpose:
  8742. //-----------------------------------------------------------------------------
  8743. bool CTFPlayer::CanPlayerMove() const
  8744. {
  8745. // No one can move when in a final countdown transition.
  8746. if ( TFGameRules() && TFGameRules()->BInMatchStartCountdown() )
  8747. { return false; }
  8748. bool bFreezeOnRestart = tf_player_movement_restart_freeze.GetBool();
  8749. if ( bFreezeOnRestart )
  8750. {
  8751. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  8752. if ( mp_developer.GetBool() )
  8753. bFreezeOnRestart = false;
  8754. #endif // _DEBUG || STAGING_ONLY
  8755. if ( TFGameRules() && TFGameRules()->UsePlayerReadyStatusMode() && ( TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS ) )
  8756. bFreezeOnRestart = false;
  8757. }
  8758. bool bInRoundRestart = TFGameRules() && TFGameRules()->InRoundRestart();
  8759. if ( bInRoundRestart && TFGameRules()->IsCompetitiveMode() )
  8760. {
  8761. if ( TFGameRules()->GetRoundsPlayed() > 0 )
  8762. {
  8763. if ( gpGlobals->curtime < TFGameRules()->GetPreroundCountdownTime() )
  8764. {
  8765. bFreezeOnRestart = true;
  8766. }
  8767. }
  8768. else
  8769. {
  8770. bFreezeOnRestart = false;
  8771. }
  8772. }
  8773. bool bNoMovement = bInRoundRestart && bFreezeOnRestart;
  8774. return !bNoMovement;
  8775. }
  8776. //-----------------------------------------------------------------------------
  8777. // Purpose:
  8778. //-----------------------------------------------------------------------------
  8779. float CTFPlayer::TeamFortress_CalculateMaxSpeed( bool bIgnoreSpecialAbility /*= false*/ ) const
  8780. {
  8781. if ( !GameRules() )
  8782. return 0.0f;
  8783. int playerclass = GetPlayerClass()->GetClassIndex();
  8784. // Spectators can move while in Classic Observer mode
  8785. if ( IsObserver() )
  8786. {
  8787. if ( GetObserverMode() == OBS_MODE_ROAMING )
  8788. return GetPlayerClassData( TF_CLASS_SCOUT )->m_flMaxSpeed;
  8789. return 0.0f;
  8790. }
  8791. // Check for any reason why they can't move at all
  8792. if ( playerclass == TF_CLASS_UNDEFINED || !CanPlayerMove() )
  8793. return 1.0f; // this can't return 0 because other parts of the code interpret that as "use default speed" during setup
  8794. // First, get their max class speed
  8795. float default_speed = GetPlayerClassData( playerclass )->m_flMaxSpeed;
  8796. // Avoid re-entering and calculating our velocity while we're calculating our velocity.
  8797. // This can happen if we have two characters trying to match each other's velocity, for
  8798. // example if you have two medics with Quick-Fixes healing each other.
  8799. //
  8800. // In the case where we run into this, we end the recursion with someone running default
  8801. // speed.
  8802. if ( m_bIsCalculatingMaximumSpeed )
  8803. return default_speed;
  8804. CScopedFlag<char> flagAvoidReentrancy( m_bIsCalculatingMaximumSpeed );
  8805. // Slow us down if we're disguised as a slower class
  8806. // unless we're cloaked..
  8807. float maxfbspeed = default_speed;
  8808. bool bAllowSlowing = m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) ? false : true;
  8809. if ( m_Shared.InCond( TF_COND_DISGUISED_AS_DISPENSER ) && !m_Shared.IsStealthed() )
  8810. {
  8811. maxfbspeed = 0.0f;
  8812. }
  8813. else if ( m_Shared.InCond( TF_COND_DISGUISED ) && !m_Shared.IsStealthed() )
  8814. {
  8815. float flMaxDisguiseSpeed = GetPlayerClassData( m_Shared.GetDisguiseClass() )->m_flMaxSpeed;
  8816. maxfbspeed = MIN( flMaxDisguiseSpeed, maxfbspeed );
  8817. }
  8818. if ( !TFGameRules()->IsMannVsMachineMode() || !IsMiniBoss() ) // No aiming slowdown penalties for MiniBoss players in MVM
  8819. {
  8820. // if they're a sniper, and they're aiming, their speed must be 80 or less
  8821. if ( m_Shared.InCond( TF_COND_AIMING ) )
  8822. {
  8823. float flAimMax = 0;
  8824. // Heavies are allowed to move slightly faster than a sniper when spun-up
  8825. if ( playerclass == TF_CLASS_HEAVYWEAPONS )
  8826. {
  8827. {
  8828. flAimMax = 110;
  8829. }
  8830. }
  8831. else
  8832. {
  8833. if ( GetActiveTFWeapon() && (GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_COMPOUND_BOW) )
  8834. {
  8835. flAimMax = 160;
  8836. }
  8837. else
  8838. {
  8839. flAimMax = 80;
  8840. }
  8841. }
  8842. CALL_ATTRIB_HOOK_FLOAT( flAimMax, mult_player_aiming_movespeed );
  8843. maxfbspeed = MIN( maxfbspeed, flAimMax );
  8844. }
  8845. }
  8846. #ifdef GAME_DLL
  8847. #ifdef STAGING_ONLY
  8848. if ( m_Shared.InCond( TF_COND_SPEED_BOOST ) || m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  8849. #else
  8850. if ( m_Shared.InCond( TF_COND_SPEED_BOOST ) )
  8851. #endif
  8852. {
  8853. // We only allow our speed boost to apply if we have a base speed to work with. If we're supposed
  8854. // to be stationary for whatever reason we don't allow a speed to allow us to move.
  8855. if ( maxfbspeed > 0.0f )
  8856. {
  8857. maxfbspeed += MIN( maxfbspeed * 0.4f, tf_whip_speed_increase.GetFloat() );
  8858. }
  8859. }
  8860. #endif
  8861. if ( m_Shared.InCond( TF_COND_STEALTHED ) )
  8862. {
  8863. if (maxfbspeed > tf_spy_max_cloaked_speed.GetFloat() )
  8864. {
  8865. maxfbspeed = tf_spy_max_cloaked_speed.GetFloat();
  8866. }
  8867. }
  8868. // if we're in bonus time because a team has won, give the winners 110% speed and the losers 90% speed
  8869. if ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN )
  8870. {
  8871. int iWinner = TFGameRules()->GetWinningTeam();
  8872. if ( iWinner != TEAM_UNASSIGNED )
  8873. {
  8874. if ( iWinner == GetTeamNumber() )
  8875. {
  8876. maxfbspeed *= 1.1f;
  8877. }
  8878. else
  8879. {
  8880. maxfbspeed *= 0.9f;
  8881. }
  8882. }
  8883. }
  8884. CTFWeaponBase* pWeapon = GetActiveTFWeapon();
  8885. if ( pWeapon )
  8886. {
  8887. maxfbspeed *= pWeapon->GetSpeedMod();
  8888. }
  8889. if ( playerclass == TF_CLASS_DEMOMAN )
  8890. {
  8891. CTFSword *pSword = dynamic_cast<CTFSword*>(Weapon_OwnsThisID( TF_WEAPON_SWORD ));
  8892. if ( pSword )
  8893. {
  8894. maxfbspeed *= pSword->GetSwordSpeedMod();
  8895. }
  8896. if ( !bIgnoreSpecialAbility && m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  8897. {
  8898. maxfbspeed = tf_max_charge_speed.GetFloat();
  8899. }
  8900. }
  8901. bool bCarryPenalty = true;
  8902. if ( TFGameRules()->IsMannVsMachineMode() )
  8903. {
  8904. bCarryPenalty = false;
  8905. }
  8906. if ( m_Shared.IsCarryingObject() && bCarryPenalty && bAllowSlowing )
  8907. {
  8908. #ifdef STAGING_ONLY
  8909. CBaseObject* pObject = m_Shared.GetCarriedObject();
  8910. if ( pObject && pObject->GetType() == OBJ_TELEPORTER )
  8911. {
  8912. CALL_ATTRIB_HOOK_FLOAT( maxfbspeed, teleporter_carry_speed );
  8913. }
  8914. #endif // STAGING_ONLY
  8915. // STAGING_ENGY
  8916. maxfbspeed *= 0.90f;
  8917. }
  8918. if ( m_Shared.IsLoserStateStunned() && bAllowSlowing )
  8919. {
  8920. // Yikes is not as slow, terrible gotcha
  8921. if ( m_Shared.GetActiveStunInfo()->iStunFlags & TF_STUN_BY_TRIGGER )
  8922. {
  8923. maxfbspeed *= 0.75f;
  8924. }
  8925. else
  8926. {
  8927. maxfbspeed *= 0.5f;
  8928. }
  8929. }
  8930. // If we have an item with a move speed modification, apply it to the final speed.
  8931. CALL_ATTRIB_HOOK_FLOAT( maxfbspeed, mult_player_movespeed );
  8932. if ( m_Shared.IsShieldEquipped() )
  8933. {
  8934. CALL_ATTRIB_HOOK_FLOAT( maxfbspeed, mult_player_movespeed_shieldrequired );
  8935. }
  8936. if ( playerclass == TF_CLASS_MEDIC )
  8937. {
  8938. if ( pWeapon )
  8939. {
  8940. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pWeapon );
  8941. if ( pMedigun )
  8942. {
  8943. // Medics match faster classes when healing them
  8944. CTFPlayer *pHealTarget = ToTFPlayer( pMedigun->GetHealTarget() );
  8945. if ( pHealTarget )
  8946. {
  8947. // The Quick-Fix attaches to charging demos
  8948. bool bCharge = ( pMedigun->GetMedigunType() == MEDIGUN_QUICKFIX && pHealTarget->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) );
  8949. const float flHealTargetMaxSpeed = ( bCharge ) ? tf_max_charge_speed.GetFloat() : pHealTarget->TeamFortress_CalculateMaxSpeed( true );
  8950. maxfbspeed = Max( maxfbspeed, flHealTargetMaxSpeed );
  8951. }
  8952. }
  8953. }
  8954. // Special bone saw
  8955. int iTakeHeads = 0;
  8956. CALL_ATTRIB_HOOK_INT( iTakeHeads, add_head_on_hit );
  8957. if ( iTakeHeads )
  8958. {
  8959. CTFBonesaw *pSaw = dynamic_cast<CTFBonesaw*>(Weapon_OwnsThisID( TF_WEAPON_HARVESTER_SAW ));
  8960. if ( pSaw )
  8961. {
  8962. maxfbspeed *= pSaw->GetBoneSawSpeedMod();
  8963. }
  8964. }
  8965. }
  8966. float flClassResourceLevelMod = 1.f;
  8967. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flClassResourceLevelMod, mult_player_movespeed_resource_level );
  8968. if ( flClassResourceLevelMod != 1.f )
  8969. {
  8970. // Medic Uber
  8971. if ( playerclass == TF_CLASS_MEDIC )
  8972. {
  8973. CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( Weapon_OwnsThisID( TF_WEAPON_MEDIGUN ) );
  8974. if ( pMedigun )
  8975. {
  8976. maxfbspeed *= RemapValClamped( pMedigun->GetChargeLevel(), 0.f, 1.f, 1.f, flClassResourceLevelMod );
  8977. }
  8978. }
  8979. }
  8980. // If we're a heavy with berzerker mode...
  8981. if ( playerclass == TF_CLASS_HEAVYWEAPONS )
  8982. {
  8983. float heavy_max_speed = default_speed * 1.35f;
  8984. if ( m_Shared.InCond( TF_COND_ENERGY_BUFF ) )
  8985. {
  8986. maxfbspeed *= 1.35f;
  8987. if ( maxfbspeed > heavy_max_speed )
  8988. {
  8989. // Prevent other speed modifiers like GRU from making berzerker mode too fast.
  8990. maxfbspeed = heavy_max_speed;
  8991. }
  8992. }
  8993. }
  8994. if ( playerclass == TF_CLASS_SCOUT )
  8995. {
  8996. if ( Weapon_OwnsThisID( TF_WEAPON_PEP_BRAWLER_BLASTER ) )
  8997. {
  8998. // Make this change based on attrs, hardcode right now
  8999. maxfbspeed *= RemapValClamped( m_Shared.GetScoutHypeMeter(), 0.0f, 100.0f, 1.0f, 1.45f );
  9000. }
  9001. // Crit-a-Cola gives a move bonus while active
  9002. if ( m_Shared.InCond( TF_COND_ENERGY_BUFF ) )
  9003. {
  9004. maxfbspeed *= 1.25f;
  9005. }
  9006. }
  9007. // Mann Vs Machine mode has a speed penalty for carrying the flag
  9008. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  9009. {
  9010. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  9011. {
  9012. if ( HasTheFlag() && !IsMiniBoss() )
  9013. {
  9014. maxfbspeed *= tf_mvm_bot_flag_carrier_movement_penalty.GetFloat();
  9015. }
  9016. }
  9017. }
  9018. #ifdef STAGING_ONLY
  9019. // Overloaded circuits!
  9020. if ( m_Shared.InCond( TF_COND_REPROGRAMMED ) )
  9021. {
  9022. maxfbspeed *= 2.f;
  9023. }
  9024. #endif // STAGING_ONLY
  9025. if ( m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  9026. {
  9027. maxfbspeed *= 1.3f;
  9028. }
  9029. if ( m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  9030. {
  9031. // light classes get more benefit due to movement speed cap of 520
  9032. switch ( GetPlayerClass()->GetClassIndex() )
  9033. {
  9034. case TF_CLASS_DEMOMAN:
  9035. case TF_CLASS_SOLDIER:
  9036. case TF_CLASS_HEAVYWEAPONS:
  9037. maxfbspeed *= 1.4f;
  9038. break;
  9039. default:
  9040. maxfbspeed *= 1.5f;
  9041. break;
  9042. }
  9043. }
  9044. return maxfbspeed;
  9045. }
  9046. void CTFPlayer::TeamFortress_SetSpeed()
  9047. {
  9048. #ifdef GAME_DLL
  9049. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() && g_pPasstimeLogic )
  9050. {
  9051. float flPackSpeed = g_pPasstimeLogic->GetPackSpeed( this );
  9052. if ( flPackSpeed > 0 )
  9053. {
  9054. SetMaxSpeed( flPackSpeed );
  9055. return;
  9056. }
  9057. }
  9058. #endif
  9059. const float fMaxSpeed = TeamFortress_CalculateMaxSpeed();
  9060. // Set the speed
  9061. SetMaxSpeed( fMaxSpeed );
  9062. if ( fMaxSpeed <= 0.0f )
  9063. {
  9064. SetAbsVelocity( vec3_origin );
  9065. }
  9066. #ifdef GAME_DLL
  9067. // Anyone that's watching our speed should know that our speed changed so they can
  9068. // update their own speed.
  9069. //
  9070. // We guard against re-entrancy here as well to avoid the case where two medics are
  9071. // healing each other with Quick-Fixes.
  9072. //
  9073. // This can also happen when a quickfix medic is healing a player that gets a speed
  9074. // boost. And it doesn't work because the recursive call will just return the healed
  9075. // character's default speed instead of current speed.
  9076. // TODO fix this. why not just set the medic's speed directly at this point?
  9077. //
  9078. if ( !m_bIsCalculatingMaximumSpeed )
  9079. {
  9080. CScopedFlag<char> flagAvoidReentrancy( m_bIsCalculatingMaximumSpeed );
  9081. CUtlVector<CTFPlayer *> vecSpeedWatchers;
  9082. m_Shared.GetSpeedWatchersList( &vecSpeedWatchers );
  9083. FOR_EACH_VEC( vecSpeedWatchers, i )
  9084. {
  9085. vecSpeedWatchers[i]->TeamFortress_SetSpeed();
  9086. }
  9087. }
  9088. #endif // GAME_DLL
  9089. }
  9090. //-----------------------------------------------------------------------------
  9091. // Purpose:
  9092. //-----------------------------------------------------------------------------
  9093. bool CTFPlayer::HasItem( void ) const
  9094. {
  9095. return ( m_hItem != NULL );
  9096. }
  9097. //-----------------------------------------------------------------------------
  9098. // Purpose:
  9099. //-----------------------------------------------------------------------------
  9100. void CTFPlayer::SetItem( CTFItem *pItem )
  9101. {
  9102. m_hItem = pItem;
  9103. #ifndef CLIENT_DLL
  9104. if ( pItem )
  9105. {
  9106. AddGlowEffect();
  9107. }
  9108. else
  9109. {
  9110. RemoveGlowEffect();
  9111. }
  9112. if ( pItem && pItem->GetItemID() == TF_ITEM_CAPTURE_FLAG )
  9113. {
  9114. RemoveInvisibility();
  9115. }
  9116. #endif
  9117. }
  9118. //-----------------------------------------------------------------------------
  9119. // Purpose:
  9120. //-----------------------------------------------------------------------------
  9121. CTFItem *CTFPlayer::GetItem( void ) const
  9122. {
  9123. return m_hItem;
  9124. }
  9125. //-----------------------------------------------------------------------------
  9126. // Purpose: Is the player carrying the flag?
  9127. //-----------------------------------------------------------------------------
  9128. bool CTFPlayer::HasTheFlag( ETFFlagType exceptionTypes[], int nNumExceptions ) const
  9129. {
  9130. if ( HasItem() && GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG )
  9131. {
  9132. CCaptureFlag* pFlag = static_cast< CCaptureFlag* >( GetItem() );
  9133. for( int i=0; i < nNumExceptions; ++i )
  9134. {
  9135. if ( exceptionTypes[ i ] == pFlag->GetType() )
  9136. return false;
  9137. }
  9138. return true;
  9139. }
  9140. return false;
  9141. }
  9142. bool CTFPlayer::IsAllowedToPickUpFlag( void ) const
  9143. {
  9144. int iCannotPickUpIntelligence = 0;
  9145. CALL_ATTRIB_HOOK_INT( iCannotPickUpIntelligence, cannot_pick_up_intelligence );
  9146. if ( iCannotPickUpIntelligence )
  9147. {
  9148. return false;
  9149. }
  9150. return true;
  9151. }
  9152. //-----------------------------------------------------------------------------
  9153. // Purpose:
  9154. //-----------------------------------------------------------------------------
  9155. CCaptureZone *CTFPlayer::GetCaptureZoneStandingOn( void )
  9156. {
  9157. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  9158. if ( root )
  9159. {
  9160. for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
  9161. {
  9162. CBaseEntity *pTouch = link->entityTouched;
  9163. if ( pTouch && pTouch->IsSolidFlagSet( FSOLID_TRIGGER ) && pTouch->IsBSPModel() )
  9164. {
  9165. CCaptureZone *pAreaTrigger = dynamic_cast< CCaptureZone* >(pTouch);
  9166. if ( pAreaTrigger )
  9167. {
  9168. return pAreaTrigger;
  9169. }
  9170. }
  9171. }
  9172. }
  9173. return NULL;
  9174. }
  9175. CCaptureZone *CTFPlayer::GetClosestCaptureZone( void )
  9176. {
  9177. CCaptureZone *pCaptureZone = NULL;
  9178. float flClosestDistance = FLT_MAX;
  9179. for ( int i=0; i<ICaptureZoneAutoList::AutoList().Count(); ++i )
  9180. {
  9181. CCaptureZone *pTempCaptureZone = static_cast< CCaptureZone* >( ICaptureZoneAutoList::AutoList()[i] );
  9182. if ( !pTempCaptureZone->IsDisabled() && pTempCaptureZone->GetTeamNumber() == GetTeamNumber() )
  9183. {
  9184. float fCurrentDistance = GetAbsOrigin().DistTo( pTempCaptureZone->WorldSpaceCenter() );
  9185. if ( flClosestDistance > fCurrentDistance )
  9186. {
  9187. pCaptureZone = pTempCaptureZone;
  9188. flClosestDistance = fCurrentDistance;
  9189. }
  9190. }
  9191. }
  9192. return pCaptureZone;
  9193. }
  9194. //-----------------------------------------------------------------------------
  9195. // Purpose: Return true if this player's allowed to build another one of the specified object
  9196. //-----------------------------------------------------------------------------
  9197. int CTFPlayer::CanBuild( int iObjectType, int iObjectMode )
  9198. {
  9199. if ( iObjectType < 0 || iObjectType >= OBJ_LAST )
  9200. return CB_UNKNOWN_OBJECT;
  9201. const CObjectInfo *pInfo = GetObjectInfo( iObjectType );
  9202. if ( pInfo && ((iObjectMode > pInfo->m_iNumAltModes) || (iObjectMode < 0)) )
  9203. return CB_CANNOT_BUILD;
  9204. // Does this type require a specific builder?
  9205. bool bHasSubType = false;
  9206. CTFWeaponBuilder *pBuilder = CTFPlayerSharedUtils::GetBuilderForObjectType( this, iObjectType );
  9207. if ( pBuilder )
  9208. {
  9209. bHasSubType = true;
  9210. }
  9211. if ( TFGameRules() )
  9212. {
  9213. if ( TFGameRules()->IsTruceActive() && ( iObjectType == OBJ_ATTACHMENT_SAPPER ) )
  9214. return CB_CANNOT_BUILD;
  9215. if ( TFGameRules()->IsMannVsMachineMode() )
  9216. {
  9217. // If a human is placing a sapper
  9218. if ( !IsBot() && iObjectType == OBJ_ATTACHMENT_SAPPER )
  9219. {
  9220. // Only allow one Sapper of any kind in MvM
  9221. if ( GetNumObjects( iObjectType, BUILDING_MODE_ANY ) )
  9222. return CB_LIMIT_REACHED;
  9223. return ( ( GetAmmoCount( TF_AMMO_GRENADES2 ) > 0 ) ? CB_CAN_BUILD : CB_CANNOT_BUILD );
  9224. }
  9225. }
  9226. }
  9227. #ifndef CLIENT_DLL
  9228. CTFPlayerClass *pCls = GetPlayerClass();
  9229. if ( !bHasSubType && pCls && pCls->CanBuildObject( iObjectType ) == false )
  9230. {
  9231. return CB_CANNOT_BUILD;
  9232. }
  9233. #endif
  9234. // We can redeploy the object if we are carrying it.
  9235. CBaseObject* pObjType = GetObjectOfType( iObjectType, iObjectMode );
  9236. if ( pObjType && pObjType->IsCarried() )
  9237. {
  9238. return CB_CAN_BUILD;
  9239. }
  9240. // Special handling of "disposable" sentries
  9241. if ( TFGameRules()->GameModeUsesUpgrades() && iObjectType == OBJ_SENTRYGUN )
  9242. {
  9243. // If we have our main sentry, see if we're allowed to build disposables
  9244. if ( GetNumObjects( iObjectType, iObjectMode ) )
  9245. {
  9246. bool bHasPrimary = false;
  9247. int nDisposableCount = 0;
  9248. int nMaxDisposableCount = 0;
  9249. CALL_ATTRIB_HOOK_INT( nMaxDisposableCount, engy_disposable_sentries );
  9250. if ( nMaxDisposableCount )
  9251. {
  9252. for ( int i = GetObjectCount()-1; i >= 0; i-- )
  9253. {
  9254. CBaseObject *pObj = GetObject( i );
  9255. if ( pObj )
  9256. {
  9257. if ( !pObj->IsDisposableBuilding() )
  9258. {
  9259. bHasPrimary = true;
  9260. }
  9261. else
  9262. {
  9263. nDisposableCount++;
  9264. }
  9265. }
  9266. }
  9267. if ( bHasPrimary )
  9268. {
  9269. if ( nDisposableCount < nMaxDisposableCount )
  9270. {
  9271. return CB_CAN_BUILD;
  9272. }
  9273. else
  9274. {
  9275. return CB_LIMIT_REACHED;
  9276. }
  9277. }
  9278. }
  9279. }
  9280. }
  9281. // Allow MVM engineer bots to have multiple sentries. Currently they only need this so
  9282. // they can appear to be carrying a new building when advancing their nest rather than
  9283. // transporting an existing building.
  9284. if( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && IsBot() )
  9285. {
  9286. return CB_CAN_BUILD;
  9287. }
  9288. // Make sure we haven't hit maximum number
  9289. int iObjectCount = GetNumObjects( iObjectType, iObjectMode );
  9290. if ( iObjectCount >= GetObjectInfo( iObjectType )->m_nMaxObjects && GetObjectInfo( iObjectType )->m_nMaxObjects != -1)
  9291. {
  9292. return CB_LIMIT_REACHED;
  9293. }
  9294. // Find out how much the object should cost
  9295. int iCost = m_Shared.CalculateObjectCost( this, iObjectType );
  9296. // Make sure we have enough resources
  9297. if ( GetBuildResources() < iCost )
  9298. {
  9299. return CB_NEED_RESOURCES;
  9300. }
  9301. return CB_CAN_BUILD;
  9302. }
  9303. //-----------------------------------------------------------------------------
  9304. // Purpose:
  9305. //-----------------------------------------------------------------------------
  9306. bool CTFPlayerShared::IsAiming( void )
  9307. {
  9308. if ( !m_pOuter )
  9309. return false;
  9310. bool bAiming = InCond( TF_COND_AIMING ) && !m_pOuter->IsPlayerClass( TF_CLASS_SOLDIER );
  9311. if ( m_pOuter->IsPlayerClass( TF_CLASS_SNIPER ) && m_pOuter->GetActiveTFWeapon() && ( m_pOuter->GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC ) )
  9312. {
  9313. bAiming = InCond( TF_COND_ZOOMED );
  9314. }
  9315. return bAiming;
  9316. }
  9317. //-----------------------------------------------------------------------------
  9318. // Purpose: Set what type of rune we are carrying--or that we are not carrying any.
  9319. //-----------------------------------------------------------------------------
  9320. void CTFPlayerShared::SetCarryingRuneType( RuneTypes_t rt )
  9321. {
  9322. #ifdef GAME_DLL
  9323. // Stat Tracking
  9324. if ( rt != RUNE_NONE )
  9325. {
  9326. // if getting a rune, start timer
  9327. m_flRuneAcquireTime = gpGlobals->curtime;
  9328. }
  9329. else if ( IsCarryingRune() )
  9330. {
  9331. // if setting to none (death or drop) and I have a power up, report and set timer to -1
  9332. float duration = gpGlobals->curtime - m_flRuneAcquireTime;
  9333. m_flRuneAcquireTime = -1;
  9334. CTF_GameStats.Event_PowerUpRuneDuration( m_pOuter, (int)duration, GetCarryingRuneType() );
  9335. }
  9336. // clear rune charge
  9337. SetRuneCharge( 0.f );
  9338. #endif
  9339. // Not 100% sure AddCond does what I want to do if we already have that cond, so
  9340. // let's assert so we can debug it if it ever comes up.
  9341. Assert( rt != GetCarryingRuneType() );
  9342. // We are only ever allowed to carry one rune type at a time, this logic ensures that.
  9343. for ( int i = 0; i < RUNE_TYPES_MAX; ++i )
  9344. {
  9345. if ( i == rt )
  9346. {
  9347. AddCond( GetConditionFromRuneType( (RuneTypes_t) i ) );
  9348. }
  9349. else
  9350. {
  9351. RemoveCond( GetConditionFromRuneType( (RuneTypes_t) i ) );
  9352. }
  9353. }
  9354. }
  9355. //-----------------------------------------------------------------------------
  9356. // Purpose: Return the currently carried rune type, or RUNE_NONE if we are not carrying one.
  9357. //-----------------------------------------------------------------------------
  9358. RuneTypes_t CTFPlayerShared::GetCarryingRuneType( void ) const
  9359. {
  9360. RuneTypes_t retVal = RUNE_NONE;
  9361. for ( int i = 0; i < RUNE_TYPES_MAX; ++i )
  9362. {
  9363. if ( InCond( GetConditionFromRuneType( (RuneTypes_t) i ) ) )
  9364. {
  9365. // You are only allowed to have one rune type, if this hits we somehow erroneously
  9366. // have two condition bits set for different types of runes.
  9367. Assert( retVal == RUNE_NONE );
  9368. retVal = (RuneTypes_t)i;
  9369. break;
  9370. }
  9371. }
  9372. return retVal;
  9373. }
  9374. //-----------------------------------------------------------------------------
  9375. // Purpose:
  9376. //-----------------------------------------------------------------------------
  9377. int CTFPlayerShared::CalculateObjectCost( CTFPlayer* pBuilder, int iObjectType )
  9378. {
  9379. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  9380. {
  9381. #ifdef STAGING_ONLY
  9382. if ( ( TFGameRules()->InSetup() || TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() ) && iObjectType != OBJ_SPY_TRAP )
  9383. #else
  9384. if ( TFGameRules()->InSetup() || TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() )
  9385. #endif
  9386. {
  9387. return 0;
  9388. }
  9389. }
  9390. int nCost = InternalCalculateObjectCost( iObjectType );
  9391. // Mini sentires are 30 metal cheaper
  9392. CTFWrench* pWrench = dynamic_cast<CTFWrench*>( pBuilder->Weapon_OwnsThisID( TF_WEAPON_WRENCH ) );
  9393. if ( pWrench && pWrench->IsPDQ() && ( iObjectType == OBJ_SENTRYGUN ) )
  9394. {
  9395. nCost -= 30;
  9396. }
  9397. #ifdef STAGING_ONLY
  9398. // Mini dispensers are 30 metal cheaper
  9399. int nMiniDispenserEnabled = 0;
  9400. CALL_ATTRIB_HOOK_INT_ON_OTHER( pBuilder, nMiniDispenserEnabled, allows_building_mini_dispenser );
  9401. if ( nMiniDispenserEnabled != 0 && iObjectType == OBJ_DISPENSER )
  9402. {
  9403. nCost -= 30;
  9404. }
  9405. // Speed Pads are cheaper
  9406. int nSpeedPad = 0;
  9407. CALL_ATTRIB_HOOK_INT_ON_OTHER( pBuilder, nSpeedPad, teleporter_is_speedpad );
  9408. if ( nSpeedPad != 0 && iObjectType == OBJ_TELEPORTER )
  9409. {
  9410. nCost -= 25;
  9411. }
  9412. #endif
  9413. if ( iObjectType == OBJ_TELEPORTER )
  9414. {
  9415. float flCostMod = 1.f;
  9416. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pBuilder, flCostMod, mod_teleporter_cost );
  9417. if ( flCostMod != 1.f )
  9418. {
  9419. nCost *= flCostMod;
  9420. }
  9421. }
  9422. CALL_ATTRIB_HOOK_INT_ON_OTHER( pBuilder, nCost, building_cost_reduction );
  9423. return nCost;
  9424. }
  9425. //-----------------------------------------------------------------------------
  9426. // Purpose:
  9427. //-----------------------------------------------------------------------------
  9428. void CTFPlayerShared::HealthKitPickupEffects( int iHealthGiven /*= 0*/ )
  9429. {
  9430. // Healthkits also contain a fire blanket.
  9431. if ( InCond( TF_COND_BURNING ) )
  9432. {
  9433. RemoveCond( TF_COND_BURNING );
  9434. }
  9435. // and sutures
  9436. if ( InCond( TF_COND_BLEEDING ) )
  9437. {
  9438. RemoveCond( TF_COND_BLEEDING );
  9439. }
  9440. // and cures plague
  9441. if ( InCond( TF_COND_PLAGUE ) )
  9442. {
  9443. RemoveCond( TF_COND_PLAGUE );
  9444. }
  9445. // Spawns a number on the player's health bar in the HUD, and also
  9446. // spawns a "+" particle over their head for enemies to see
  9447. if ( iHealthGiven && !IsStealthed() && m_pOuter )
  9448. {
  9449. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  9450. if ( event )
  9451. {
  9452. event->SetInt( "amount", iHealthGiven );
  9453. event->SetInt( "entindex", m_pOuter->entindex() );
  9454. event->SetInt( "weapon_def_index", INVALID_ITEM_DEF_INDEX );
  9455. gameeventmanager->FireEvent( event );
  9456. }
  9457. }
  9458. }
  9459. //-----------------------------------------------------------------------------
  9460. // Purpose: Get the number of objects of the specified type that this player has
  9461. //-----------------------------------------------------------------------------
  9462. int CTFPlayer::GetNumObjects( int iObjectType, int iObjectMode /*= 0*/ )
  9463. {
  9464. int iCount = 0;
  9465. for (int i = 0; i < GetObjectCount(); i++)
  9466. {
  9467. if ( !GetObject(i) )
  9468. continue;
  9469. if ( GetObject(i)->IsDisposableBuilding() )
  9470. continue;
  9471. if ( GetObject(i)->GetType() == iObjectType &&
  9472. ( GetObject(i)->GetObjectMode() == iObjectMode || iObjectMode == BUILDING_MODE_ANY ) )
  9473. {
  9474. iCount++;
  9475. }
  9476. }
  9477. return iCount;
  9478. }
  9479. //-----------------------------------------------------------------------------
  9480. // Purpose:
  9481. //-----------------------------------------------------------------------------
  9482. void CTFPlayer::ItemPostFrame()
  9483. {
  9484. if ( m_hOffHandWeapon.Get() && m_hOffHandWeapon->IsWeaponVisible() )
  9485. {
  9486. if ( gpGlobals->curtime < m_flNextAttack )
  9487. {
  9488. m_hOffHandWeapon->ItemBusyFrame();
  9489. }
  9490. else
  9491. {
  9492. #if defined( CLIENT_DLL )
  9493. // Not predicting this weapon
  9494. if ( m_hOffHandWeapon->IsPredicted() )
  9495. #endif
  9496. {
  9497. m_hOffHandWeapon->ItemPostFrame( );
  9498. }
  9499. }
  9500. }
  9501. #ifdef GAME_DLL
  9502. CTFWeaponBase *pActiveWeapon = GetActiveTFWeapon();
  9503. if ( pActiveWeapon )
  9504. {
  9505. pActiveWeapon->HandleInspect();
  9506. }
  9507. #endif // GAME_DLL
  9508. BaseClass::ItemPostFrame();
  9509. }
  9510. void CTFPlayer::SetOffHandWeapon( CTFWeaponBase *pWeapon )
  9511. {
  9512. m_hOffHandWeapon = pWeapon;
  9513. if ( m_hOffHandWeapon.Get() )
  9514. {
  9515. m_hOffHandWeapon->Deploy();
  9516. }
  9517. }
  9518. // Set to NULL at the end of the holster?
  9519. void CTFPlayer::HolsterOffHandWeapon( void )
  9520. {
  9521. if ( m_hOffHandWeapon.Get() )
  9522. {
  9523. m_hOffHandWeapon->Holster();
  9524. }
  9525. }
  9526. //-----------------------------------------------------------------------------
  9527. // Purpose: Return true if we should record our last weapon when switching between the two specified weapons
  9528. //-----------------------------------------------------------------------------
  9529. bool CTFPlayer::Weapon_ShouldSetLast( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon )
  9530. {
  9531. // if the weapon doesn't want to be auto-switched to, don't!
  9532. CTFWeaponBase *pTFOldWeapon = dynamic_cast< CTFWeaponBase * >( pOldWeapon );
  9533. if ( pTFOldWeapon )
  9534. {
  9535. if ( pTFOldWeapon->AllowsAutoSwitchTo() == false )
  9536. {
  9537. return false;
  9538. }
  9539. }
  9540. return BaseClass::Weapon_ShouldSetLast( pOldWeapon, pNewWeapon );
  9541. }
  9542. //-----------------------------------------------------------------------------
  9543. // Purpose:
  9544. //-----------------------------------------------------------------------------
  9545. void CTFPlayer::SelectItem( const char *pstr, int iSubType /*= 0*/ )
  9546. {
  9547. // This is basically a copy from the base class with addition of Weapon_CanSwitchTo
  9548. // We're not calling BaseClass::SelectItem on purpose to prevent breaking other games
  9549. // that might rely on not calling Weapon_CanSwitchTo
  9550. if (!pstr)
  9551. return;
  9552. CBaseCombatWeapon *pItem = Weapon_OwnsThisType( pstr, iSubType );
  9553. if (!pItem)
  9554. return;
  9555. if( GetObserverMode() != OBS_MODE_NONE )
  9556. return;// Observers can't select things.
  9557. if ( !Weapon_ShouldSelectItem( pItem ) )
  9558. return;
  9559. // FIX, this needs to queue them up and delay
  9560. // Make sure the current weapon can be holstered
  9561. if ( !Weapon_CanSwitchTo( pItem ) )
  9562. return;
  9563. ResetAutoaim();
  9564. Weapon_Switch( pItem );
  9565. }
  9566. //-----------------------------------------------------------------------------
  9567. // Purpose:
  9568. //-----------------------------------------------------------------------------
  9569. bool CTFPlayer::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex )
  9570. {
  9571. // Ghosts cant switch weapons!
  9572. if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  9573. {
  9574. return false;
  9575. }
  9576. // set last weapon before we switch to a new weapon to make sure that we can get the correct last weapon in Deploy/Holster
  9577. // This should be done in CBasePlayer::Weapon_Switch, but we don't want to break other games
  9578. CBaseCombatWeapon *pPreviousLastWeapon = GetLastWeapon();
  9579. CBaseCombatWeapon *pPreviousActiveWeapon = GetActiveWeapon();
  9580. // always set last for Weapon_Switch code to get attribute from the correct last item
  9581. Weapon_SetLast( GetActiveWeapon() );
  9582. bool bSwitched = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
  9583. if ( bSwitched )
  9584. {
  9585. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_ATTACK_AND_RELOAD );
  9586. // valid last weapon
  9587. if ( Weapon_ShouldSetLast( pPreviousActiveWeapon, pWeapon ) )
  9588. {
  9589. Weapon_SetLast( pPreviousActiveWeapon );
  9590. SetSecondaryLastWeapon( pPreviousLastWeapon );
  9591. }
  9592. // previous active weapon is not valid to be last weapon, but the new active weapon is
  9593. else if ( Weapon_ShouldSetLast( pWeapon, pPreviousLastWeapon ) )
  9594. {
  9595. // this will skip the logic to ignore first time block and allow weapon to check for honorbound attribute right away
  9596. CTFWeaponBase *pTFWeapon = assert_cast< CTFWeaponBase * >( pWeapon );
  9597. if ( pTFWeapon && pTFWeapon->IsHonorBound() )
  9598. {
  9599. m_Shared.m_flFirstPrimaryAttack = gpGlobals->curtime;
  9600. }
  9601. if ( pWeapon != GetSecondaryLastWeapon() )
  9602. {
  9603. Weapon_SetLast( GetSecondaryLastWeapon() );
  9604. SetSecondaryLastWeapon( pPreviousLastWeapon );
  9605. }
  9606. else
  9607. {
  9608. // new active weapon is the same as the secondary last weapon, leave the last weapon alone
  9609. Weapon_SetLast( pPreviousLastWeapon );
  9610. }
  9611. }
  9612. // both previous and new active weapons are not not valid for last weapon
  9613. else
  9614. {
  9615. Weapon_SetLast( pPreviousLastWeapon );
  9616. }
  9617. }
  9618. else
  9619. {
  9620. // restore to the previous last weapon if we failed to switch to a new weapon
  9621. Weapon_SetLast( pPreviousLastWeapon );
  9622. }
  9623. #ifdef GAME_DLL
  9624. if ( bSwitched && TFGameRules() && TFGameRules()->IsInTraining() && TFGameRules()->GetTrainingModeLogic() && IsFakeClient() == false)
  9625. {
  9626. TFGameRules()->GetTrainingModeLogic()->OnPlayerSwitchedWeapons( this );
  9627. }
  9628. #endif
  9629. #ifdef CLIENT_DLL
  9630. m_Shared.UpdateCritBoostEffect();
  9631. #endif
  9632. return bSwitched;
  9633. }
  9634. //-----------------------------------------------------------------------------
  9635. // Purpose:
  9636. //-----------------------------------------------------------------------------
  9637. CTFWearable *CTFPlayer::GetEquippedWearableForLoadoutSlot( int iLoadoutSlot )
  9638. {
  9639. int iClass = GetPlayerClass()->GetClassIndex();
  9640. for ( int i = 0; i < GetNumWearables(); ++i )
  9641. {
  9642. CTFWearable *pWearableItem = dynamic_cast< CTFWearable * >( GetWearable( i ) );
  9643. if ( !pWearableItem )
  9644. continue;
  9645. if ( !pWearableItem->GetAttributeContainer() )
  9646. continue;
  9647. CEconItemView *pEconItemView = pWearableItem->GetAttributeContainer()->GetItem();
  9648. if ( !pEconItemView )
  9649. continue;
  9650. CTFItemDefinition *pItemDef = pEconItemView->GetStaticData();
  9651. if ( !pItemDef )
  9652. continue;
  9653. if ( pItemDef->GetLoadoutSlot(iClass) == iLoadoutSlot )
  9654. return pWearableItem;
  9655. }
  9656. return NULL;
  9657. }
  9658. //-----------------------------------------------------------------------------
  9659. CBaseEntity *CTFPlayer::GetEntityForLoadoutSlot( int iLoadoutSlot )
  9660. {
  9661. CBaseEntity *pEntity = NULL;
  9662. if ( IsWearableSlot( iLoadoutSlot ) )
  9663. {
  9664. // Search Wearables first otherwise search Weapons as a fall back
  9665. pEntity = GetEquippedWearableForLoadoutSlot( iLoadoutSlot );
  9666. if ( pEntity )
  9667. {
  9668. return pEntity;
  9669. }
  9670. }
  9671. int iClass = GetPlayerClass()->GetClassIndex();
  9672. for ( int i = 0; i < MAX_WEAPONS; i++ )
  9673. {
  9674. if ( GetWeapon(i) )
  9675. {
  9676. CEconItemView *pEconItemView = GetWeapon(i)->GetAttributeContainer()->GetItem();
  9677. if ( !pEconItemView )
  9678. continue;
  9679. CTFItemDefinition *pItemDef = pEconItemView->GetStaticData();
  9680. if ( !pItemDef )
  9681. continue;
  9682. if ( pItemDef->GetLoadoutSlot( iClass ) == iLoadoutSlot )
  9683. return GetWeapon(i);
  9684. }
  9685. }
  9686. return NULL;
  9687. }
  9688. #ifdef CLIENT_DLL
  9689. //-----------------------------------------------------------------------------
  9690. // Purpose:
  9691. //-----------------------------------------------------------------------------
  9692. void CTFPlayer::StopViewModelParticles( C_BaseEntity *pParticleEnt )
  9693. {
  9694. pParticleEnt->ParticleProp()->StopParticlesInvolving( pParticleEnt );
  9695. }
  9696. #endif
  9697. //-----------------------------------------------------------------------------
  9698. // Purpose:
  9699. //-----------------------------------------------------------------------------
  9700. void CTFPlayer::GetStepSoundVelocities( float *velwalk, float *velrun )
  9701. {
  9702. float flMaxSpeed = MaxSpeed();
  9703. if ( ( GetFlags() & FL_DUCKING ) || ( GetMoveType() == MOVETYPE_LADDER ) )
  9704. {
  9705. if ( m_Shared.IsLoser() )
  9706. {
  9707. *velwalk = 0;
  9708. *velrun = 0;
  9709. }
  9710. else
  9711. {
  9712. *velwalk = flMaxSpeed * 0.25;
  9713. *velrun = flMaxSpeed * 0.3;
  9714. }
  9715. }
  9716. else
  9717. {
  9718. *velwalk = flMaxSpeed * 0.3;
  9719. *velrun = flMaxSpeed * 0.8;
  9720. }
  9721. }
  9722. //-----------------------------------------------------------------------------
  9723. // Purpose:
  9724. //-----------------------------------------------------------------------------
  9725. void CTFPlayer::SetStepSoundTime( stepsoundtimes_t iStepSoundTime, bool bWalking )
  9726. {
  9727. float flMaxSpeed = MaxSpeed();
  9728. switch ( iStepSoundTime )
  9729. {
  9730. case STEPSOUNDTIME_NORMAL:
  9731. case STEPSOUNDTIME_WATER_FOOT:
  9732. m_flStepSoundTime = RemapValClamped( flMaxSpeed, 200, 450, 400, 200 );
  9733. if ( bWalking )
  9734. {
  9735. m_flStepSoundTime += 100;
  9736. }
  9737. break;
  9738. case STEPSOUNDTIME_ON_LADDER:
  9739. m_flStepSoundTime = 350;
  9740. break;
  9741. case STEPSOUNDTIME_WATER_KNEE:
  9742. m_flStepSoundTime = RemapValClamped( flMaxSpeed, 200, 450, 600, 400 );
  9743. break;
  9744. default:
  9745. Assert(0);
  9746. break;
  9747. }
  9748. if ( ( GetFlags() & FL_DUCKING) || ( GetMoveType() == MOVETYPE_LADDER ) )
  9749. {
  9750. m_flStepSoundTime += 100;
  9751. }
  9752. }
  9753. //-----------------------------------------------------------------------------
  9754. // Purpose:
  9755. //-----------------------------------------------------------------------------
  9756. const char *CTFPlayer::GetOverrideStepSound( const char *pszBaseStepSoundName )
  9757. {
  9758. if( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS && !IsMiniBoss() && !m_Shared.InCond( TF_COND_DISGUISED ) )
  9759. {
  9760. return "MVM.BotStep";
  9761. }
  9762. Assert( pszBaseStepSoundName );
  9763. struct override_sound_entry_t { int iOverrideIndex; const char *pszBaseSoundName; const char *pszNewSoundName; };
  9764. enum
  9765. {
  9766. kFootstepSoundSet_Default = 0,
  9767. kFootstepSoundSet_SoccerCleats = 1,
  9768. kFootstepSoundSet_HeavyGiant = 2,
  9769. kFootstepSoundSet_SoldierGiant = 3,
  9770. kFootstepSoundSet_DemoGiant = 4,
  9771. kFootstepSoundSet_ScoutGiant = 5,
  9772. kFootstepSoundSet_PyroGiant = 6,
  9773. kFootstepSoundSet_SentryBuster = 7,
  9774. kFootstepSoundSet_TreasureChest = 8,
  9775. kFootstepSoundSet_Octopus = 9,
  9776. };
  9777. int iOverrideFootstepSoundSet = kFootstepSoundSet_Default;
  9778. CALL_ATTRIB_HOOK_INT( iOverrideFootstepSoundSet, override_footstep_sound_set );
  9779. if ( iOverrideFootstepSoundSet != kFootstepSoundSet_Default )
  9780. {
  9781. static const override_sound_entry_t s_ReplacementSounds[] =
  9782. {
  9783. { kFootstepSoundSet_SoccerCleats, "Default.StepLeft", "cleats_conc.StepLeft" },
  9784. { kFootstepSoundSet_SoccerCleats, "Default.StepRight", "cleats_conc.StepRight" },
  9785. { kFootstepSoundSet_SoccerCleats, "Dirt.StepLeft", "cleats_dirt.StepLeft" },
  9786. { kFootstepSoundSet_SoccerCleats, "Dirt.StepRight", "cleats_dirt.StepRight" },
  9787. { kFootstepSoundSet_SoccerCleats, "Concrete.StepLeft", "cleats_conc.StepLeft" },
  9788. { kFootstepSoundSet_SoccerCleats, "Concrete.StepRight", "cleats_conc.StepRight" },
  9789. //
  9790. { kFootstepSoundSet_Octopus, "Default.StepLeft", "Octopus.StepCommon" },
  9791. { kFootstepSoundSet_Octopus, "Default.StepRight", "Octopus.StepCommon" },
  9792. { kFootstepSoundSet_Octopus, "Dirt.StepLeft", "Octopus.StepCommon" },
  9793. { kFootstepSoundSet_Octopus, "Dirt.StepRight", "Octopus.StepCommon" },
  9794. { kFootstepSoundSet_Octopus, "Concrete.StepLeft", "Octopus.StepCommon" },
  9795. { kFootstepSoundSet_Octopus, "Concrete.StepRight", "Octopus.StepCommon" },
  9796. //
  9797. { kFootstepSoundSet_HeavyGiant, "", "MVM.GiantHeavyStep" },
  9798. //
  9799. { kFootstepSoundSet_SoldierGiant, "", "MVM.GiantSoldierStep" },
  9800. //
  9801. { kFootstepSoundSet_DemoGiant, "", "MVM.GiantDemomanStep" },
  9802. //
  9803. { kFootstepSoundSet_ScoutGiant, "", "MVM.GiantScoutStep" },
  9804. //
  9805. { kFootstepSoundSet_PyroGiant, "", "MVM.GiantPyroStep" },
  9806. //
  9807. { kFootstepSoundSet_SentryBuster, "", "MVM.SentryBusterStep" },
  9808. //
  9809. { kFootstepSoundSet_TreasureChest, "", "Chest.Step" },
  9810. };
  9811. for ( int i = 0; i < ARRAYSIZE( s_ReplacementSounds ); i++ )
  9812. {
  9813. if ( iOverrideFootstepSoundSet == s_ReplacementSounds[i].iOverrideIndex )
  9814. {
  9815. if ( !s_ReplacementSounds[i].pszBaseSoundName[0] ||
  9816. !Q_stricmp( pszBaseStepSoundName, s_ReplacementSounds[i].pszBaseSoundName ) )
  9817. return s_ReplacementSounds[i].pszNewSoundName;
  9818. }
  9819. }
  9820. }
  9821. // Fallback.
  9822. return BaseClass::GetOverrideStepSound( pszBaseStepSoundName );
  9823. }
  9824. void CTFPlayer::OnEmitFootstepSound( const CSoundParameters& params, const Vector& vecOrigin, float fVolume )
  9825. {
  9826. // play jingles in addition to normal footstep sounds,
  9827. // and play them quietly to the local player so they don't go insane
  9828. int iJingle = 0;
  9829. CALL_ATTRIB_HOOK_INT( iJingle, add_jingle_to_footsteps );
  9830. if ( iJingle > 0 )
  9831. {
  9832. CRecipientFilter filter;
  9833. filter.AddRecipientsByPAS( vecOrigin );
  9834. #ifndef CLIENT_DLL
  9835. // in MP, server removes all players in the vecOrigin's PVS, these players generate the footsteps client side
  9836. if ( gpGlobals->maxClients > 1 )
  9837. {
  9838. filter.RemoveRecipientsByPVS( vecOrigin );
  9839. }
  9840. #endif
  9841. EmitSound_t ep;
  9842. ep.m_nChannel = CHAN_BODY;
  9843. ep.m_pSoundName = ( iJingle == 1 ) ? "xmas.jingle" : "xmas.jingle_higher";
  9844. #ifdef CLIENT_DLL
  9845. ep.m_flVolume = IsLocalPlayer() ? 0.3f * fVolume : fVolume; // quieter for local player
  9846. #else
  9847. ep.m_flVolume = fVolume;
  9848. #endif
  9849. ep.m_SoundLevel = params.soundlevel;
  9850. ep.m_nFlags = SND_CHANGE_VOL;
  9851. ep.m_nPitch = params.pitch;
  9852. ep.m_pOrigin = &vecOrigin;
  9853. EmitSound( filter, entindex(), ep );
  9854. }
  9855. #ifdef CLIENT_DLL
  9856. // Halloween-specific bonus footsteps for viewmodel-rendering only. Real model footsteps will happen in the real
  9857. // footstep code in response to animation events. THIS IS A HACK!
  9858. if ( !ShouldDrawThisPlayer() && !m_Shared.IsStealthed() && !m_Shared.InCond( TF_COND_DISGUISED ) )
  9859. {
  9860. int iHalloweenFootstepType = 0;
  9861. if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  9862. {
  9863. CALL_ATTRIB_HOOK_INT( iHalloweenFootstepType, halloween_footstep_type );
  9864. }
  9865. if ( m_nFootStamps > 0 )
  9866. {
  9867. // White color!
  9868. iHalloweenFootstepType = 0xFFFFFFFF;
  9869. }
  9870. if ( iHalloweenFootstepType != 0 )
  9871. {
  9872. CNewParticleEffect *pEffect = SpawnHalloweenSpellFootsteps( PATTACH_CUSTOMORIGIN, iHalloweenFootstepType );
  9873. if ( pEffect )
  9874. {
  9875. pEffect->SetControlPoint( 0, GetAbsOrigin() );
  9876. }
  9877. }
  9878. if ( m_nFootStamps > 0 )
  9879. {
  9880. m_nFootStamps--;
  9881. }
  9882. }
  9883. #endif
  9884. }
  9885. void CTFPlayer::ModifyEmitSoundParams( EmitSound_t &params )
  9886. {
  9887. BaseClass::ModifyEmitSoundParams( params );
  9888. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  9889. if ( pWeapon )
  9890. {
  9891. pWeapon->ModifyEmitSoundParams( params );
  9892. }
  9893. }
  9894. //-----------------------------------------------------------------------------
  9895. // Purpose:
  9896. //-----------------------------------------------------------------------------
  9897. bool CTFPlayer::CanAttack( int iCanAttackFlags )
  9898. {
  9899. CTFGameRules *pRules = TFGameRules();
  9900. Assert( pRules );
  9901. if ( m_Shared.HasPasstimeBall() )
  9902. {
  9903. // Always allow throwing the ball.
  9904. return true;
  9905. }
  9906. if ( ( m_Shared.GetStealthNoAttackExpireTime() > gpGlobals->curtime && !m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF ) ) || m_Shared.InCond( TF_COND_STEALTHED ) )
  9907. {
  9908. if ( !( iCanAttackFlags & TF_CAN_ATTACK_FLAG_GRAPPLINGHOOK ) )
  9909. {
  9910. #ifdef CLIENT_DLL
  9911. HintMessage( HINT_CANNOT_ATTACK_WHILE_CLOAKED, true, true );
  9912. #endif
  9913. return false;
  9914. }
  9915. }
  9916. if ( m_Shared.IsFeignDeathReady() )
  9917. {
  9918. #ifdef CLIENT_DLL
  9919. HintMessage( HINT_CANNOT_ATTACK_WHILE_FEIGN_ARMED, true, true );
  9920. #endif
  9921. return false;
  9922. }
  9923. if ( IsTaunting() )
  9924. return false;
  9925. if ( m_Shared.InCond( TF_COND_PHASE ) == true )
  9926. return false;
  9927. if ( ( pRules->State_Get() == GR_STATE_TEAM_WIN ) && ( pRules->GetWinningTeam() != GetTeamNumber() ) )
  9928. {
  9929. return false;
  9930. }
  9931. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  9932. {
  9933. return false;
  9934. }
  9935. return true;
  9936. }
  9937. //-----------------------------------------------------------------------------
  9938. // Purpose: Weapons can call this on secondary attack and it will link to the class
  9939. // ability
  9940. //-----------------------------------------------------------------------------
  9941. bool CTFPlayer::DoClassSpecialSkill( void )
  9942. {
  9943. if ( !IsAlive() )
  9944. return false;
  9945. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  9946. return false;
  9947. bool bDoSkill = false;
  9948. // powerup charge activation has higher priority than any class special skill
  9949. if ( m_Shared.IsRuneCharged() && GetActiveTFWeapon() && GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_GRAPPLINGHOOK )
  9950. {
  9951. #ifdef GAME_DLL
  9952. CTFGrapplingHook *pHook = static_cast<CTFGrapplingHook*>( GetActiveTFWeapon() );
  9953. if ( pHook )
  9954. {
  9955. pHook->ActivateRune();
  9956. }
  9957. #endif // GAME_DLL
  9958. return true;
  9959. }
  9960. switch( GetPlayerClass()->GetClassIndex() )
  9961. {
  9962. case TF_CLASS_SPY:
  9963. {
  9964. if ( !m_Shared.InCond( TF_COND_TAUNTING ) )
  9965. {
  9966. if ( m_Shared.m_flStealthNextChangeTime <= gpGlobals->curtime )
  9967. {
  9968. // Feign death if we have the right equipment mod.
  9969. CTFWeaponInvis* pInvisWatch = static_cast<CTFWeaponInvis*>( Weapon_OwnsThisID( TF_WEAPON_INVIS ) );
  9970. if ( pInvisWatch )
  9971. {
  9972. pInvisWatch->ActivateInvisibilityWatch();
  9973. }
  9974. }
  9975. }
  9976. }
  9977. break;
  9978. case TF_CLASS_DEMOMAN:
  9979. if ( !m_Shared.HasPasstimeBall() )
  9980. {
  9981. CTFPipebombLauncher *pPipebombLauncher = static_cast<CTFPipebombLauncher*>( Weapon_OwnsThisID( TF_WEAPON_PIPEBOMBLAUNCHER ) );
  9982. if ( pPipebombLauncher )
  9983. {
  9984. pPipebombLauncher->SecondaryAttack();
  9985. }
  9986. else
  9987. {
  9988. CTFWearableDemoShield *pWearableShield = GetEquippedDemoShield( this );
  9989. if ( pWearableShield )
  9990. {
  9991. pWearableShield->DoSpecialAction( this );
  9992. break;
  9993. }
  9994. }
  9995. }
  9996. bDoSkill = true;
  9997. break;
  9998. case TF_CLASS_ENGINEER:
  9999. if ( !m_Shared.HasPasstimeBall() )
  10000. {
  10001. bDoSkill = TryToPickupBuilding();
  10002. }
  10003. break;
  10004. default:
  10005. break;
  10006. }
  10007. return bDoSkill;
  10008. }
  10009. //-----------------------------------------------------------------------------
  10010. // Purpose:
  10011. //-----------------------------------------------------------------------------
  10012. bool CTFPlayer::EndClassSpecialSkill( void )
  10013. {
  10014. if ( !IsAlive() )
  10015. return false;
  10016. switch( GetPlayerClass()->GetClassIndex() )
  10017. {
  10018. case TF_CLASS_DEMOMAN:
  10019. {
  10020. CTFPipebombLauncher *pPipebombLauncher = static_cast<CTFPipebombLauncher*>( Weapon_OwnsThisID( TF_WEAPON_PIPEBOMBLAUNCHER ) );
  10021. if ( !pPipebombLauncher )
  10022. {
  10023. CTFWearableDemoShield *pWearableShield = GetEquippedDemoShield( this );
  10024. if ( pWearableShield )
  10025. {
  10026. pWearableShield->EndSpecialAction( this );
  10027. break;
  10028. }
  10029. }
  10030. }
  10031. break;
  10032. default:
  10033. break;
  10034. }
  10035. return true;
  10036. }
  10037. //-----------------------------------------------------------------------------
  10038. // Purpose:
  10039. //-----------------------------------------------------------------------------
  10040. bool CTFPlayer::CanPickupBuilding( CBaseObject *pPickupObject )
  10041. {
  10042. if ( !pPickupObject )
  10043. return false;
  10044. if ( pPickupObject->IsBuilding() )
  10045. return false;
  10046. if ( pPickupObject->IsUpgrading() )
  10047. return false;
  10048. if ( pPickupObject->HasSapper() )
  10049. return false;
  10050. if ( pPickupObject->IsPlasmaDisabled() )
  10051. return false;
  10052. // If we were recently carried & placed we may still be upgrading up to our old level.
  10053. if ( pPickupObject->GetUpgradeLevel() != pPickupObject->GetHighestUpgradeLevel() )
  10054. return false;
  10055. if ( m_Shared.IsCarryingObject() )
  10056. return false;
  10057. if ( m_Shared.IsLoserStateStunned() || m_Shared.IsControlStunned() )
  10058. return false;
  10059. if ( m_Shared.IsLoser() )
  10060. return false;
  10061. if ( TFGameRules()->State_Get() != GR_STATE_RND_RUNNING && TFGameRules()->State_Get() != GR_STATE_STALEMATE && TFGameRules()->State_Get() != GR_STATE_BETWEEN_RNDS )
  10062. return false;
  10063. // don't allow to pick up building while grappling hook
  10064. if ( m_Shared.InCond( TF_COND_GRAPPLINGHOOK ) )
  10065. return false;
  10066. // There's ammo in the clip... no switching away!
  10067. if ( GetActiveTFWeapon() && GetActiveTFWeapon()->AutoFiresFullClip() && GetActiveTFWeapon()->Clip1() > 0 )
  10068. return false;
  10069. // Knockout powerup restricts user to melee only, so cannot equip other items such as building pickups
  10070. if ( m_Shared.GetCarryingRuneType() == RUNE_KNOCKOUT )
  10071. // ClientPrint( this, HUD_PRINTCENTER, "#TF_Powerup_Pickup_Deny" );
  10072. {
  10073. ClientPrint( this, HUD_PRINTCENTER, "You can't pickup buildings while holding the KNOCKOUT powerup" );
  10074. return false;
  10075. }
  10076. // Check it's within range
  10077. int nPickUpRangeSq = TF_BUILDING_PICKUP_RANGE * TF_BUILDING_PICKUP_RANGE;
  10078. int iIncreasedRangeCost = 0;
  10079. int nSqrDist = (EyePosition() - pPickupObject->GetAbsOrigin()).LengthSqr();
  10080. // Extra range only works with primary weapon
  10081. CTFWeaponBase * pWeapon = GetActiveTFWeapon();
  10082. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iIncreasedRangeCost, building_teleporting_pickup );
  10083. if ( iIncreasedRangeCost != 0 )
  10084. {
  10085. // False on deadzone
  10086. if ( nSqrDist > nPickUpRangeSq && nSqrDist < TF_BUILDING_RESCUE_MIN_RANGE_SQ )
  10087. return false;
  10088. if ( nSqrDist >= TF_BUILDING_RESCUE_MIN_RANGE_SQ && GetAmmoCount( TF_AMMO_METAL ) < iIncreasedRangeCost )
  10089. return false;
  10090. return true;
  10091. }
  10092. else if ( nSqrDist > nPickUpRangeSq )
  10093. return false;
  10094. if ( TFGameRules()->IsInTraining() )
  10095. {
  10096. ConVarRef training_can_pickup_sentry( "training_can_pickup_sentry" );
  10097. ConVarRef training_can_pickup_dispenser( "training_can_pickup_dispenser" );
  10098. ConVarRef training_can_pickup_tele_entrance( "training_can_pickup_tele_entrance" );
  10099. ConVarRef training_can_pickup_tele_exit( "training_can_pickup_tele_exit" );
  10100. switch ( pPickupObject->GetType() )
  10101. {
  10102. case OBJ_DISPENSER:
  10103. return training_can_pickup_dispenser.GetBool();
  10104. case OBJ_TELEPORTER:
  10105. return pPickupObject->GetObjectMode() == MODE_TELEPORTER_ENTRANCE ? training_can_pickup_tele_entrance.GetBool() : training_can_pickup_tele_exit.GetBool();
  10106. case OBJ_SENTRYGUN:
  10107. return training_can_pickup_sentry.GetBool();
  10108. } // switch
  10109. }
  10110. return true;
  10111. }
  10112. //-----------------------------------------------------------------------------
  10113. // Purpose:
  10114. //-----------------------------------------------------------------------------
  10115. bool CTFPlayer::TryToPickupBuilding()
  10116. {
  10117. if ( m_Shared.IsCarryingObject() )
  10118. return false;
  10119. if ( m_Shared.InCond( TF_COND_TAUNTING ) )
  10120. return false;
  10121. if ( m_Shared.InCond( TF_COND_HALLOWEEN_TINY ) )
  10122. return false;
  10123. if ( m_Shared.InCond( TF_COND_MELEE_ONLY ) )
  10124. return false;
  10125. if ( m_Shared.InCond( TF_COND_SWIMMING_CURSE ) )
  10126. return false;
  10127. #ifdef GAME_DLL
  10128. if ( m_bIsTeleportingUsingEurekaEffect )
  10129. return false;
  10130. int iCannotPickUpBuildings = 0;
  10131. CALL_ATTRIB_HOOK_INT( iCannotPickUpBuildings, cannot_pick_up_buildings );
  10132. if ( iCannotPickUpBuildings )
  10133. {
  10134. return false;
  10135. }
  10136. #endif
  10137. // Check to see if a building we own is in front of us.
  10138. Vector vecForward;
  10139. AngleVectors( EyeAngles(), &vecForward, NULL, NULL );
  10140. int iPickUpRange = TF_BUILDING_PICKUP_RANGE;
  10141. int iIncreasedRangeCost = 0;
  10142. CTFWeaponBase * pWeapon = GetActiveTFWeapon();
  10143. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iIncreasedRangeCost, building_teleporting_pickup );
  10144. if ( iIncreasedRangeCost != 0 )
  10145. {
  10146. iPickUpRange = TF_BUILDING_RESCUE_MAX_RANGE;
  10147. }
  10148. // Create a ray a see if any of my objects touch it
  10149. Ray_t ray;
  10150. ray.Init( EyePosition(), EyePosition() + vecForward * iPickUpRange );
  10151. CBulletPenetrateEnum ePickupPenetrate( &ray, this, TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS, false );
  10152. enginetrace->EnumerateEntities( ray, false, &ePickupPenetrate );
  10153. CBaseObject *pPickupObject = NULL;
  10154. float flCurrDistanceSq = iPickUpRange * iPickUpRange;
  10155. for ( int i=0; i<GetObjectCount(); i++ )
  10156. {
  10157. CBaseObject *pObj = GetObject(i);
  10158. if ( !pObj )
  10159. continue;
  10160. float flDistToObjSq = ( pObj->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  10161. if ( flDistToObjSq > flCurrDistanceSq )
  10162. continue;
  10163. FOR_EACH_VEC( ePickupPenetrate.m_Targets, iTarget )
  10164. {
  10165. if ( ePickupPenetrate.m_Targets[iTarget].pTarget == pObj )
  10166. {
  10167. CTargetOnlyFilter penetrateFilter( this, pObj );
  10168. trace_t pTraceToUse;
  10169. UTIL_TraceLine( EyePosition(), EyePosition() + vecForward * iPickUpRange, ( MASK_SOLID | CONTENTS_HITBOX ), &penetrateFilter, &pTraceToUse );
  10170. if ( pTraceToUse.m_pEnt == pObj )
  10171. {
  10172. pPickupObject = pObj;
  10173. flCurrDistanceSq = flDistToObjSq;
  10174. break;
  10175. }
  10176. }
  10177. if ( ePickupPenetrate.m_Targets[iTarget].pTarget->IsWorld() )
  10178. {
  10179. break;
  10180. }
  10181. }
  10182. }
  10183. if ( !CanPickupBuilding(pPickupObject) )
  10184. {
  10185. if ( pPickupObject )
  10186. {
  10187. CSingleUserRecipientFilter filter( this );
  10188. EmitSound( filter, entindex(), "Player.UseDeny", NULL, 0.0f );
  10189. }
  10190. return false;
  10191. }
  10192. #ifdef CLIENT_DLL
  10193. return (bool) pPickupObject;
  10194. #elif GAME_DLL
  10195. if ( pPickupObject )
  10196. {
  10197. // remove rage for long range
  10198. if ( iIncreasedRangeCost )
  10199. {
  10200. int nSqrDist = (EyePosition() - pPickupObject->GetAbsOrigin()).LengthSqr();
  10201. if ( nSqrDist > TF_BUILDING_RESCUE_MIN_RANGE_SQ )
  10202. {
  10203. RemoveAmmo( iIncreasedRangeCost, TF_AMMO_METAL );
  10204. // Particles
  10205. // Spawn a railgun
  10206. Vector origin = pPickupObject->GetAbsOrigin();
  10207. CPVSFilter filter( origin );
  10208. const char *pRailParticleName = GetTeamNumber() == TF_TEAM_BLUE ? "dxhr_sniper_rail_blue" : "dxhr_sniper_rail_red";
  10209. const char *pTeleParticleName = GetTeamNumber() == TF_TEAM_BLUE ? "teleported_blue" : "teleported_red";
  10210. TE_TFParticleEffect( filter, 0.0, pTeleParticleName, origin, vec3_angle );
  10211. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_WORLDORIGIN, pPickupObject->GetAbsOrigin() + Vector(0,0,32) };
  10212. TE_TFParticleEffectComplex( filter, 0.0f, pRailParticleName, GetAbsOrigin() + Vector(0,0,32), QAngle( 0, 0, 0 ), NULL, &controlPoint );
  10213. // Play Sounds
  10214. pPickupObject->EmitSound( "Building_Teleporter.Send" );
  10215. EmitSound( "Building_Teleporter.Receive" );
  10216. }
  10217. }
  10218. pPickupObject->MakeCarriedObject( this );
  10219. CTFWeaponBuilder *pBuilder = dynamic_cast<CTFWeaponBuilder*>(Weapon_OwnsThisID( TF_WEAPON_BUILDER ));
  10220. if ( pBuilder )
  10221. {
  10222. if ( GetActiveTFWeapon() == pBuilder )
  10223. SetActiveWeapon( NULL );
  10224. Weapon_Switch( pBuilder );
  10225. pBuilder->m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
  10226. }
  10227. SpeakConceptIfAllowed( MP_CONCEPT_PICKUP_BUILDING, pPickupObject->GetResponseRulesModifier() );
  10228. m_flCommentOnCarrying = gpGlobals->curtime + random->RandomFloat( 6.f, 12.f );
  10229. return true;
  10230. }
  10231. else
  10232. {
  10233. return false;
  10234. }
  10235. #endif
  10236. }
  10237. //-----------------------------------------------------------------------------
  10238. // Purpose:
  10239. //-----------------------------------------------------------------------------
  10240. bool CTFPlayer::CanGoInvisible( bool bAllowWhileCarryingFlag )
  10241. {
  10242. // The "flag" in Player Destruction doesn't block cloak
  10243. ETFFlagType ignoreTypes[] = { TF_FLAGTYPE_PLAYER_DESTRUCTION };
  10244. if ( !bAllowWhileCarryingFlag && ( HasTheFlag( ignoreTypes, ARRAYSIZE( ignoreTypes ) ) || m_Shared.HasPasstimeBall() ) )
  10245. {
  10246. HintMessage( HINT_CANNOT_CLOAK_WITH_FLAG );
  10247. return false;
  10248. }
  10249. CTFGameRules *pRules = TFGameRules();
  10250. Assert( pRules );
  10251. if ( ( pRules->State_Get() == GR_STATE_TEAM_WIN ) && ( pRules->GetWinningTeam() != GetTeamNumber() ) )
  10252. {
  10253. return false;
  10254. }
  10255. return true;
  10256. }
  10257. //-----------------------------------------------------------------------------
  10258. // Purpose:
  10259. //-----------------------------------------------------------------------------
  10260. bool CTFPlayer::CanStartPhase( void )
  10261. {
  10262. if ( HasTheFlag() || m_Shared.HasPasstimeBall() )
  10263. {
  10264. HintMessage( HINT_CANNOT_PHASE_WITH_FLAG );
  10265. return false;
  10266. }
  10267. CTFGameRules *pRules = TFGameRules();
  10268. Assert( pRules );
  10269. if ( ( pRules->State_Get() == GR_STATE_TEAM_WIN ) && ( pRules->GetWinningTeam() != GetTeamNumber() ) )
  10270. {
  10271. return false;
  10272. }
  10273. return true;
  10274. }
  10275. //ConVar testclassviewheight( "testclassviewheight", "0", FCVAR_DEVELOPMENTONLY );
  10276. //Vector vecTestViewHeight(0,0,0);
  10277. //-----------------------------------------------------------------------------
  10278. // Purpose: Return class-specific standing eye height
  10279. //-----------------------------------------------------------------------------
  10280. Vector CTFPlayer::GetClassEyeHeight( void )
  10281. {
  10282. CTFPlayerClass *pClass = GetPlayerClass();
  10283. if ( !pClass )
  10284. return VEC_VIEW_SCALED( this );
  10285. //if ( testclassviewheight.GetFloat() > 0 )
  10286. //{
  10287. // vecTestViewHeight.z = test.GetFloat();
  10288. // return vecTestViewHeight;
  10289. //}
  10290. int iClassIndex = pClass->GetClassIndex();
  10291. if ( iClassIndex < TF_FIRST_NORMAL_CLASS || iClassIndex > TF_LAST_NORMAL_CLASS )
  10292. return VEC_VIEW_SCALED( this );
  10293. return g_TFClassViewVectors[pClass->GetClassIndex()] * GetModelScale();
  10294. }
  10295. CTFWeaponBase *CTFPlayer::Weapon_OwnsThisID( int iWeaponID ) const
  10296. {
  10297. for (int i = 0;i < WeaponCount(); i++)
  10298. {
  10299. CTFWeaponBase *pWpn = ( CTFWeaponBase *)GetWeapon( i );
  10300. if ( pWpn == NULL )
  10301. continue;
  10302. if ( pWpn->GetWeaponID() == iWeaponID )
  10303. {
  10304. return pWpn;
  10305. }
  10306. }
  10307. return NULL;
  10308. }
  10309. CTFWeaponBase *CTFPlayer::Weapon_GetWeaponByType( int iType )
  10310. {
  10311. for (int i = 0;i < WeaponCount(); i++)
  10312. {
  10313. CTFWeaponBase *pWpn = ( CTFWeaponBase *)GetWeapon( i );
  10314. if ( pWpn == NULL )
  10315. continue;
  10316. int iWeaponRole = pWpn->GetTFWpnData().m_iWeaponType;
  10317. if ( iWeaponRole == iType )
  10318. {
  10319. return pWpn;
  10320. }
  10321. }
  10322. return NULL;
  10323. }
  10324. bool CTFPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
  10325. {
  10326. bool bCanSwitch = BaseClass::Weapon_CanSwitchTo( pWeapon );
  10327. if ( bCanSwitch )
  10328. {
  10329. if ( GetActiveTFWeapon() )
  10330. {
  10331. // There's ammo in the clip while auto firing... no switching away!
  10332. if ( GetActiveTFWeapon()->AutoFiresFullClip() && GetActiveTFWeapon()->Clip1() > 0 )
  10333. return false;
  10334. }
  10335. if ( m_Shared.IsCarryingObject() && (GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER) )
  10336. {
  10337. CTFWeaponBase *pTFWeapon = dynamic_cast<CTFWeaponBase*>( pWeapon );
  10338. if ( pTFWeapon && (pTFWeapon->GetWeaponID() != TF_WEAPON_BUILDER) )
  10339. {
  10340. return false;
  10341. }
  10342. }
  10343. // prevents script exploits, like switching to the minigun while eating a sandvich
  10344. if ( IsTaunting() && tf_allow_taunt_switch.GetInt() == 0 )
  10345. {
  10346. return false;
  10347. }
  10348. int iDisableWeaponSwitch = 0;
  10349. CALL_ATTRIB_HOOK_INT( iDisableWeaponSwitch, disable_weapon_switch );
  10350. if ( iDisableWeaponSwitch != 0 )
  10351. return false;
  10352. }
  10353. return bCanSwitch;
  10354. }
  10355. //-----------------------------------------------------------------------------
  10356. // Purpose: Gives the player an opportunity to abort a double jump.
  10357. //-----------------------------------------------------------------------------
  10358. bool CTFPlayer::CanAirDash( void ) const
  10359. {
  10360. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  10361. return false;
  10362. if ( m_Shared.InCond( TF_COND_HALLOWEEN_SPEED_BOOST ) )
  10363. return true;
  10364. bool bScout = GetPlayerClass()->IsClass( TF_CLASS_SCOUT );
  10365. if ( !bScout )
  10366. return false;
  10367. if ( m_Shared.InCond( TF_COND_SODAPOPPER_HYPE ) )
  10368. {
  10369. if ( m_Shared.GetAirDash() < 5 )
  10370. return true;
  10371. else
  10372. return false;
  10373. }
  10374. int iDashCount = tf_scout_air_dash_count.GetInt();
  10375. CALL_ATTRIB_HOOK_INT( iDashCount, air_dash_count );
  10376. if ( m_Shared.GetAirDash() >= iDashCount )
  10377. return false;
  10378. int iNoAirDash = 0;
  10379. CALL_ATTRIB_HOOK_INT( iNoAirDash, set_scout_doublejump_disabled );
  10380. if ( 1 == iNoAirDash )
  10381. return false;
  10382. return true;
  10383. }
  10384. //-----------------------------------------------------------------------------
  10385. // Purpose: Should be immune to Jarate and Mad Milk?
  10386. //-----------------------------------------------------------------------------
  10387. bool CTFPlayer::CanGetWet( void ) const
  10388. {
  10389. int iWetImmune = 0;
  10390. CALL_ATTRIB_HOOK_INT( iWetImmune, wet_immunity );
  10391. return iWetImmune ? false : true;
  10392. }
  10393. //-----------------------------------------------------------------------------
  10394. // Purpose: Remove disguise
  10395. //-----------------------------------------------------------------------------
  10396. void CTFPlayer::RemoveDisguise( void )
  10397. {
  10398. // remove quickly
  10399. if ( m_Shared.InCond( TF_COND_DISGUISED ) || m_Shared.InCond( TF_COND_DISGUISING ) )
  10400. {
  10401. m_Shared.RemoveDisguise();
  10402. }
  10403. }
  10404. //-----------------------------------------------------------------------------
  10405. // Purpose:
  10406. //-----------------------------------------------------------------------------
  10407. void CTFPlayerShared::RemoveDisguiseWeapon( void )
  10408. {
  10409. #ifdef GAME_DLL
  10410. if ( m_hDisguiseWeapon )
  10411. {
  10412. m_hDisguiseWeapon->Drop( Vector(0,0,0) );
  10413. m_hDisguiseWeapon = NULL;
  10414. }
  10415. #endif
  10416. }
  10417. //-----------------------------------------------------------------------------
  10418. // Purpose:
  10419. //-----------------------------------------------------------------------------
  10420. bool CTFPlayer::CanDisguise( void )
  10421. {
  10422. if ( !IsAlive() )
  10423. return false;
  10424. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_SPY )
  10425. return false;
  10426. bool bHasFlag = false;
  10427. ETFFlagType ignoreTypes[] = { TF_FLAGTYPE_PLAYER_DESTRUCTION };
  10428. if ( HasTheFlag( ignoreTypes, ARRAYSIZE( ignoreTypes ) ) )
  10429. {
  10430. bHasFlag = true;
  10431. }
  10432. if ( bHasFlag || m_Shared.HasPasstimeBall() )
  10433. {
  10434. HintMessage( HINT_CANNOT_DISGUISE_WITH_FLAG );
  10435. return false;
  10436. }
  10437. if ( !Weapon_GetWeaponByType( TF_WPN_TYPE_PDA ) )
  10438. return false;
  10439. int iCannotDisguise = 0;
  10440. CALL_ATTRIB_HOOK_INT( iCannotDisguise, set_cannot_disguise );
  10441. if ( iCannotDisguise == 1 )
  10442. return false;
  10443. return true;
  10444. }
  10445. //-----------------------------------------------------------------------------
  10446. // Purpose: 'Your Eternal Reward' handling for Disguise testing
  10447. //-----------------------------------------------------------------------------
  10448. bool CTFPlayer::CanDisguise_OnKill( void )
  10449. {
  10450. if ( !IsAlive() )
  10451. return false;
  10452. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_SPY )
  10453. return false;
  10454. CTFKnife *pKnife = dynamic_cast<CTFKnife *>( Weapon_GetWeaponByType( TF_WPN_TYPE_MELEE ) );
  10455. if ( pKnife && pKnife->GetKnifeType() != KNIFE_DISGUISE_ONKILL )
  10456. return false;
  10457. return true;
  10458. }
  10459. //-----------------------------------------------------------------------------
  10460. // Purpose:
  10461. //-----------------------------------------------------------------------------
  10462. int CTFPlayer::GetMaxAmmo( int iAmmoIndex, int iClassIndex /*= -1*/ )
  10463. {
  10464. int iMax = ( iClassIndex == -1 ) ? m_PlayerClass.GetData()->m_aAmmoMax[iAmmoIndex] : GetPlayerClassData( iClassIndex )->m_aAmmoMax[iAmmoIndex];
  10465. if ( iAmmoIndex == TF_AMMO_PRIMARY )
  10466. {
  10467. CALL_ATTRIB_HOOK_INT( iMax, mult_maxammo_primary );
  10468. }
  10469. else if ( iAmmoIndex == TF_AMMO_SECONDARY )
  10470. {
  10471. CALL_ATTRIB_HOOK_INT( iMax, mult_maxammo_secondary );
  10472. }
  10473. else if ( iAmmoIndex == TF_AMMO_METAL )
  10474. {
  10475. CALL_ATTRIB_HOOK_INT( iMax, mult_maxammo_metal );
  10476. }
  10477. else if ( iAmmoIndex == TF_AMMO_GRENADES1 )
  10478. {
  10479. CALL_ATTRIB_HOOK_INT( iMax, mult_maxammo_grenades1 );
  10480. }
  10481. else if ( iAmmoIndex == TF_AMMO_GRENADES3 )
  10482. {
  10483. // All classes by default can carry a max of 1 "Grenade3" which is being used as ACTIONSLOT Throwables
  10484. iMax = 1;
  10485. }
  10486. // Haste Powerup Rune adds multiplier to Max Ammo
  10487. if ( m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  10488. {
  10489. iMax *= 2.0f;
  10490. }
  10491. return iMax;
  10492. }
  10493. bool CTFPlayer::IsMiniBoss( void ) const
  10494. {
  10495. return m_bIsMiniBoss;
  10496. }
  10497. //-----------------------------------------------------------------------------
  10498. // Purpose:
  10499. //-----------------------------------------------------------------------------
  10500. CBaseEntity *CTFPlayer::MedicGetHealTarget( void )
  10501. {
  10502. if ( IsPlayerClass(TF_CLASS_MEDIC) )
  10503. {
  10504. CWeaponMedigun *pWeapon = dynamic_cast <CWeaponMedigun*>( GetActiveWeapon() );
  10505. if ( pWeapon )
  10506. return pWeapon->GetHealTarget();
  10507. }
  10508. return NULL;
  10509. }
  10510. //-----------------------------------------------------------------------------
  10511. // Purpose:
  10512. //-----------------------------------------------------------------------------
  10513. float CTFPlayer::MedicGetChargeLevel( CTFWeaponBase **pRetMedigun )
  10514. {
  10515. if ( IsPlayerClass(TF_CLASS_MEDIC) )
  10516. {
  10517. CTFWeaponBase *pWpn = ( CTFWeaponBase *)Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  10518. if ( pWpn == NULL )
  10519. return 0;
  10520. CWeaponMedigun *pMedigun = dynamic_cast <CWeaponMedigun*>( pWpn );
  10521. if ( pRetMedigun )
  10522. {
  10523. *pRetMedigun = pMedigun;
  10524. }
  10525. if ( pMedigun )
  10526. return pMedigun->GetChargeLevel();
  10527. }
  10528. return 0;
  10529. }
  10530. //-----------------------------------------------------------------------------
  10531. // Purpose:
  10532. //-----------------------------------------------------------------------------
  10533. int CTFPlayer::GetNumActivePipebombs( void )
  10534. {
  10535. if ( IsPlayerClass( TF_CLASS_DEMOMAN ) )
  10536. {
  10537. CTFPipebombLauncher *pWeapon = dynamic_cast < CTFPipebombLauncher*>( Weapon_OwnsThisID( TF_WEAPON_PIPEBOMBLAUNCHER ) );
  10538. if ( pWeapon )
  10539. {
  10540. return pWeapon->GetPipeBombCount();
  10541. }
  10542. }
  10543. return 0;
  10544. }
  10545. //-----------------------------------------------------------------------------
  10546. // Purpose: Fills out the vector with the sets that are currently active on this player
  10547. //-----------------------------------------------------------------------------
  10548. void CTFPlayer::GetActiveSets( CUtlVector<const CEconItemSetDefinition *> *pItemSets )
  10549. {
  10550. pItemSets->Purge();
  10551. CSteamID steamIDForPlayer;
  10552. GetSteamID( &steamIDForPlayer );
  10553. TFInventoryManager()->GetActiveSets( pItemSets, steamIDForPlayer, GetPlayerClass()->GetClassIndex() );
  10554. }
  10555. //-----------------------------------------------------------------------------
  10556. // Purpose:
  10557. //-----------------------------------------------------------------------------
  10558. bool CTFPlayer::CanMoveDuringTaunt()
  10559. {
  10560. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  10561. {
  10562. if ( ( TFGameRules()->GetRoundRestartTime() > -1.f ) && ( (int)( TFGameRules()->GetRoundRestartTime() - gpGlobals->curtime ) <= mp_tournament_readymode_countdown.GetInt() ) )
  10563. return false;
  10564. if ( TFGameRules()->PlayersAreOnMatchSummaryStage() )
  10565. return false;
  10566. }
  10567. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  10568. return true;
  10569. if ( m_Shared.InCond( TF_COND_TAUNTING ) || m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
  10570. {
  10571. #ifdef GAME_DLL
  10572. if ( tf_allow_sliding_taunt.GetBool() )
  10573. {
  10574. return true;
  10575. }
  10576. #endif // GAME_DLL
  10577. #ifdef STAGING_ONLY
  10578. if ( tf_force_allow_move_during_taunt.GetBool() )
  10579. {
  10580. return true;
  10581. }
  10582. #endif // STAGING_ONLY
  10583. if ( m_bAllowMoveDuringTaunt )
  10584. {
  10585. return true;
  10586. }
  10587. if ( IsReadyToTauntWithPartner() || CTFPlayerSharedUtils::ConceptIsPartnerTaunt( m_Shared.m_iTauntConcept ) )
  10588. {
  10589. return false;
  10590. }
  10591. }
  10592. return false;
  10593. }
  10594. //-----------------------------------------------------------------------------
  10595. // Purpose:
  10596. //-----------------------------------------------------------------------------
  10597. bool CTFPlayer::ShouldStopTaunting()
  10598. {
  10599. // stop taunt if we're under water
  10600. return GetWaterLevel() > WL_Waist;
  10601. }
  10602. //-----------------------------------------------------------------------------
  10603. // Purpose:
  10604. //-----------------------------------------------------------------------------
  10605. #if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
  10606. static ConVar tf_tauntcam_dist_override( "tf_tauntcam_dist_override", "0", FCVAR_CHEAT );
  10607. static ConVar tf_tauntcam_distup_override( "tf_tauntcam_distup_override", "0", FCVAR_CHEAT );
  10608. #endif // CLIENT_DLL && STAGING_ONLY
  10609. void CTFPlayer::ParseSharedTauntDataFromEconItemView( CEconItemView *pEconItemView )
  10610. {
  10611. static CSchemaAttributeDefHandle pAttrDef_TauntForceMoveForward( "taunt force move forward" );
  10612. attrib_value_t attrTauntForceMoveForward = 0;
  10613. pEconItemView->FindAttribute( pAttrDef_TauntForceMoveForward, &attrTauntForceMoveForward );
  10614. m_bTauntForceMoveForward = attrTauntForceMoveForward != 0;
  10615. static CSchemaAttributeDefHandle pAttrDef_TauntMoveSpeed( "taunt move speed" );
  10616. attrib_value_t attrTauntMoveSpeed = 0;
  10617. pEconItemView->FindAttribute( pAttrDef_TauntMoveSpeed, &attrTauntMoveSpeed );
  10618. m_flTauntForceMoveForwardSpeed = (float&)attrTauntMoveSpeed;
  10619. static CSchemaAttributeDefHandle pAttrDef_TauntMoveAccelerationTime( "taunt move acceleration time" );
  10620. attrib_value_t attrTauntMoveAccelerationTime = 0;
  10621. pEconItemView->FindAttribute( pAttrDef_TauntMoveAccelerationTime, &attrTauntMoveAccelerationTime );
  10622. m_flTauntMoveAccelerationTime = (float&)attrTauntMoveAccelerationTime;
  10623. static CSchemaAttributeDefHandle pAttrDef_TauntTurnSpeed( "taunt turn speed" );
  10624. attrib_value_t attrTauntTurnSpeed = 0;
  10625. pEconItemView->FindAttribute( pAttrDef_TauntTurnSpeed, &attrTauntTurnSpeed );
  10626. m_flTauntTurnSpeed = (float&)attrTauntTurnSpeed;
  10627. static CSchemaAttributeDefHandle pAttrDef_TauntTurnAccelerationTime( "taunt turn acceleration time" );
  10628. attrib_value_t attrTauntTurnAccelerationTime = 0;
  10629. pEconItemView->FindAttribute( pAttrDef_TauntTurnAccelerationTime, &attrTauntTurnAccelerationTime );
  10630. m_flTauntTurnAccelerationTime = (float&)attrTauntTurnAccelerationTime;
  10631. #ifdef CLIENT_DLL
  10632. CTFTauntInfo *pTauntInfo = pEconItemView->GetStaticData()->GetTauntData();
  10633. if ( pTauntInfo )
  10634. {
  10635. if ( pTauntInfo->GetCameraDist() != 0 )
  10636. m_flTauntCamTargetDist = pTauntInfo->GetCameraDist();
  10637. if ( pTauntInfo->GetCameraDistUp() != 0 )
  10638. m_flTauntCamTargetDistUp = pTauntInfo->GetCameraDistUp();
  10639. #ifdef STAGING_ONLY
  10640. if ( tf_tauntcam_dist_override.GetFloat() != 0 )
  10641. m_flTauntCamTargetDist = tf_tauntcam_dist_override.GetFloat();
  10642. if ( tf_tauntcam_distup_override.GetFloat() != 0 )
  10643. m_flTauntCamTargetDistUp = tf_tauntcam_distup_override.GetFloat();
  10644. #endif // STAGING_ONLY
  10645. }
  10646. #endif // CLIENT_DLL
  10647. }
  10648. //-----------------------------------------------------------------------------
  10649. // Purpose:
  10650. //-----------------------------------------------------------------------------
  10651. void CTFPlayer::SetTauntYaw( float flTauntYaw )
  10652. {
  10653. m_flPrevTauntYaw = m_flTauntYaw;
  10654. m_flTauntYaw = flTauntYaw;
  10655. QAngle angle = GetLocalAngles();
  10656. angle.y = flTauntYaw;
  10657. SetLocalAngles( angle );
  10658. }
  10659. //-----------------------------------------------------------------------------
  10660. // Purpose:
  10661. //-----------------------------------------------------------------------------
  10662. void CTFPlayer::StartBuildingObjectOfType( int iType, int iMode )
  10663. {
  10664. // early out if we can't build this type of object
  10665. if ( CanBuild( iType, iMode ) != CB_CAN_BUILD )
  10666. return;
  10667. // Does this type require a specific builder?
  10668. CTFWeaponBuilder *pBuilder = CTFPlayerSharedUtils::GetBuilderForObjectType( this, iType );
  10669. if ( pBuilder )
  10670. {
  10671. #ifdef GAME_DLL
  10672. pBuilder->SetSubType( iType );
  10673. pBuilder->SetObjectMode( iMode );
  10674. if ( GetActiveTFWeapon() == pBuilder )
  10675. {
  10676. SetActiveWeapon( NULL );
  10677. }
  10678. #endif
  10679. // try to switch to this weapon
  10680. Weapon_Switch( pBuilder );
  10681. }
  10682. }
  10683. //-----------------------------------------------------------------------------
  10684. // Purpose:
  10685. //-----------------------------------------------------------------------------
  10686. void CTFPlayerShared::UpdatePhaseEffects( void )
  10687. {
  10688. bool bRunning;
  10689. float flSpeed = m_pOuter->MaxSpeed();
  10690. flSpeed *= flSpeed;
  10691. CTFPlayer *pPlayer = ToTFPlayer( m_pOuter );
  10692. if ( InCond( TF_COND_SHIELD_CHARGE ) || (pPlayer->GetAbsVelocity().LengthSqr() >= (flSpeed* 0.1f)) )
  10693. {
  10694. bRunning = true;
  10695. }
  10696. else
  10697. {
  10698. bRunning = false;
  10699. }
  10700. #ifdef CLIENT_DLL
  10701. if ( m_pOuter )
  10702. {
  10703. if ( !bRunning && !m_pOuter->m_pPhaseStandingEffect && m_flEnergyDrinkMeter < 100.0f )
  10704. {
  10705. m_pOuter->m_pPhaseStandingEffect = m_pOuter->ParticleProp()->Create( "warp_version", PATTACH_ABSORIGIN_FOLLOW );
  10706. }
  10707. else if ( bRunning && m_pOuter->m_pPhaseStandingEffect )
  10708. {
  10709. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pPhaseStandingEffect );
  10710. m_pOuter->m_pPhaseStandingEffect = NULL;
  10711. }
  10712. }
  10713. #else
  10714. // #ifdef STAGING_ONLY
  10715. // if ( !InCond( TF_COND_PHASE ) && !InCond( TF_COND_SHIELD_CHARGE ) && !InCond( TF_COND_SELF_CONC ) )
  10716. // #else
  10717. if ( !InCond( TF_COND_PHASE ) && !InCond( TF_COND_SHIELD_CHARGE ) )
  10718. //#endif // STAGING_ONLY
  10719. return;
  10720. if ( bRunning )
  10721. {
  10722. if ( !m_bPhaseFXOn )
  10723. {
  10724. AddPhaseEffects();
  10725. }
  10726. else
  10727. {
  10728. if ( m_flEnergyDrinkMeter <= 10.0f )
  10729. {
  10730. float fAlpha = ( m_flEnergyDrinkMeter / 10 ) * 255;
  10731. for ( int i = 0; i < TF_SCOUT_NUMBEROFPHASEATTACHMENTS; ++i )
  10732. {
  10733. CSpriteTrail *pTempTrail = dynamic_cast< CSpriteTrail*>( m_pPhaseTrail[i].Get() );
  10734. if ( pTempTrail )
  10735. {
  10736. pTempTrail->SetBrightness( int(fAlpha) );
  10737. }
  10738. }
  10739. }
  10740. // #ifdef STAGING_ONLY
  10741. // else if ( InCond( TF_COND_SHIELD_CHARGE ) || InCond( TF_COND_SELF_CONC ) )
  10742. // #else
  10743. else if ( InCond( TF_COND_SHIELD_CHARGE ) )
  10744. //#endif // STAGING_ONLY
  10745. {
  10746. for ( int i = 0; i < TF_SCOUT_NUMBEROFPHASEATTACHMENTS; ++i )
  10747. {
  10748. CSpriteTrail *pTempTrail = dynamic_cast< CSpriteTrail*>( m_pPhaseTrail[i].Get() );
  10749. if ( pTempTrail )
  10750. {
  10751. pTempTrail->SetBrightness( 0 );
  10752. }
  10753. }
  10754. }
  10755. }
  10756. }
  10757. #endif
  10758. }
  10759. //-----------------------------------------------------------------------------
  10760. // Purpose:
  10761. //-----------------------------------------------------------------------------
  10762. void CTFPlayerShared::AddPhaseEffects( void )
  10763. {
  10764. #ifdef CLIENT_DLL
  10765. #else
  10766. const char *pTrailTeamName = ( m_pOuter->GetTeamNumber() == TF_TEAM_RED ) ? "effects/beam001_red.vmt" : "effects/beam001_blu.vmt";
  10767. CSpriteTrail *pTempTrail = NULL;
  10768. pTempTrail = CSpriteTrail::SpriteTrailCreate( pTrailTeamName, m_pOuter->GetAbsOrigin(), true );
  10769. pTempTrail->FollowEntity( m_pOuter );
  10770. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNone );
  10771. pTempTrail->SetStartWidth( 12 );
  10772. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  10773. pTempTrail->SetLifeTime( 1 );
  10774. pTempTrail->TurnOn();
  10775. pTempTrail->SetAttachment( m_pOuter, m_pOuter->LookupAttachment( "back_upper" ) );
  10776. m_pPhaseTrail[0] = pTempTrail;
  10777. pTempTrail = CSpriteTrail::SpriteTrailCreate( pTrailTeamName, m_pOuter->GetAbsOrigin(), true );
  10778. pTempTrail->FollowEntity( m_pOuter );
  10779. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNone );
  10780. pTempTrail->SetStartWidth( 16 );
  10781. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  10782. pTempTrail->SetLifeTime( 1 );
  10783. pTempTrail->TurnOn();
  10784. pTempTrail->SetAttachment( m_pOuter, m_pOuter->LookupAttachment( "back_lower" ) );
  10785. m_pPhaseTrail[1] = pTempTrail;
  10786. pTempTrail = CSpriteTrail::SpriteTrailCreate( "effects/beam001_white.vmt", m_pOuter->GetAbsOrigin(), true );
  10787. pTempTrail->FollowEntity( m_pOuter );
  10788. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNone );
  10789. pTempTrail->SetStartWidth( 8 );
  10790. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  10791. pTempTrail->SetLifeTime( 0.5f );
  10792. pTempTrail->TurnOn();
  10793. pTempTrail->SetAttachment( m_pOuter, m_pOuter->LookupAttachment( "foot_R" ) );
  10794. m_pPhaseTrail[2] = pTempTrail;
  10795. pTempTrail = CSpriteTrail::SpriteTrailCreate( "effects/beam001_white.vmt", m_pOuter->GetAbsOrigin(), true );
  10796. pTempTrail->FollowEntity( m_pOuter );
  10797. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNone );
  10798. pTempTrail->SetStartWidth( 8 );
  10799. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  10800. pTempTrail->SetLifeTime( 0.5f );
  10801. pTempTrail->TurnOn();
  10802. pTempTrail->SetAttachment( m_pOuter, m_pOuter->LookupAttachment( "foot_L" ) );
  10803. m_pPhaseTrail[3] = pTempTrail;
  10804. pTempTrail = CSpriteTrail::SpriteTrailCreate( pTrailTeamName, m_pOuter->GetAbsOrigin(), true );
  10805. pTempTrail->FollowEntity( m_pOuter );
  10806. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNone );
  10807. pTempTrail->SetStartWidth( 8 );
  10808. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  10809. pTempTrail->SetLifeTime( 0.5f );
  10810. pTempTrail->TurnOn();
  10811. pTempTrail->SetAttachment( m_pOuter, m_pOuter->LookupAttachment( "hand_L" ) );
  10812. m_pPhaseTrail[4] = pTempTrail;
  10813. m_bPhaseFXOn = true;
  10814. #endif
  10815. }
  10816. //-----------------------------------------------------------------------------
  10817. // Purpose:
  10818. //-----------------------------------------------------------------------------
  10819. void CTFPlayerShared::RemovePhaseEffects( void )
  10820. {
  10821. #ifdef CLIENT_DLL
  10822. if ( m_pOuter->m_pPhaseStandingEffect )
  10823. {
  10824. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pPhaseStandingEffect );
  10825. m_pOuter->m_pPhaseStandingEffect = NULL;
  10826. }
  10827. #else
  10828. for ( int i = 0; i < TF_SCOUT_NUMBEROFPHASEATTACHMENTS; ++i )
  10829. {
  10830. UTIL_Remove(m_pPhaseTrail[i]);
  10831. }
  10832. m_bPhaseFXOn = false;
  10833. #endif
  10834. }
  10835. //-----------------------------------------------------------------------------
  10836. // Purpose:
  10837. //-----------------------------------------------------------------------------
  10838. int CTFPlayerShared::GetSequenceForDeath( CBaseAnimating* pRagdoll, bool bBurning, int nCustomDeath )
  10839. {
  10840. if ( !pRagdoll )
  10841. return -1;
  10842. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  10843. {
  10844. if ( m_pOuter && ( m_pOuter->GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  10845. return -1;
  10846. }
  10847. int iDeathSeq = -1;
  10848. // if ( bBurning )
  10849. // {
  10850. // iDeathSeq = pRagdoll->LookupSequence( "primary_death_burning" );
  10851. // }
  10852. switch ( nCustomDeath )
  10853. {
  10854. case TF_DMG_CUSTOM_HEADSHOT_DECAPITATION:
  10855. case TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING:
  10856. case TF_DMG_CUSTOM_DECAPITATION:
  10857. case TF_DMG_CUSTOM_HEADSHOT:
  10858. iDeathSeq = pRagdoll->LookupSequence( "primary_death_headshot" );
  10859. break;
  10860. case TF_DMG_CUSTOM_BACKSTAB:
  10861. iDeathSeq = pRagdoll->LookupSequence( "primary_death_backstab" );
  10862. break;
  10863. }
  10864. return iDeathSeq;
  10865. }
  10866. extern ConVar tf_halloween_kart_dash_speed;
  10867. ConVar tf_halloween_kart_slow_turn_accel_speed( "tf_halloween_kart_slow_turn_accel_speed", "200", FCVAR_CHEAT | FCVAR_REPLICATED );
  10868. ConVar tf_halloween_kart_fast_turn_accel_speed( "tf_halloween_kart_fast_turn_accel_speed", "400", FCVAR_CHEAT | FCVAR_REPLICATED );
  10869. ConVar tf_halloween_kart_return_turn_accell( "tf_halloween_kart_return_turn_accell", "200", FCVAR_CHEAT | FCVAR_REPLICATED );
  10870. ConVar tf_halloween_kart_slow_turn_speed( "tf_halloween_kart_slow_turn_speed", "100", FCVAR_CHEAT | FCVAR_REPLICATED );
  10871. ConVar tf_halloween_kart_fast_turn_speed( "tf_halloween_kart_fast_turn_speed", "60", FCVAR_CHEAT | FCVAR_REPLICATED );
  10872. ConVar tf_halloween_kart_turning_curve_peak_position( "tf_halloween_kart_turning_curve_peak_position", "0.5", FCVAR_CHEAT | FCVAR_REPLICATED );
  10873. ConVar tf_halloween_kart_stationary_turn_speed( "tf_halloween_kart_stationary_turn_speed", "50", FCVAR_CHEAT | FCVAR_REPLICATED );
  10874. ConVar tf_halloween_kart_reverse_turn_speed( "tf_halloween_kart_reverse_turn_speed", "50", FCVAR_CHEAT | FCVAR_REPLICATED );
  10875. ConVar tf_halloween_kart_air_turn_scale( "tf_halloween_kart_air_turn_scale", "1.2f", FCVAR_CHEAT | FCVAR_REPLICATED );
  10876. #ifdef CLIENT_DLL
  10877. ConVar tf_halloween_kart_pitch( "tf_halloween_kart_pitch", "10", FCVAR_ARCHIVE );
  10878. ConVar tf_halloween_kart_pitch_slow_follow_rate( "tf_halloween_kart_pitch_slow_follow_rate", "0.5", FCVAR_ARCHIVE );
  10879. ConVar tf_halloween_kart_pitch_fast_follow_rate( "tf_halloween_kart_pitch_fast_follow_rate", "2", FCVAR_ARCHIVE );
  10880. #endif // CLIENT_DLL
  10881. void CTFPlayerShared::CreateVehicleMove( float flInputSampleTime, CUserCmd *pCmd )
  10882. {
  10883. const float flSign = pCmd->sidemove == 0.f ? 0.f : Sign( pCmd->sidemove );
  10884. // Compute target turn speed
  10885. const float flVel = m_pOuter->GetAbsVelocity().Length2D();
  10886. const float flNormalizedSpeed = Clamp( flVel / tf_halloween_kart_dash_speed.GetFloat(), 0.0f, 1.0f );
  10887. float flTargetTurnSpeed;
  10888. if ( flNormalizedSpeed == 0.f )
  10889. {
  10890. flTargetTurnSpeed = flSign * tf_halloween_kart_stationary_turn_speed.GetFloat();
  10891. }
  10892. else if ( Sign( m_pOuter->GetCurrentTauntMoveSpeed() ) < 0 )
  10893. {
  10894. flTargetTurnSpeed = Sign( m_pOuter->GetCurrentTauntMoveSpeed() ) * flSign * tf_halloween_kart_reverse_turn_speed.GetFloat();
  10895. }
  10896. else
  10897. {
  10898. const float flSmoothCurveVal = SmoothCurve_Tweak( flNormalizedSpeed, tf_halloween_kart_turning_curve_peak_position.GetFloat() );
  10899. flTargetTurnSpeed = Sign( m_pOuter->GetCurrentTauntMoveSpeed() ) * flSign * RemapValClamped( flSmoothCurveVal, 0.f, 1.f, tf_halloween_kart_slow_turn_speed.GetFloat(), tf_halloween_kart_fast_turn_speed.GetFloat() );
  10900. }
  10901. float flTurnAccel = 0.f;
  10902. // Compute turn accelleration
  10903. if ( flSign == Sign( m_flCurrentTauntTurnSpeed ) )
  10904. {
  10905. flTurnAccel = RemapValClamped( flNormalizedSpeed, 0.f, 1.f, tf_halloween_kart_slow_turn_accel_speed.GetFloat(), tf_halloween_kart_fast_turn_accel_speed.GetFloat() );
  10906. }
  10907. else
  10908. { // When not trying to turn, or turning the opposite way you're already turning
  10909. // accelerate much faster
  10910. flTurnAccel = tf_halloween_kart_return_turn_accell.GetFloat();
  10911. }
  10912. // Turn faster in the air
  10913. if ( !(m_pOuter->GetFlags() & FL_ONGROUND) )
  10914. {
  10915. flTurnAccel *= tf_halloween_kart_air_turn_scale.GetFloat();
  10916. }
  10917. // Get actual turn speed
  10918. m_flCurrentTauntTurnSpeed = Approach( flTargetTurnSpeed, m_flCurrentTauntTurnSpeed, flTurnAccel * flInputSampleTime );
  10919. const float flMaxPossibleTurnSpeed = Max( tf_halloween_kart_slow_turn_speed.GetFloat(), tf_halloween_kart_fast_turn_speed.GetFloat() );
  10920. m_flCurrentTauntTurnSpeed = clamp( m_flCurrentTauntTurnSpeed, -flMaxPossibleTurnSpeed, flMaxPossibleTurnSpeed );
  10921. #ifdef DEBUG
  10922. #ifdef CLIENT_DLL
  10923. engine->Con_NPrintf( 4, "Turn: %3.2f", m_flCurrentTauntTurnSpeed );
  10924. engine->Con_NPrintf( 5, "TargetTurn: %3.2f", flTargetTurnSpeed );
  10925. engine->Con_NPrintf( 6, "TurnAccell: %3.2f", flTurnAccel );
  10926. #else
  10927. engine->Con_NPrintf( 4+3, "Turn: %3.2f", m_flCurrentTauntTurnSpeed );
  10928. engine->Con_NPrintf( 5+3, "TargetTurn: %3.2f", flTargetTurnSpeed );
  10929. engine->Con_NPrintf( 6+3, "TurnAccell: %3.2f", flTurnAccel );
  10930. #endif
  10931. #endif
  10932. #ifdef CLIENT_DLL
  10933. // Turn!
  10934. m_angVehicleMoveAngles -= QAngle( 0.f, m_flCurrentTauntTurnSpeed * flInputSampleTime, 0.f );
  10935. // We want our pitch to slowly catch up to the pitch of the player's model
  10936. const float flTargetPitch = tf_halloween_kart_pitch.GetFloat() + m_pOuter->m_PlayerAnimState->GetRenderAngles()[PITCH];
  10937. const float flStepSpeed = fabs( flTargetPitch - tf_halloween_kart_pitch.GetFloat() ) < fabs( m_angVehicleMovePitchLast - tf_halloween_kart_pitch.GetFloat() ) ? tf_halloween_kart_pitch_fast_follow_rate.GetFloat() : tf_halloween_kart_pitch_slow_follow_rate.GetFloat();
  10938. const float flPitchDiff = fabs( flTargetPitch - m_angVehicleMovePitchLast );
  10939. const float flPitchStep = flPitchDiff * flStepSpeed;
  10940. m_angVehicleMovePitchLast = Approach( flTargetPitch, m_angVehicleMovePitchLast, flPitchStep * gpGlobals->frametime );
  10941. m_angVehicleMoveAngles[PITCH] = m_angVehicleMovePitchLast;
  10942. pCmd->weaponselect = 0;
  10943. pCmd->buttons &= IN_MOVELEFT | IN_MOVERIGHT | IN_ATTACK | IN_ATTACK2 | IN_FORWARD | IN_BACK | IN_JUMP;
  10944. VectorCopy( m_angVehicleMoveAngles, pCmd->viewangles );
  10945. m_pOuter->SetLocalAngles( m_angVehicleMoveAngles );
  10946. // Fill out our kart state for the local client
  10947. m_pOuter->m_iKartState = 0;
  10948. // Hitting the gas
  10949. if ( pCmd->buttons & IN_FORWARD )
  10950. {
  10951. m_pOuter->m_iKartState |= CTFPlayerShared::kKartState_Driving;
  10952. }
  10953. else if ( pCmd->buttons & IN_BACK ) // Hitting the brakes
  10954. {
  10955. // slowing down
  10956. if ( m_pOuter->GetCurrentTauntMoveSpeed() > 0 )
  10957. {
  10958. m_pOuter->m_iKartState |= CTFPlayerShared::kKartState_Braking;
  10959. }
  10960. // if we are already stopped, look for new input to start going backwards
  10961. else
  10962. {
  10963. // check for new input, else do nothing
  10964. if ( ( pCmd->buttons & IN_BACK )
  10965. || m_pOuter->GetCurrentTauntMoveSpeed() < 0
  10966. || m_pOuter->GetVehicleReverseTime() < gpGlobals->curtime
  10967. )
  10968. {
  10969. m_pOuter->m_iKartState |= CTFPlayerShared::kKartState_Reversing;
  10970. }
  10971. else
  10972. {
  10973. m_pOuter->m_iKartState |= CTFPlayerShared::kKartState_Stopped;
  10974. }
  10975. }
  10976. }
  10977. #endif
  10978. }
  10979. void CTFPlayerShared::VehicleThink( void )
  10980. {
  10981. #ifdef CLIENT_DLL
  10982. m_pOuter->UpdateKartSounds();
  10983. // Ordered list of effects. Lower on the list has higher prescedence
  10984. static WheelEffect_t wheelEffects[] =
  10985. {
  10986. WheelEffect_t( 20.f, "kart_dust_trail_red", "kart_dust_trail_blue" )
  10987. };
  10988. const float flCurrentSpeed = m_pOuter->GetCurrentTauntMoveSpeed();
  10989. const WheelEffect_t* pDesiredEffect = NULL;
  10990. if ( InCond( TF_COND_HALLOWEEN_KART ) )
  10991. {
  10992. // Go through the effects, and figure out which effect to use
  10993. for( int i=0; i < ARRAYSIZE(wheelEffects); ++ i )
  10994. {
  10995. const WheelEffect_t& effect = wheelEffects[ i ];
  10996. if ( effect.m_flMinTriggerSpeed <= flCurrentSpeed )
  10997. {
  10998. pDesiredEffect = &effect;
  10999. }
  11000. }
  11001. }
  11002. // Start/stop effects if the desired effect is different
  11003. if ( pDesiredEffect != m_pWheelEffect )
  11004. {
  11005. C_BaseAnimating * pKart = m_pOuter->GetKart();
  11006. if ( !pKart )
  11007. return;
  11008. m_pWheelEffect = pDesiredEffect;
  11009. // New effect
  11010. if ( pDesiredEffect )
  11011. {
  11012. const char *pszEffectName = pDesiredEffect->m_pszParticleName[ m_pOuter->GetTeamNumber() ];
  11013. m_pOuter->CreateKartEffect( pszEffectName );
  11014. }
  11015. else // Turn off current effect
  11016. {
  11017. m_pOuter->StopKartEffect();
  11018. }
  11019. }
  11020. #endif
  11021. }
  11022. //-----------------------------------------------------------------------------
  11023. float CTFPlayer::GetKartSpeedBoost( void )
  11024. {
  11025. if ( m_flKartNextAvailableBoost < gpGlobals->curtime )
  11026. return 1.0f;
  11027. if ( m_flKartNextAvailableBoost > gpGlobals->curtime + tf_halloween_kart_boost_recharge.GetFloat() )
  11028. return 0.0f;
  11029. // Calculate time
  11030. return RemapValClamped( gpGlobals->curtime, m_flKartNextAvailableBoost - tf_halloween_kart_boost_recharge.GetFloat(), m_flKartNextAvailableBoost, 0.0f, 1.0f );
  11031. }
  11032. //-----------------------------------------------------------------------------
  11033. // Purpose:
  11034. //-----------------------------------------------------------------------------
  11035. bool CTFPlayerShared::IsLoser( void )
  11036. {
  11037. if ( tf_always_loser.GetBool() )
  11038. return true;
  11039. if ( !TFGameRules() )
  11040. return false;
  11041. // No loser mode in competitive
  11042. if ( TFGameRules()->IsMatchTypeCompetitive() )
  11043. return false;
  11044. if ( TFGameRules()->State_Get() != GR_STATE_TEAM_WIN )
  11045. {
  11046. if ( IsLoserStateStunned() )
  11047. return true;
  11048. else
  11049. return false;
  11050. }
  11051. bool bLoser = TFGameRules()->GetWinningTeam() != m_pOuter->GetTeamNumber();
  11052. int iClass = m_pOuter->GetPlayerClass()->GetClassIndex();
  11053. // don't reveal disguised spies
  11054. if ( bLoser && iClass == TF_CLASS_SPY )
  11055. {
  11056. if ( InCond( TF_COND_DISGUISED ) && GetDisguiseTeam() == TFGameRules()->GetWinningTeam() )
  11057. {
  11058. bLoser = false;
  11059. }
  11060. }
  11061. return bLoser;
  11062. }
  11063. //-----------------------------------------------------------------------------
  11064. // Purpose:
  11065. //-----------------------------------------------------------------------------
  11066. void CTFPlayerShared::RecalculatePlayerBodygroups( void )
  11067. {
  11068. // We have to clear the m_nBody bitfield.
  11069. // Leaving bits on from previous player classes can have weird effects
  11070. // like if we switch to a class that uses those bits for other things.
  11071. m_pOuter->m_nBody = 0;
  11072. // Update our weapon bodygroups that change state purely based on whether they're
  11073. // equipped or not.
  11074. CTFWeaponBase::UpdateWeaponBodyGroups( m_pOuter, false );
  11075. // Update our wearable bodygroups.
  11076. CEconWearable::UpdateWearableBodyGroups( m_pOuter );
  11077. // Update our weapon bodygroups for weapons that only change state when active.
  11078. CTFWeaponBase::UpdateWeaponBodyGroups( m_pOuter, true );
  11079. }
  11080. #ifdef GAME_DLL
  11081. //-----------------------------------------------------------------------------
  11082. // Purpose:
  11083. //-----------------------------------------------------------------------------
  11084. void CTFPlayerShared::GetSpeedWatchersList( CUtlVector<CTFPlayer *> *out_pVecSpeedWatchers ) const
  11085. {
  11086. Assert( out_pVecSpeedWatchers != NULL );
  11087. // Are any medics healing us with the Quick-Fix?
  11088. FOR_EACH_VEC( m_aHealers, i )
  11089. {
  11090. CTFPlayer *pTFHealer = ToTFPlayer( m_aHealers[i].pHealer );
  11091. if ( !pTFHealer )
  11092. continue;
  11093. if ( !pTFHealer->GetActiveTFWeapon() || pTFHealer->GetActiveTFWeapon()->GetWeaponID() != TF_WEAPON_MEDIGUN )
  11094. continue;
  11095. // QuickFix medics heal themselves when deploying an Uber
  11096. if ( m_aHealers[i].pHealer == m_pOuter )
  11097. continue;
  11098. out_pVecSpeedWatchers->AddToTail( pTFHealer );
  11099. }
  11100. }
  11101. #endif // GAME_DLL
  11102. //-----------------------------------------------------------------------------
  11103. // Purpose:
  11104. //-----------------------------------------------------------------------------
  11105. void CTFPlayerShared::SetupRageBuffTimer( int iBuffType, int iPulseCount, ERageBuffSlot eBuffSlot )
  11106. {
  11107. m_RageBuffSlots[eBuffSlot].m_iBuffTypeActive = iBuffType;
  11108. m_RageBuffSlots[eBuffSlot].m_iBuffPulseCount = iPulseCount;
  11109. m_RageBuffSlots[eBuffSlot].m_flNextBuffPulseTime = gpGlobals->curtime + 1.0f;
  11110. PulseRageBuff( eBuffSlot );
  11111. }
  11112. //-----------------------------------------------------------------------------
  11113. // Purpose:
  11114. //-----------------------------------------------------------------------------
  11115. void CTFPlayerShared::ActivateRageBuff( CBaseEntity *pBuffItem, int iBuffType )
  11116. {
  11117. // Sniper Focus can be activated at all times
  11118. if ( GetRageMeter() < 100.f && iBuffType != 6 )
  11119. return;
  11120. Assert( iBuffType > 0 && iBuffType < ARRAYSIZE( g_RageBuffTypes ) ); // 0 is valid in the array, but an invalid buff
  11121. if ( iBuffType < 0 || iBuffType >= ARRAYSIZE( g_RageBuffTypes ) )
  11122. {
  11123. DevMsg( "Invalid rage buff type %i for entindex %i\n", iBuffType, m_pOuter->entindex() );
  11124. ResetRageSystem();
  11125. return;
  11126. }
  11127. int nBuffPulses = g_RageBuffTypes[iBuffType].m_nMaxPulses;
  11128. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, nBuffPulses, mod_buff_duration );
  11129. #ifdef GAME_DLL
  11130. switch ( iBuffType )
  11131. {
  11132. case 1:
  11133. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
  11134. break;
  11135. case 2:
  11136. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_INCOMING );
  11137. break;
  11138. case 3:
  11139. // FIXME: new sound file for samurai buff?
  11140. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
  11141. case 5:
  11142. // Pyro Rage
  11143. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
  11144. break;
  11145. case 6 :
  11146. // Sniper Focus
  11147. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
  11148. nBuffPulses *= (m_flRageMeter / 100);
  11149. break;
  11150. }
  11151. #endif
  11152. m_bRageDraining = true;
  11153. SetupRageBuffTimer( iBuffType, nBuffPulses, kBuffSlot_Rage );
  11154. }
  11155. //-----------------------------------------------------------------------------
  11156. // Purpose:
  11157. //-----------------------------------------------------------------------------
  11158. void CTFPlayerShared::UpdateRageBuffsAndRage( void )
  11159. {
  11160. // We allow this for all classes to allow item creators and plugin authors to give rage to any class.
  11161. // If we're dead, reset both our rage and our active buffs.
  11162. if ( !m_pOuter->IsAlive() )
  11163. {
  11164. ResetRageSystem();
  11165. return;
  11166. }
  11167. // Find out whether we've run out of rage.
  11168. if ( m_bRageDraining )
  11169. {
  11170. int nBuffType = 0;
  11171. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, nBuffType, set_buff_type );
  11172. Assert( nBuffType >= 0 && nBuffType < ARRAYSIZE( g_RageBuffTypes ) ); // 0 is valid in the array, but an invalid buff
  11173. if ( nBuffType < 0 || nBuffType >= ARRAYSIZE( g_RageBuffTypes ) )
  11174. {
  11175. DevMsg( "Invalid rage buff type %i for entindex %i\n", nBuffType, m_pOuter->entindex() );
  11176. ResetRageSystem();
  11177. return;
  11178. }
  11179. int nBuffPulses = g_RageBuffTypes[nBuffType].m_nMaxPulses;
  11180. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, nBuffPulses, mod_buff_duration );
  11181. if ( nBuffPulses > 0 )
  11182. {
  11183. m_flRageMeter -= gpGlobals->frametime * ( 100 / nBuffPulses );
  11184. if ( m_flRageMeter <= 0.0f )
  11185. {
  11186. m_flRageMeter = 0.0f;
  11187. m_bRageDraining = false;
  11188. if ( g_SoldierBuffAttributeIDToConditionMap[ nBuffType ] == TF_COND_CRITBOOSTED_RAGE_BUFF )
  11189. {
  11190. // Pyro rage needs a cooldown so that the final crit flames
  11191. // don't significantly fill up his next rage meter
  11192. m_flNextRageEarnTime = gpGlobals->curtime + tf_flamethrower_flametime.GetFloat() + 0.1f;
  11193. }
  11194. }
  11195. }
  11196. else
  11197. {
  11198. ResetRageSystem();
  11199. }
  11200. }
  11201. #ifdef STAGING_ONLY
  11202. if ( m_flRageMeter >= 100.f )
  11203. {
  11204. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  11205. {
  11206. if ( CanBuildSpyTraps() )
  11207. {
  11208. // Whenever a spy fills their rage meter, give them a speed boost and grenade (used by traps)
  11209. if ( m_pOuter->GetAmmoCount( TF_AMMO_GRENADES1 ) <= m_pOuter->GetMaxAmmo( TF_AMMO_GRENADES1 ) )
  11210. {
  11211. #ifdef GAME_DLL
  11212. m_pOuter->GiveAmmo( 1, TF_AMMO_GRENADES1 );
  11213. AddCond( TF_COND_SPEED_BOOST, 5.f );
  11214. #endif // GAME_DLL
  11215. m_flRageMeter = 0.f;
  11216. }
  11217. }
  11218. }
  11219. }
  11220. #endif // STAGING_ONLY
  11221. // Handle pulsing all of our active rage buffs.
  11222. for ( int i = 0; i < ARRAYSIZE( m_RageBuffSlots ); i++ )
  11223. {
  11224. RageBuff& rageBuff = m_RageBuffSlots[i];
  11225. if ( gpGlobals->curtime > rageBuff.m_flNextBuffPulseTime && rageBuff.m_iBuffPulseCount > 0 )
  11226. {
  11227. rageBuff.m_flNextBuffPulseTime += 1.0f;
  11228. --rageBuff.m_iBuffPulseCount;
  11229. PulseRageBuff( (ERageBuffSlot)i );
  11230. }
  11231. }
  11232. }
  11233. static const int k_RageBuffType_Sniper = 6;
  11234. //-----------------------------------------------------------------------------
  11235. // Purpose:
  11236. //-----------------------------------------------------------------------------
  11237. void CTFPlayerShared::SetRageMeter( float val )
  11238. {
  11239. // Allow Sniper to gain rage on kills even when buffed
  11240. if ( !InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) && !m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  11241. {
  11242. if ( IsRageDraining() )
  11243. return;
  11244. // Can't earn rage until the time is past this delay
  11245. if ( val > m_flRageMeter && gpGlobals->curtime < m_flNextRageEarnTime )
  11246. return;
  11247. }
  11248. m_flRageMeter = MIN( val, 100.0f );
  11249. if ( InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) )
  11250. {
  11251. Assert( k_RageBuffType_Sniper > 0 && k_RageBuffType_Sniper < ARRAYSIZE( g_RageBuffTypes ) ); // 0 is valid in the array, but an invalid buff
  11252. if ( k_RageBuffType_Sniper < 0 || k_RageBuffType_Sniper >= ARRAYSIZE( g_RageBuffTypes ) )
  11253. return;
  11254. int nBuffPulses = g_RageBuffTypes[k_RageBuffType_Sniper].m_nMaxPulses;
  11255. m_bRageDraining = true;
  11256. nBuffPulses *= (m_flRageMeter / 100);
  11257. m_RageBuffSlots[kBuffSlot_Rage].m_iBuffPulseCount = nBuffPulses;
  11258. }
  11259. }
  11260. //-----------------------------------------------------------------------------
  11261. // Purpose:
  11262. //-----------------------------------------------------------------------------
  11263. void CTFPlayerShared::ModifyRage( float fDelta )
  11264. {
  11265. SetRageMeter( GetRageMeter() + fDelta );
  11266. }
  11267. //-----------------------------------------------------------------------------
  11268. // Purpose:
  11269. //-----------------------------------------------------------------------------
  11270. void CTFPlayerShared::ResetRageMeter( void )
  11271. {
  11272. m_flRageMeter = 0.0f;
  11273. m_flNextRageEarnTime = 0.0f;
  11274. ResetRageBuffs();
  11275. UpdateRageBuffsAndRage();
  11276. }
  11277. //-----------------------------------------------------------------------------
  11278. // Purpose: Apply the buff effect to everyone within a radius around the player.
  11279. //-----------------------------------------------------------------------------
  11280. void CTFPlayerShared::PulseRageBuff( ERageBuffSlot eBuffSlot )
  11281. {
  11282. Assert( m_RageBuffSlots[eBuffSlot].m_iBuffTypeActive != 0 );
  11283. #ifdef CLIENT_DLL
  11284. // if this is not the local player, we don't want to do anything
  11285. if ( !m_pOuter->IsLocalPlayer() )
  11286. return;
  11287. int nBuffedFriends = 0;
  11288. #else
  11289. int nBuffedPlayers = 0;
  11290. #endif
  11291. int iSoldierBuffType = m_RageBuffSlots[eBuffSlot].m_iBuffTypeActive;
  11292. ETFCond eBuffCond = TF_COND_LAST;
  11293. if ( iSoldierBuffType > 0 || iSoldierBuffType <= kSoldierBuffCount )
  11294. {
  11295. eBuffCond = g_SoldierBuffAttributeIDToConditionMap[iSoldierBuffType];
  11296. }
  11297. float fMaxRadius = 450.0f;
  11298. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pOuter, fMaxRadius, mod_soldier_buff_range );
  11299. const float fMaxRadiusSq = fMaxRadius * fMaxRadius;
  11300. for( int iPlayerIndex=1; iPlayerIndex<=MAX_PLAYERS; ++iPlayerIndex )
  11301. {
  11302. CTFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  11303. if ( !pTFPlayer || !pTFPlayer->IsAlive() )
  11304. continue;
  11305. if ( pTFPlayer->GetTeamNumber() != m_pOuter->GetTeamNumber() )
  11306. continue;
  11307. if ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && (pTFPlayer->m_Shared.GetDisguiseTeam() != m_pOuter->GetTeamNumber()) )
  11308. continue; // For now don't give the buff to spies on our team who are disguised as enemies.
  11309. if ( pTFPlayer->m_Shared.IsStealthed() )
  11310. continue; // Don't give the buff to cloaked spies
  11311. Vector vDist = pTFPlayer->GetAbsOrigin() - m_pOuter->GetAbsOrigin();
  11312. if ( vDist.LengthSqr() > fMaxRadiusSq )
  11313. continue;
  11314. #ifdef CLIENT_DLL
  11315. if ( pTFPlayer != m_pOuter )
  11316. {
  11317. if ( eBuffCond == TF_COND_CRITBOOSTED_RAGE_BUFF || eBuffCond == TF_COND_SNIPERCHARGE_RAGE_BUFF )
  11318. {
  11319. // Pyro and sniper only buffs themselves
  11320. continue;
  11321. }
  11322. // this is not the localplayer, are they a friend?
  11323. if ( !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() )
  11324. return;
  11325. player_info_t pi;
  11326. if ( !engine->GetPlayerInfo( pTFPlayer->entindex(), &pi ) )
  11327. return;
  11328. if ( !pi.friendsID )
  11329. return;
  11330. // check and see if they're on the local player's friends list
  11331. CSteamID steamID( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
  11332. if ( steamapicontext->SteamFriends()->HasFriend( steamID, k_EFriendFlagImmediate ) )
  11333. {
  11334. nBuffedFriends++;
  11335. }
  11336. }
  11337. #else
  11338. if ( pTFPlayer != m_pOuter )
  11339. {
  11340. if ( eBuffCond == TF_COND_CRITBOOSTED_RAGE_BUFF || eBuffCond == TF_COND_SNIPERCHARGE_RAGE_BUFF )
  11341. {
  11342. // Pyro and sniper only buffs themselves
  11343. continue;
  11344. }
  11345. }
  11346. if ( eBuffCond != TF_COND_LAST )
  11347. {
  11348. pTFPlayer->m_Shared.AddCond( eBuffCond, 1.2f, m_pOuter );
  11349. nBuffedPlayers++;
  11350. IGameEvent* event = gameeventmanager->CreateEvent( "player_buff" );
  11351. if ( event )
  11352. {
  11353. event->SetInt( "userid", pTFPlayer->GetUserID() );
  11354. event->SetInt( "buff_owner", m_pOuter->GetUserID() );
  11355. event->SetInt( "buff_type", iSoldierBuffType );
  11356. gameeventmanager->FireEvent( event );
  11357. }
  11358. }
  11359. #endif
  11360. }
  11361. #ifdef CLIENT_DLL
  11362. if ( nBuffedFriends >= 5 )
  11363. {
  11364. g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_SOLDIER_BUFF_FRIENDS );
  11365. }
  11366. #else
  11367. // ACHIEVEMENT_TF_MVM_SOLDIER_BUFF_TEAM
  11368. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  11369. {
  11370. if ( ( m_pOuter->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) && m_pOuter->IsPlayerClass( TF_CLASS_SOLDIER ) )
  11371. {
  11372. if ( nBuffedPlayers >= 5 )
  11373. {
  11374. m_pOuter->AwardAchievement( ACHIEVEMENT_TF_MVM_SOLDIER_BUFF_TEAM );
  11375. }
  11376. }
  11377. }
  11378. #endif
  11379. }
  11380. //-----------------------------------------------------------------------------
  11381. // Purpose:
  11382. //-----------------------------------------------------------------------------
  11383. void CTFPlayerShared::ResetRageSystem( void )
  11384. {
  11385. m_flRageMeter = 0.f;
  11386. m_bRageDraining = false;
  11387. m_flNextRageEarnTime = 0.f;
  11388. ResetRageBuffs();
  11389. }
  11390. //-----------------------------------------------------------------------------
  11391. // Purpose:
  11392. //-----------------------------------------------------------------------------
  11393. void CTFPlayerShared::UpdateEnergyDrinkMeter( void )
  11394. {
  11395. if ( !m_pOuter->IsPlayerClass( TF_CLASS_SCOUT ) )
  11396. return;
  11397. bool bIsLocalPlayer = false;
  11398. #ifdef CLIENT_DLL
  11399. bIsLocalPlayer = m_pOuter->IsLocalPlayer();
  11400. #else
  11401. bIsLocalPlayer = true;
  11402. #endif
  11403. if ( bIsLocalPlayer )
  11404. {
  11405. if ( IsHypeBuffed() )
  11406. {
  11407. m_flHypeMeter -= gpGlobals->frametime * (m_fEnergyDrinkConsumeRate*0.75f);
  11408. if ( m_flHypeMeter <= 0.0f )
  11409. {
  11410. RemoveCond( TF_COND_SODAPOPPER_HYPE );
  11411. }
  11412. }
  11413. if ( InCond( TF_COND_PHASE ) || InCond( TF_COND_ENERGY_BUFF ) )
  11414. {
  11415. // Drain the meter
  11416. m_flEnergyDrinkMeter -= gpGlobals->frametime * m_fEnergyDrinkConsumeRate;
  11417. // If we've drained the meter, remove the condition
  11418. if ( m_flEnergyDrinkMeter <= 0.f )
  11419. {
  11420. if ( InCond( TF_COND_ENERGY_BUFF ) )
  11421. {
  11422. AddCond( TF_COND_MARKEDFORDEATH_SILENT, 2.f );
  11423. }
  11424. RemoveCond( TF_COND_PHASE );
  11425. RemoveCond( TF_COND_ENERGY_BUFF );
  11426. #ifdef GAME_DLL
  11427. m_pOuter->SpeakConceptIfAllowed( MP_CONCEPT_TIRED );
  11428. #endif
  11429. }
  11430. // Update the effect on phasing only
  11431. else if ( InCond( TF_COND_PHASE ) )
  11432. {
  11433. UpdatePhaseEffects();
  11434. }
  11435. }
  11436. else if ( m_flEnergyDrinkMeter < 100.0f )
  11437. {
  11438. // Regen the meter
  11439. m_flEnergyDrinkMeter += gpGlobals->frametime * m_fEnergyDrinkRegenRate;
  11440. CTFLunchBox_Drink *pDrink = static_cast< CTFLunchBox_Drink* >( m_pOuter->Weapon_OwnsThisID( TF_WEAPON_LUNCHBOX ) );
  11441. if ( pDrink )
  11442. {
  11443. // This is here in case something replenishes grenades
  11444. if ( m_flEnergyDrinkMeter < 100.0f && m_pOuter->GetAmmoCount( TF_AMMO_GRENADES2 ) == m_pOuter->GetMaxAmmo( TF_AMMO_GRENADES2 ) )
  11445. {
  11446. m_flEnergyDrinkMeter = 100.0f;
  11447. }
  11448. }
  11449. else if ( m_flEnergyDrinkMeter >= 100.0f )
  11450. {
  11451. m_flEnergyDrinkMeter = 100.0f;
  11452. }
  11453. }
  11454. }
  11455. }
  11456. void CTFPlayerShared::SetScoutHypeMeter( float val )
  11457. {
  11458. if ( IsHypeBuffed() )
  11459. return;
  11460. m_flHypeMeter = Clamp(val, 0.0f, 100.0f);
  11461. //if ( m_flHypeMeter >= 100.f )
  11462. //{
  11463. // if ( m_pOuter->IsPlayerClass( TF_CLASS_SCOUT ) )
  11464. // {
  11465. // CTFWeaponBase* pWeapon = m_pOuter->GetActiveTFWeapon();
  11466. // if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_SODA_POPPER )
  11467. // {
  11468. // AddCond( TF_COND_CRITBOOSTED_HYPE );
  11469. // }
  11470. // }
  11471. //}
  11472. }
  11473. //-----------------------------------------------------------------------------
  11474. // Purpose:
  11475. //-----------------------------------------------------------------------------
  11476. void CTFPlayerShared::UpdateCloakMeter( void )
  11477. {
  11478. if ( !m_pOuter->IsPlayerClass( TF_CLASS_SPY ) )
  11479. return;
  11480. if ( InCond( TF_COND_STEALTHED ) )
  11481. {
  11482. if ( m_bMotionCloak )
  11483. {
  11484. // Motion cloak: drain based on our movement rate.
  11485. Vector vVel = m_pOuter->GetAbsVelocity();
  11486. float fSpdSqr = vVel.LengthSqr();
  11487. if ( fSpdSqr == 0.f )
  11488. {
  11489. if ( gpGlobals->curtime - m_flLastStealthExposeTime > 1.f )
  11490. {
  11491. m_flCloakMeter += gpGlobals->frametime * m_fCloakRegenRate;
  11492. if ( m_flCloakMeter >= 100.0f )
  11493. {
  11494. m_flCloakMeter = 100.0f;
  11495. }
  11496. }
  11497. }
  11498. else
  11499. {
  11500. float fFactor = RemapVal( fSpdSqr, 0, m_pOuter->MaxSpeed()*m_pOuter->MaxSpeed(), 0.f, 1.f );
  11501. if ( fFactor > 1.f )
  11502. {
  11503. fFactor = 1.f;
  11504. }
  11505. m_flCloakMeter -= gpGlobals->frametime * m_fCloakConsumeRate * fFactor * 1.5f;
  11506. if ( m_flCloakMeter < 0.f )
  11507. {
  11508. m_flCloakMeter = 0.f;
  11509. }
  11510. }
  11511. }
  11512. else
  11513. {
  11514. // Classic cloak: drain at a fixed rate.
  11515. m_flCloakMeter -= gpGlobals->frametime * m_fCloakConsumeRate;
  11516. }
  11517. if ( m_flCloakMeter <= 0.0f && !m_bMotionCloak)
  11518. {
  11519. FadeInvis( 1.0f );
  11520. }
  11521. // Update Debuffs
  11522. // Decrease duration if cloaked
  11523. #ifdef GAME_DLL
  11524. // staging_spy
  11525. float flReduction = gpGlobals->frametime * 0.75f;
  11526. for ( int i = 0; g_aDebuffConditions[i] != TF_COND_LAST; i++ )
  11527. {
  11528. if ( InCond( g_aDebuffConditions[i] ) )
  11529. {
  11530. if ( m_ConditionData[g_aDebuffConditions[i]].m_flExpireTime != PERMANENT_CONDITION )
  11531. {
  11532. m_ConditionData[g_aDebuffConditions[i]].m_flExpireTime = MAX( m_ConditionData[g_aDebuffConditions[i]].m_flExpireTime - flReduction, 0 );
  11533. }
  11534. // Burning and Bleeding and extra timers
  11535. if ( g_aDebuffConditions[i] == TF_COND_BURNING )
  11536. {
  11537. // Reduce the duration of this burn
  11538. m_flFlameRemoveTime -= flReduction;
  11539. }
  11540. else if ( g_aDebuffConditions[i] == TF_COND_BLEEDING )
  11541. {
  11542. // Reduce the duration of this bleeding
  11543. FOR_EACH_VEC( m_PlayerBleeds, i )
  11544. {
  11545. m_PlayerBleeds[i].flBleedingRemoveTime -= flReduction;
  11546. }
  11547. }
  11548. }
  11549. }
  11550. #endif
  11551. }
  11552. else
  11553. {
  11554. m_flCloakMeter += gpGlobals->frametime * m_fCloakRegenRate;
  11555. if ( m_flCloakMeter >= 100.0f )
  11556. {
  11557. m_flCloakMeter = 100.0f;
  11558. }
  11559. }
  11560. }
  11561. //-----------------------------------------------------------------------------
  11562. // Purpose: Whether we should be doing a radius heal (does not stack with a medigun)
  11563. //-----------------------------------------------------------------------------
  11564. void CTFPlayerShared::Heal_Radius( bool bActive )
  11565. {
  11566. if ( bActive )
  11567. {
  11568. m_bPulseRadiusHeal = true;
  11569. }
  11570. else
  11571. {
  11572. m_bPulseRadiusHeal = false;
  11573. #ifdef GAME_DLL
  11574. // Stop any Radius healing
  11575. if ( m_iRadiusHealTargets.Count() > 0 )
  11576. {
  11577. for ( int iIndex = 0; iIndex < m_iRadiusHealTargets.Count(); iIndex++ )
  11578. {
  11579. CTFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( m_iRadiusHealTargets[iIndex] ) );
  11580. if ( !pTFPlayer )
  11581. continue;
  11582. pTFPlayer->m_Shared.StopHealing( m_pOuter );
  11583. }
  11584. m_iRadiusHealTargets.RemoveAll();
  11585. }
  11586. #endif // GAME_DLL
  11587. #ifdef CLIENT_DLL
  11588. if ( m_pOuter && m_pOuter->m_pRadiusHealEffect )
  11589. {
  11590. m_pOuter->ParticleProp()->StopEmission( m_pOuter->m_pRadiusHealEffect );
  11591. }
  11592. m_pOuter->m_pRadiusHealEffect = NULL;
  11593. #endif // CLIENT_DLL
  11594. }
  11595. }
  11596. #ifdef STAGING_ONLY
  11597. //-----------------------------------------------------------------------------
  11598. // Purpose:
  11599. //-----------------------------------------------------------------------------
  11600. ConVar tf_rocket_pack_cooldown( "tf_rocket_pack_cooldown", "1.f", FCVAR_CHEAT | FCVAR_REPLICATED );
  11601. ConVar tf_rocket_pack_enabled( "tf_rocket_pack_enabled", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
  11602. void CTFPlayerShared::DoRocketPack()
  11603. {
  11604. if ( !tf_rocket_pack_enabled.GetBool() )
  11605. return;
  11606. if ( !( m_pOuter->m_nButtons & IN_ATTACK3 ) )
  11607. return;
  11608. if ( gpGlobals->curtime < m_flNextRocketPackTime )
  11609. return;
  11610. #ifdef GAME_DLL
  11611. // Launch
  11612. if ( !InCond( TF_COND_ROCKETPACK ) )
  11613. {
  11614. AddCond( TF_COND_ROCKETPACK );
  11615. StunPlayer( 0.5f, 1.0f, TF_STUN_MOVEMENT );
  11616. }
  11617. #endif
  11618. Vector vecDir;
  11619. m_pOuter->EyeVectors( &vecDir );
  11620. m_pOuter->SetAbsVelocity( vec3_origin );
  11621. Vector vecFlightDir = -vecDir;
  11622. VectorNormalize( vecFlightDir );
  11623. float flForce = 450.f;
  11624. const float flPushScale = ( m_pOuter->GetFlags() & FL_ONGROUND ) ? 1.2f : 1.8f; // Greater force while airborne
  11625. const float flVertPushScale = ( m_pOuter->GetFlags() & FL_ONGROUND ) ? 1.2f : 0.25f; // Less vertical force while airborne
  11626. Vector vecForce = vecFlightDir * -flForce * flPushScale;
  11627. vecForce.z += 1.f * flForce * flVertPushScale;
  11628. m_pOuter->RemoveFlag( FL_ONGROUND );
  11629. m_pOuter->ApplyAbsVelocityImpulse( vecForce );
  11630. m_pOuter->EmitSound( "Equipment.RocketPack_Activate" );
  11631. m_flNextRocketPackTime = gpGlobals->curtime + tf_rocket_pack_cooldown.GetFloat();
  11632. }
  11633. #endif // STAGING_ONLY
  11634. //-----------------------------------------------------------------------------
  11635. // Purpose: Emits an area-of-effect heal around the medic
  11636. //-----------------------------------------------------------------------------
  11637. void CTFPlayerShared::PulseMedicRadiusHeal( void )
  11638. {
  11639. if ( !m_bPulseRadiusHeal )
  11640. {
  11641. #ifdef GAME_DLL
  11642. Assert( m_iRadiusHealTargets.Count() == 0 );
  11643. if ( m_iRadiusHealTargets.Count() > 0 )
  11644. {
  11645. // We shouldn't have any heal targets if we aren't pulsing.
  11646. Heal_Radius( false );
  11647. }
  11648. #endif
  11649. return;
  11650. }
  11651. // If we're set to heal, make sure it's still valid
  11652. if ( !m_pOuter->IsAlive() || ( !m_pOuter->IsPlayerClass( TF_CLASS_MEDIC ) && !InCond( TF_COND_RADIUSHEAL_ON_DAMAGE ) ) )
  11653. {
  11654. Heal_Radius( false );
  11655. return;
  11656. }
  11657. #ifdef GAME_DLL
  11658. if ( gpGlobals->curtime >= m_flRadiusHealCheckTime )
  11659. {
  11660. for( int iPlayerIndex = 1; iPlayerIndex <= MAX_PLAYERS; ++iPlayerIndex )
  11661. {
  11662. CTFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  11663. if ( !pTFPlayer || !pTFPlayer->IsAlive() )
  11664. continue;
  11665. // Don't heal ourselves, unless this is due to radius heal on damage proc
  11666. if ( pTFPlayer == m_pOuter && !InCond( TF_COND_RADIUSHEAL_ON_DAMAGE ) )
  11667. continue;
  11668. if ( !pTFPlayer->InSameTeam( m_pOuter ) )
  11669. {
  11670. if ( !pTFPlayer->m_Shared.IsStealthed() && !pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  11671. continue;
  11672. if ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( pTFPlayer->m_Shared.GetDisguiseTeam() != m_pOuter->GetTeamNumber() ) )
  11673. continue;
  11674. }
  11675. // Don't heal players with weapon_blocks_healing
  11676. CTFWeaponBase *pTFWeapon = pTFPlayer->GetActiveTFWeapon();
  11677. if ( pTFWeapon )
  11678. {
  11679. int iBlockHealing = 0;
  11680. CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFWeapon, iBlockHealing, weapon_blocks_healing );
  11681. if ( iBlockHealing )
  11682. continue;
  11683. }
  11684. Vector vDist = pTFPlayer->GetAbsOrigin() - m_pOuter->GetAbsOrigin();
  11685. if ( vDist.LengthSqr() <= 450 * 450 )
  11686. {
  11687. // Ignore players we can't see
  11688. trace_t trace;
  11689. UTIL_TraceLine( pTFPlayer->WorldSpaceCenter(), m_pOuter->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  11690. if ( trace.fraction < 1.0f )
  11691. continue;
  11692. // Refresh this condition, which we use to give players a particle effect
  11693. pTFPlayer->m_Shared.AddCond( TF_COND_RADIUSHEAL, 1.2f );
  11694. // Make sure we're not already healing them
  11695. if ( m_iRadiusHealTargets.Find( iPlayerIndex ) == m_iRadiusHealTargets.InvalidIndex() )
  11696. {
  11697. m_iRadiusHealTargets.AddToTail( iPlayerIndex );
  11698. pTFPlayer->m_Shared.Heal( m_pOuter, 25, 1, 1 );
  11699. }
  11700. }
  11701. else
  11702. {
  11703. if ( m_iRadiusHealTargets.Find( iPlayerIndex ) != m_iRadiusHealTargets.InvalidIndex() )
  11704. {
  11705. m_iRadiusHealTargets.FindAndRemove( iPlayerIndex );
  11706. pTFPlayer->m_Shared.StopHealing( m_pOuter );
  11707. }
  11708. }
  11709. }
  11710. m_flRadiusHealCheckTime = gpGlobals->curtime + 1.0f;
  11711. }
  11712. #endif // GAME_DLL
  11713. #ifdef CLIENT_DLL
  11714. // Radius healer gets an effect to broadcast to others what they're doing
  11715. if ( !m_pOuter->m_pRadiusHealEffect )
  11716. {
  11717. const char *pszRadiusHealEffect;
  11718. if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED )
  11719. {
  11720. pszRadiusHealEffect = "medic_healradius_red_buffed";
  11721. }
  11722. else
  11723. {
  11724. pszRadiusHealEffect = "medic_healradius_blue_buffed";
  11725. }
  11726. m_pOuter->m_pRadiusHealEffect = m_pOuter->ParticleProp()->Create( pszRadiusHealEffect, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 0 ) );
  11727. }
  11728. #endif // CLIENT_DLL
  11729. }
  11730. //-----------------------------------------------------------------------------
  11731. // Purpose: Emits an area-of-effect buff around the King Rune carrier
  11732. //-----------------------------------------------------------------------------
  11733. void CTFPlayerShared::PulseKingRuneBuff( void )
  11734. {
  11735. // Make sure we have the King Powerup and are not invisible
  11736. if ( !m_pOuter->IsAlive() || IsStealthed() || GetCarryingRuneType() != RUNE_KING )
  11737. {
  11738. return;
  11739. }
  11740. #ifdef GAME_DLL
  11741. if ( gpGlobals->curtime >= m_flKingRuneBuffCheckTime )
  11742. {
  11743. m_bKingRuneBuffActive = false;
  11744. // Plague blocks king team buff
  11745. if ( !InCond( TF_COND_PLAGUE ) )
  11746. {
  11747. for ( int iPlayerIndex = 1; iPlayerIndex <= MAX_PLAYERS; ++iPlayerIndex )
  11748. {
  11749. CTFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  11750. if ( !pTFPlayer || !pTFPlayer->IsAlive() )
  11751. continue;
  11752. // Ignore players outside of the buff radius
  11753. Vector vDist = pTFPlayer->GetAbsOrigin() - m_pOuter->GetAbsOrigin();
  11754. if ( vDist.LengthSqr() >= 768 * 768 )
  11755. continue;
  11756. // If King is the only player, there's no effect
  11757. if ( pTFPlayer == m_pOuter )
  11758. continue;
  11759. // Spies who are invisible or disguised as the King's enemy team are ignored
  11760. if ( pTFPlayer->m_Shared.IsStealthed() || ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTFPlayer->m_Shared.GetDisguiseTeam() != m_pOuter->GetTeamNumber() ) )
  11761. continue;
  11762. // Enemies - ignore unless they are disguised as the King's team
  11763. if ( !pTFPlayer->InSameTeam( m_pOuter ) && !pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  11764. continue;
  11765. pTFPlayer->m_Shared.AddCond( TF_COND_KING_BUFFED, 1.f );
  11766. m_bKingRuneBuffActive = true;
  11767. }
  11768. }
  11769. m_flKingRuneBuffCheckTime = gpGlobals->curtime + 0.5f;
  11770. }
  11771. #endif // GAME_DLL
  11772. #ifdef CLIENT_DLL
  11773. // King Rune carrier gets an effect to show that he's buffing someone
  11774. if ( m_bKingRuneBuffActive && !InCond( TF_COND_PLAGUE ) )
  11775. {
  11776. if ( !m_pOuter->m_pKingRuneRadiusEffect )
  11777. {
  11778. const char *pszRadiusEffect;
  11779. if ( m_pOuter->GetTeamNumber() == TF_TEAM_RED )
  11780. {
  11781. pszRadiusEffect = "powerup_king_red";
  11782. }
  11783. else
  11784. {
  11785. pszRadiusEffect = "powerup_king_blue";
  11786. }
  11787. m_pOuter->m_pKingRuneRadiusEffect = m_pOuter->ParticleProp()->Create( pszRadiusEffect, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 0 ) );
  11788. }
  11789. }
  11790. else
  11791. {
  11792. EndKingBuffRadiusEffect();
  11793. }
  11794. #endif // CLIENT_DLL
  11795. }
  11796. //-----------------------------------------------------------------------------
  11797. // Purpose:
  11798. //-----------------------------------------------------------------------------
  11799. void CTFPlayerShared::IncrementRevengeCrits( void )
  11800. {
  11801. SetRevengeCrits( m_iRevengeCrits + 1 );
  11802. }
  11803. //-----------------------------------------------------------------------------
  11804. // Purpose:
  11805. //-----------------------------------------------------------------------------
  11806. void CTFPlayerShared::SetRevengeCrits( int iVal )
  11807. {
  11808. m_iRevengeCrits = clamp( iVal, 0, 35 );
  11809. CTFWeaponBase *pWeapon = m_pOuter->GetActiveTFWeapon();
  11810. if ( ( pWeapon && pWeapon->CanHaveRevengeCrits() ) )
  11811. {
  11812. if ( m_iRevengeCrits > 0 && !InCond( TF_COND_CRITBOOSTED ) )
  11813. {
  11814. AddCond( TF_COND_CRITBOOSTED );
  11815. }
  11816. else if ( m_iRevengeCrits == 0 && InCond( TF_COND_CRITBOOSTED ) )
  11817. {
  11818. RemoveCond( TF_COND_CRITBOOSTED );
  11819. }
  11820. }
  11821. }
  11822. //-----------------------------------------------------------------------------
  11823. // Purpose:
  11824. //-----------------------------------------------------------------------------
  11825. void CTFPlayerShared::FireGameEvent( IGameEvent *event )
  11826. {
  11827. #ifdef GAME_DLL
  11828. const char *eventName = event->GetName();
  11829. if ( !Q_strcmp( eventName, "player_disconnect" ) )
  11830. {
  11831. CBasePlayer *pPlayer = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  11832. if ( pPlayer )
  11833. {
  11834. int iIndex = m_iRadiusHealTargets.Find( pPlayer->entindex() );
  11835. if ( iIndex != m_iRadiusHealTargets.InvalidIndex() )
  11836. {
  11837. m_iRadiusHealTargets.FastRemove( iIndex );
  11838. }
  11839. }
  11840. }
  11841. #endif //GAME_DLL
  11842. }
  11843. //-----------------------------------------------------------------------------
  11844. void CTFPlayerShared::SetPasstimePassTarget( CTFPlayer *pEnt )
  11845. {
  11846. if ( CBaseEntity *pTarget = m_hPasstimePassTarget )
  11847. {
  11848. CTFPlayer *pPlayerTarget = ToTFPlayer( pTarget );
  11849. if ( pPlayerTarget )
  11850. pPlayerTarget->m_Shared.m_bIsTargetedForPasstimePass = false;
  11851. }
  11852. Assert( pEnt != m_pOuter );
  11853. m_hPasstimePassTarget = pEnt;
  11854. if ( CBaseEntity *pTarget = m_hPasstimePassTarget )
  11855. {
  11856. CTFPlayer *pPlayerTarget = ToTFPlayer( pTarget );
  11857. if ( pPlayerTarget )
  11858. pPlayerTarget->m_Shared.m_bIsTargetedForPasstimePass = true;
  11859. }
  11860. }
  11861. //-----------------------------------------------------------------------------
  11862. CTFPlayer *CTFPlayerShared::GetPasstimePassTarget() const { return m_hPasstimePassTarget.Get(); }
  11863. //-----------------------------------------------------------------------------
  11864. // Purpose:
  11865. //-----------------------------------------------------------------------------
  11866. bool CTraceFilterIgnoreTeammatesAndTeamObjects::ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  11867. {
  11868. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  11869. if ( pEntity->GetTeamNumber() == m_iIgnoreTeam )
  11870. {
  11871. return false;
  11872. }
  11873. CTFPlayer *pPlayer = dynamic_cast<CTFPlayer*>( pEntity );
  11874. if ( pPlayer )
  11875. {
  11876. if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pPlayer->m_Shared.GetDisguiseTeam() == m_iIgnoreTeam )
  11877. return false;
  11878. if ( pPlayer->m_Shared.IsStealthed() )
  11879. return false;
  11880. }
  11881. return BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
  11882. }
  11883. void CTFPlayerShared::SetCarriedObject( CBaseObject* pObj )
  11884. {
  11885. m_bCarryingObject = (pObj != NULL);
  11886. m_hCarriedObject.Set( pObj );
  11887. #ifdef GAME_DLL
  11888. if ( m_pOuter )
  11889. m_pOuter->TeamFortress_SetSpeed();
  11890. #endif
  11891. }
  11892. void localplayerscoring_t::UpdateStats( RoundStats_t& roundStats, CTFPlayer *pPlayer, bool bIsRoundData )
  11893. {
  11894. m_iCaptures = roundStats.m_iStat[TFSTAT_CAPTURES];
  11895. m_iDefenses = roundStats.m_iStat[TFSTAT_DEFENSES];
  11896. m_iKills = roundStats.m_iStat[TFSTAT_KILLS];
  11897. m_iDeaths = roundStats.m_iStat[TFSTAT_DEATHS];
  11898. m_iSuicides = roundStats.m_iStat[TFSTAT_SUICIDES];
  11899. m_iKillAssists = roundStats.m_iStat[TFSTAT_KILLASSISTS];
  11900. m_iBuildingsBuilt = roundStats.m_iStat[TFSTAT_BUILDINGSBUILT];
  11901. m_iBuildingsDestroyed = roundStats.m_iStat[TFSTAT_BUILDINGSDESTROYED];
  11902. m_iHeadshots = roundStats.m_iStat[TFSTAT_HEADSHOTS];
  11903. m_iDominations = roundStats.m_iStat[TFSTAT_DOMINATIONS];
  11904. m_iRevenge = roundStats.m_iStat[TFSTAT_REVENGE];
  11905. m_iInvulns = roundStats.m_iStat[TFSTAT_INVULNS];
  11906. m_iTeleports = roundStats.m_iStat[TFSTAT_TELEPORTS];
  11907. m_iDamageDone = roundStats.m_iStat[TFSTAT_DAMAGE];
  11908. m_iCrits = roundStats.m_iStat[TFSTAT_CRITS];
  11909. m_iBackstabs = roundStats.m_iStat[TFSTAT_BACKSTABS];
  11910. int iHealthPointsHealed = (int) roundStats.m_iStat[TFSTAT_HEALING];
  11911. // send updated healing data every 10 health points, and round off what we send to nearest 10 points
  11912. int iHealPointsDelta = abs( iHealthPointsHealed - m_iHealPoints );
  11913. if ( iHealPointsDelta > 10 )
  11914. {
  11915. m_iHealPoints = ( iHealthPointsHealed / 10 ) * 10;
  11916. }
  11917. m_iBonusPoints = roundStats.m_iStat[TFSTAT_BONUS_POINTS] / TF_SCORE_BONUS_POINT_DIVISOR;
  11918. const int nPoints = TFGameRules()->CalcPlayerScore( &roundStats, pPlayer );
  11919. const int nDelta = nPoints - m_iPoints;
  11920. m_iPoints = nPoints;
  11921. if ( nDelta > 0 && !bIsRoundData )
  11922. {
  11923. IGameEvent *event = gameeventmanager->CreateEvent( "player_score_changed" );
  11924. if ( event )
  11925. {
  11926. event->SetInt( "player", pPlayer->entindex() );
  11927. event->SetInt( "delta", nDelta );
  11928. gameeventmanager->FireEvent( event );
  11929. }
  11930. }
  11931. }
  11932. //-----------------------------------------------------------------------------
  11933. // Purpose:
  11934. //-----------------------------------------------------------------------------
  11935. CEconItemView *CTFPlayerSharedUtils::GetEconItemViewByLoadoutSlot( CTFPlayer *pTFPlayer, int iSlot, CEconEntity **pEntity )
  11936. {
  11937. int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
  11938. // See if it's a weapon first
  11939. for ( int i = 0; i < MAX_WEAPONS; i++ )
  11940. {
  11941. CTFWeaponBase *pWeapon = (CTFWeaponBase *)pTFPlayer->GetWeapon(i);
  11942. if ( !pWeapon )
  11943. continue;
  11944. CEconItemView *pEconItemView = pWeapon->GetAttributeContainer()->GetItem();
  11945. if ( !pEconItemView )
  11946. continue;
  11947. int iLoadoutSlot = pEconItemView->GetStaticData()->GetLoadoutSlot( iClass );
  11948. if ( iLoadoutSlot == iSlot )
  11949. {
  11950. if ( pEntity )
  11951. {
  11952. *pEntity = pWeapon;
  11953. }
  11954. return pEconItemView;
  11955. }
  11956. }
  11957. // Go through each of the actual items we have equipped right now...
  11958. for ( int i = 0; i < pTFPlayer->GetNumWearables(); ++i )
  11959. {
  11960. CTFWearable *pWearableItem = dynamic_cast<CTFWearable *>( pTFPlayer->GetWearable( i ) );
  11961. if ( !pWearableItem )
  11962. continue;
  11963. if ( !pWearableItem->GetAttributeContainer() )
  11964. continue;
  11965. CEconItemView *pEconItemView = pWearableItem->GetAttributeContainer()->GetItem();
  11966. if ( !pEconItemView )
  11967. continue;
  11968. CTFItemDefinition *pItemDef = pEconItemView->GetStaticData();
  11969. if ( !pItemDef )
  11970. continue;
  11971. if ( pItemDef->GetLoadoutSlot(iClass) != iSlot )
  11972. continue;
  11973. // Yay!
  11974. if ( pEntity )
  11975. {
  11976. *pEntity = pWearableItem;
  11977. }
  11978. return pEconItemView;
  11979. }
  11980. // Nothing we currently have equipped claims to be in this slot.
  11981. if ( pEntity )
  11982. {
  11983. *pEntity = NULL;
  11984. }
  11985. return NULL;
  11986. }
  11987. bool CTFPlayerSharedUtils::ConceptIsPartnerTaunt( int iConcept )
  11988. {
  11989. return iConcept == MP_CONCEPT_HIGHFIVE_SUCCESS_FULL || iConcept == MP_CONCEPT_HIGHFIVE_SUCCESS;
  11990. }
  11991. //-----------------------------------------------------------------------------
  11992. // Purpose:
  11993. //-----------------------------------------------------------------------------
  11994. CTFWeaponBuilder *CTFPlayerSharedUtils::GetBuilderForObjectType( CTFPlayer *pTFPlayer, int iObjectType )
  11995. {
  11996. const int OBJ_ANY = -1;
  11997. if ( !pTFPlayer )
  11998. return NULL;
  11999. for ( int i = 0; i < MAX_WEAPONS; i++ )
  12000. {
  12001. CTFWeaponBuilder *pBuilder = dynamic_cast< CTFWeaponBuilder* >( pTFPlayer->GetWeapon( i ) );
  12002. if ( !pBuilder )
  12003. continue;
  12004. // Any builder will do - return first
  12005. if ( iObjectType == OBJ_ANY )
  12006. return pBuilder;
  12007. // Requires a specific builder for this type
  12008. if ( pBuilder->CanBuildObjectType( iObjectType ) )
  12009. return pBuilder;
  12010. }
  12011. return NULL;
  12012. }
  12013. //-----------------------------------------------------------------------------
  12014. // Purpose: Can player pick up this weapon?
  12015. //-----------------------------------------------------------------------------
  12016. bool CTFPlayer::CanPickupDroppedWeapon( const CTFDroppedWeapon *pWeapon )
  12017. {
  12018. if ( !pWeapon->GetItem()->IsValid() )
  12019. return false;
  12020. int iClass = GetPlayerClass()->GetClassIndex();
  12021. if ( iClass == TF_CLASS_SPY && ( m_Shared.InCond( TF_COND_DISGUISED ) || m_Shared.GetPercentInvisible() > 0 ) )
  12022. return false;
  12023. if ( IsTaunting() )
  12024. return false;
  12025. if ( !IsAlive() )
  12026. return false;
  12027. // There's a rare case that the player doesn't have an active weapon. This shouldn't happen.
  12028. // If you hit this assert, figure out and fix WHY the player doesn't have a weapon.
  12029. Assert( GetActiveTFWeapon() );
  12030. if ( !GetActiveTFWeapon() || !GetActiveTFWeapon()->CanPickupOtherWeapon() )
  12031. return false;
  12032. int iItemSlot = pWeapon->GetItem()->GetStaticData()->GetLoadoutSlot( iClass );
  12033. CBaseEntity *pOwnedWeaponToDrop = GetEntityForLoadoutSlot( iItemSlot );
  12034. return pOwnedWeaponToDrop && pWeapon->GetItem()->GetStaticData()->CanBeUsedByClass( iClass ) && IsValidPickupWeaponSlot( iItemSlot );
  12035. }
  12036. //-----------------------------------------------------------------------------
  12037. // Purpose: Returns true if player is in range to pick up this weapon
  12038. //-----------------------------------------------------------------------------
  12039. CTFDroppedWeapon* CTFPlayer::GetDroppedWeaponInRange()
  12040. {
  12041. // Check to see if a building we own is in front of us.
  12042. Vector vecForward;
  12043. AngleVectors( EyeAngles(), &vecForward );
  12044. trace_t tr;
  12045. UTIL_TraceLine( EyePosition(), EyePosition() + vecForward * TF_WEAPON_PICKUP_RANGE, MASK_SOLID | CONTENTS_DEBRIS, this, COLLISION_GROUP_NONE, &tr );
  12046. CTFDroppedWeapon *pDroppedWeapon = dynamic_cast< CTFDroppedWeapon * >( tr.m_pEnt );
  12047. if ( !pDroppedWeapon )
  12048. return NULL;
  12049. if ( !CanPickupDroppedWeapon( pDroppedWeapon ) )
  12050. return NULL;
  12051. // too far?
  12052. if ( EyePosition().DistToSqr( pDroppedWeapon->GetAbsOrigin() ) > Square( TF_WEAPON_PICKUP_RANGE ) )
  12053. return NULL;
  12054. return pDroppedWeapon;
  12055. }
  12056. //-----------------------------------------------------------------------------
  12057. // Purpose: Returns true if player is inspecting
  12058. //-----------------------------------------------------------------------------
  12059. bool CTFPlayer::IsInspecting() const
  12060. {
  12061. return m_flInspectTime != 0.f && gpGlobals->curtime - m_flInspectTime > 0.2f;
  12062. }