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.

12134 lines
352 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "c_tf_player.h"
  8. #include "c_user_message_register.h"
  9. #include "view.h"
  10. #include "iclientvehicle.h"
  11. #include "ivieweffects.h"
  12. #include "input.h"
  13. #include "IEffects.h"
  14. #include "fx.h"
  15. #include "c_basetempentity.h"
  16. #include "hud_macros.h"
  17. #include "engine/ivdebugoverlay.h"
  18. #include "smoke_fog_overlay.h"
  19. #include "playerandobjectenumerator.h"
  20. #include "bone_setup.h"
  21. #include "in_buttons.h"
  22. #include "r_efx.h"
  23. #include "dlight.h"
  24. #include "shake.h"
  25. #include "cl_animevent.h"
  26. #include "animation.h"
  27. #include "choreoscene.h"
  28. #include "tf_weaponbase.h"
  29. #include "c_tf_playerresource.h"
  30. #include "toolframework/itoolframework.h"
  31. #include "tier1/KeyValues.h"
  32. #include "tier0/vprof.h"
  33. #include "prediction.h"
  34. #include "effect_dispatch_data.h"
  35. #include "c_te_effect_dispatch.h"
  36. #include "tf_fx_muzzleflash.h"
  37. #include "tf_gamerules.h"
  38. #include "view_scene.h"
  39. #include "c_baseobject.h"
  40. #include "toolframework_client.h"
  41. #include "materialsystem/imaterialvar.h"
  42. #include "soundenvelope.h"
  43. #include "voice_status.h"
  44. #include "clienteffectprecachesystem.h"
  45. #include "functionproxy.h"
  46. #include "toolframework_client.h"
  47. #include "choreoevent.h"
  48. #include "vguicenterprint.h"
  49. #include "eventlist.h"
  50. #include "input.h"
  51. #include "tf_weapon_medigun.h"
  52. #include "tf_weapon_pipebomblauncher.h"
  53. #include "tf_weapon_shovel.h"
  54. #include "tf_hud_mediccallers.h"
  55. #include "in_main.h"
  56. #include "c_team.h"
  57. #include "collisionutils.h"
  58. // for spy material proxy
  59. #include "tf_proxyentity.h"
  60. #include "materialsystem/imaterial.h"
  61. #include "materialsystem/imaterialvar.h"
  62. #include "materialsystem/itexturecompositor.h"
  63. #include "c_tf_team.h"
  64. #include "tf_item_inventory.h"
  65. #include "model_types.h"
  66. #include "dt_utlvector_recv.h"
  67. #include "tf_item_wearable.h"
  68. #include "cam_thirdperson.h"
  69. #include "c_tf_projectile_arrow.h"
  70. #include "econ_entity.h"
  71. #include "ihasowner.h"
  72. #include "tf_hud_itemeffectmeter.h"
  73. #include "replay/vgui/replayinputpanel.h"
  74. #include "tf_replay.h"
  75. #include "netadr.h"
  76. #include "input.h"
  77. #include "gcsdk/gcclientsdk.h"
  78. #include "econ_gcmessages.h"
  79. #include "rtime.h"
  80. #include "networkstringtable_clientdll.h"
  81. #include "replay/ireplaymanager.h"
  82. #include "gc_clientsystem.h"
  83. #include "c_entitydissolve.h"
  84. #include "tf_viewmodel.h"
  85. #include "player_vs_environment/c_tf_upgrades.h"
  86. #include "sourcevr/isourcevirtualreality.h"
  87. #include "tempent.h"
  88. #include "confirm_dialog.h"
  89. #include "c_tf_weapon_builder.h"
  90. #include "tf_shared_content_manager.h"
  91. #include "baseanimatedtextureproxy.h"
  92. #include "econ_entity.h"
  93. #include "halloween/tf_weapon_spellbook.h"
  94. #include "tf_weapon_grapplinghook.h"
  95. #include "tf_logic_robot_destruction.h"
  96. #include "econ_notifications.h"
  97. #include "tf_weapon_buff_item.h"
  98. #include "tf_dropped_weapon.h"
  99. #include "tf_hud_notification_panel.h"
  100. #include "tf_dropped_weapon.h"
  101. #include "tf_hud_passtime_reticle.h"
  102. #include "passtime_convars.h"
  103. #include "c_tf_passtime_logic.h"
  104. #include "tf_weapon_passtime_gun.h"
  105. #include "eiface.h"
  106. #include "filesystem.h"
  107. #include "debugoverlay_shared.h"
  108. #include "tf_hud_chat.h"
  109. #include "tf_item_powerup_bottle.h"
  110. #include <vgui_controls/AnimationController.h>
  111. #ifdef STAGING_ONLY
  112. #include "tf_matchmaking_shared.h"
  113. #include "tier2/keyvaluesmacros.h"
  114. #endif // STAGING_ONLY
  115. #if defined( REPLAY_ENABLED )
  116. #include "replay/ienginereplay.h"
  117. #endif
  118. #if defined( CTFPlayer )
  119. #undef CTFPlayer
  120. #endif
  121. #include "materialsystem/imesh.h" //for materials->FindMaterial
  122. #include "iviewrender.h" //for view->
  123. // NVNT haptics system interface
  124. #include "c_tf_haptics.h"
  125. // memdbgon must be the last include file in a .cpp file!!!
  126. #include "tier0/memdbgon.h"
  127. using namespace GCSDK;
  128. static_assert( TEAM_UNASSIGNED == 0, "If this assert fires, update the assert and the enum in ctexturecompositor.cpp which specifies team colors" );
  129. static_assert( TF_TEAM_RED == 2, "If this assert fires, update the assert and the enum in ctexturecompositor.cpp which specifies team colors" );
  130. static_assert( TF_TEAM_BLUE == 3, "If this assert fires, update the assert and the enum in ctexturecompositor.cpp which specifies team colors" );
  131. // Forward decl
  132. CEconItemView *GetEconItemViewFromProxyEntity( void *pEntity );
  133. C_TFPlayer *GetOwnerFromProxyEntity( void *pEntity );
  134. // --------------------------------------------------------------------------------
  135. // Local Convar Helper Function
  136. // --------------------------------------------------------------------------------
  137. void VisionMode_ChangeCallback( IConVar *pConVar, char const *pOldString, float flOldValue )
  138. {
  139. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  140. if ( pLocalPlayer )
  141. {
  142. pLocalPlayer->CalculateVisionUsingCurrentFlags();
  143. }
  144. }
  145. #ifdef _DEBUG
  146. CON_COMMAND_F ( tf_test_bomb, "Test halloween bomb", 0 )
  147. {
  148. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  149. if ( !pLocalPlayer )
  150. return;
  151. C_TFPlayer *pPlayer = ToTFPlayer( pLocalPlayer );
  152. if ( !pPlayer )
  153. return;
  154. pPlayer->CreateBombonomiconHint();
  155. }
  156. ConVar test_vision_off( "test_vision_off", "0", FCVAR_NONE, "Force vision modes off!", VisionMode_ChangeCallback );
  157. ConVar test_pyrovision( "test_pyrovision", "0", FCVAR_NONE, "Force Pyrovision on!", VisionMode_ChangeCallback );
  158. ConVar test_romevision( "test_romevision", "0", FCVAR_NONE, "Force Romevision on!", VisionMode_ChangeCallback );
  159. ConVar test_halloweenvision( "test_halloweenvision", "0", FCVAR_NONE, "Force halloween vision on!", VisionMode_ChangeCallback );
  160. #endif
  161. // These are all permanently STAGING_ONLY
  162. #ifdef STAGING_ONLY
  163. ConVar tf_paint_kit_disable( "tf_paint_kit_disable", "0", FCVAR_NONE, "Set to disable using paintkits completely." );
  164. ConVar tf_paint_kit_team_override( "tf_paint_kit_team_override", "-1", FCVAR_NONE, "Set to -1:disabled, 0:TF_TEAM_RED, 1:TF_TEAM_BLUE" );
  165. ConVar tf_paint_kit_seed_override( "tf_paint_kit_seed_override", "0", FCVAR_NONE, "Set to none-0 will force composite weapon skin to use this as random seed number." );
  166. ConVar tf_paint_kit_force_regen( "tf_paint_kit_force_regen", "0", FCVAR_NONE, "Set to 1 to force regen after every generation. (dev only)" );
  167. ConVar tf_paint_kit_generating_icons( "tf_paint_kit_generating_icons", "0", FCVAR_NONE, "Only Set by the Icon Generating System." );
  168. extern ConVar tf_paint_kit_force_wear;
  169. KeyValues *s_kvOverridePaintkit = NULL;
  170. bool s_bIsPaintkitOverrideSet = false;
  171. void LoadPaintKit( const char* pszFile )
  172. {
  173. //scripts\items\unencrypted\paintkits
  174. static char pszPaintkitFile[MAX_PATH] = { '\0' };
  175. if ( pszFile )
  176. {
  177. V_sprintf_safe( pszPaintkitFile, "scripts/items/unencrypted/paintkits/%s.paintkit", pszFile );
  178. }
  179. if ( pszPaintkitFile[0] == '\0' )
  180. return;
  181. // Make sure we have a file system
  182. if ( !g_pFullFileSystem || !g_pFullFileSystem->FileExists( pszPaintkitFile ) )
  183. {
  184. Msg( "Unable to load %s from File System \n", pszPaintkitFile );
  185. return;
  186. }
  187. if ( s_kvOverridePaintkit )
  188. {
  189. s_kvOverridePaintkit->deleteThis();
  190. s_kvOverridePaintkit = NULL;
  191. }
  192. // Parse the KeyValues
  193. s_kvOverridePaintkit = new KeyValues( "paintkit" );
  194. if ( !s_kvOverridePaintkit->LoadFromFile( g_pFullFileSystem, pszPaintkitFile ) )
  195. {
  196. Msg( "Unable to read KeyValues from %s \n", pszPaintkitFile );
  197. return;
  198. }
  199. HandleKeyValuesMacros( s_kvOverridePaintkit );
  200. Msg( "Setting %s as Paintkit override.\nCall with no args to disable\ntf_paint_kit_force_regen set to 1\n", pszPaintkitFile );
  201. s_bIsPaintkitOverrideSet = true;
  202. }
  203. CON_COMMAND_F( tf_paint_kit_override_file, "Set a override paintkit file", FCVAR_NONE )
  204. {
  205. // Enable force regen
  206. tf_paint_kit_force_regen.SetValue( 1 );
  207. s_bIsPaintkitOverrideSet = false;
  208. // Take the con var
  209. if ( args.ArgC() <= 1 )
  210. {
  211. Msg( "No paintkit file specified, turning override off.\nYou may want to set tf_paint_kit_force_regen to 0\n" );
  212. return;
  213. }
  214. const char *pszFile = args.Arg( 1 );
  215. LoadPaintKit( pszFile );
  216. }
  217. CON_COMMAND( tf_paint_kit_override_refresh, "Refresh the current override paint kit file" )
  218. {
  219. if ( !s_bIsPaintkitOverrideSet )
  220. return;
  221. LoadPaintKit( NULL );
  222. }
  223. #endif
  224. ConVar tf_playergib_forceup( "tf_playersgib_forceup", "1.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Upward added velocity for gibs." );
  225. ConVar tf_playergib_force( "tf_playersgib_force", "500.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Gibs force." );
  226. ConVar tf_playergib_maxspeed( "tf_playergib_maxspeed", "400", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Max gib speed." );
  227. ConVar tf_always_deathanim( "tf_always_deathanim", "0", FCVAR_CHEAT, "Force death anims to always play." );
  228. ConVar tf_clientsideeye_lookats( "tf_clientsideeye_lookats", "1", FCVAR_NONE, "When on, players will turn their pupils to look at nearby players." );
  229. extern ConVar tf_halloween_kart_boost_recharge;
  230. extern ConVar tf_halloween_kart_boost_duration;
  231. extern ConVar cl_thirdperson;
  232. #ifdef STAGING_ONLY
  233. ConVar test_local_player_skin_override( "test_local_player_skin_override", "-1", FCVAR_CHEAT, "If >= 0, force this skin index to be used by the local player model." );
  234. ConVar tf_test_setkillcount( "tf_test_setkillcount", "0", FCVAR_NONE, "Force a kill streak count for eye glow testing" );
  235. extern ConVar tf_random_item_min;
  236. extern ConVar tf_random_item_max;
  237. extern ConVar tf_killstreak_eyeglow;
  238. extern ConVar tf_killstreak_color;
  239. extern ConVar tf_eyeglow_wip;
  240. ConVar tf_sheen_all( "tf_sheen_all", "0", FCVAR_NONE, "When on, enables sheens on all weapons" );
  241. ConVar tf_sheen_fast( "tf_sheen_fast", "0", FCVAR_NONE, "When on, no delay's between sheens" );
  242. ConVar tf_sheen_shader_override( "tf_sheen_shader_override", "0", FCVAR_NONE, "Set for shader effect override" );
  243. ConVar tf_sheen_color_override_r( "tf_sheen_color_override_r", "0", FCVAR_HIDDEN, "Set by ConCommand tf_sheen_color_override" );
  244. ConVar tf_sheen_color_override_g( "tf_sheen_color_override_g", "0", FCVAR_HIDDEN, "Set by ConCommand tf_sheen_color_override" );
  245. ConVar tf_sheen_color_override_b( "tf_sheen_color_override_b", "0", FCVAR_HIDDEN, "Set by ConCommand tf_sheen_color_override" );
  246. ConVar tf_sheen_color_override_a( "tf_sheen_color_override_a", "0", FCVAR_HIDDEN, "Set by ConCommand tf_sheen_color_override" );
  247. CON_COMMAND_F ( tf_sheen_color_override, "Sheen Color Override RGBA 0-255", 0 )
  248. {
  249. if ( args.ArgC() < 5 )
  250. {
  251. Msg( "requires 4 inputs, r g b a (in 0 - 255 range) \n" );
  252. return;
  253. }
  254. float r = atof( args.Arg(1) ) / 255.0f;
  255. float g = atof( args.Arg(2) ) / 255.0f;
  256. float b = atof( args.Arg(3) ) / 255.0f;
  257. float a = atof( args.Arg(4) ) / 255.0f;
  258. tf_sheen_color_override_r.SetValue( r );
  259. tf_sheen_color_override_g.SetValue( g );
  260. tf_sheen_color_override_b.SetValue( b );
  261. tf_sheen_color_override_a.SetValue( a );
  262. }
  263. ConVar cl_taunt_max_turn_speed( "cl_taunt_max_turn_speed", "0", FCVAR_HIDDEN | FCVAR_CHEAT );
  264. ConVar cl_halloween_kart_turn_speed( "cl_halloween_kart_turn_speed", "100" );
  265. #endif // Staging only
  266. ConVar tf_sheen_framerate( "tf_sheen_framerate", "25", FCVAR_NONE | FCVAR_HIDDEN, "Set Sheen Frame Rate" );
  267. extern ConVar tf_killstreak_alwayson;
  268. ConVar tf_sheen_alpha_firstperson( "tf_sheen_alpha_firstperson", "0.1", FCVAR_NONE, "Set the Alpha Value for first person sheens" );
  269. ConVar tf_killstreakeyes_minkills( "tf_killstreakeyes_minkills", "5", FCVAR_DEVELOPMENTONLY, "min kills to get base eyeglow" );
  270. ConVar tf_killstreakeyes_maxkills( "tf_killstreakeyes_maxkills", "10", FCVAR_DEVELOPMENTONLY, "kills to get the max eye glow effect" );
  271. //ConVar spectate_random_server_basetime( "spectate_random_server_basetime", "240", FCVAR_DEVELOPMENTONLY );
  272. ConVar cl_autorezoom( "cl_autorezoom", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "When set to 1, sniper rifle will re-zoom after firing a zoomed shot." );
  273. ConVar tf_remember_activeweapon( "tf_remember_activeweapon", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE | FCVAR_USERINFO, "Setting this to 1 will make the active weapon persist between lives." );
  274. ConVar tf_remember_lastswitched( "tf_remember_lastswitched", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE | FCVAR_USERINFO, "Setting this to 1 will make the 'last weapon' persist between lives." );
  275. ConVar cl_autoreload( "cl_autoreload", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "When set to 1, clip-using weapons will automatically be reloaded whenever they're not being fired." );
  276. ConVar tf_respawn_on_loadoutchanges( "tf_respawn_on_loadoutchanges", "1", FCVAR_ARCHIVE, "When set to 1, you will automatically respawn whenever you change loadouts inside a respawn zone." );
  277. ConVar sb_dontshow_maxplayer_warning( "sb_dontshow_maxplayer_warning", "0", FCVAR_ARCHIVE );
  278. ConVar sb_close_browser_on_connect( "sb_close_browser_on_connect", "1", FCVAR_ARCHIVE );
  279. ConVar tf_spectate_pyrovision( "tf_spectate_pyrovision", "0", FCVAR_ARCHIVE, "When on, spectator will see the world with Pyrovision active", VisionMode_ChangeCallback );
  280. ConVar tf_replay_pyrovision( "tf_replay_pyrovision", "0", FCVAR_ARCHIVE, "When on, replays will be seen with Pyrovision active", VisionMode_ChangeCallback );
  281. ConVar tf_taunt_first_person( "tf_taunt_first_person", "0", FCVAR_NONE, "1 = taunts remain first-person" );
  282. ConVar tf_romevision_opt_in( "tf_romevision_opt_in", "0", FCVAR_ARCHIVE, "Enable Romevision in Mann vs. Machine mode when available." );
  283. ConVar tf_romevision_skip_prompt( "tf_romevision_skip_prompt", "0", FCVAR_ARCHIVE, "If nonzero, skip the prompt about sharing Romevision." );
  284. #ifdef STAGING_ONLY
  285. extern ConVar tf_bountymode_showhealth;
  286. #endif // STAGING_ONLY
  287. #define BDAY_HAT_MODEL "models/effects/bday_hat.mdl"
  288. #define BOMB_HAT_MODEL "models/props_lakeside_event/bomb_temp_hat.mdl"
  289. #define BOMBONOMICON_MODEL "models/props_halloween/bombonomicon.mdl"
  290. IMaterial *g_pHeadLabelMaterial[2] = { NULL, NULL };
  291. void SetupHeadLabelMaterials( void );
  292. extern CBaseEntity *BreakModelCreateSingle( CBaseEntity *pOwner, breakmodel_t *pModel, const Vector &position,
  293. const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, int nSkin, const breakablepropparams_t &params );
  294. extern int EconWear_ToIntCategory( float flWear );
  295. const char *g_pszHeadGibs[] =
  296. {
  297. "",
  298. "models/player\\gibs\\scoutgib007.mdl",
  299. "models/player\\gibs\\snipergib005.mdl",
  300. "models/player\\gibs\\soldiergib007.mdl",
  301. "models/player\\gibs\\demogib006.mdl",
  302. "models/player\\gibs\\medicgib007.mdl",
  303. "models/player\\gibs\\heavygib007.mdl",
  304. "models/player\\gibs\\pyrogib008.mdl",
  305. "models/player\\gibs\\spygib007.mdl",
  306. "models/player\\gibs\\engineergib006.mdl",
  307. };
  308. const char *g_pszBotHeadGibs[] =
  309. {
  310. "",
  311. "models/bots\\gibs\\scoutbot_gib_head.mdl",
  312. "models/bots\\gibs\\sniperbot_gib_head.mdl",
  313. "models/bots\\gibs\\soldierbot_gib_head.mdl",
  314. "models/bots\\gibs\\demobot_gib_head.mdl",
  315. "models/bots\\gibs\\medicbot_gib_head.mdl",
  316. "models/bots\\gibs\\heavybot_gib_head.mdl",
  317. "models/bots\\gibs\\pyrobot_gib_head.mdl",
  318. "models/bots\\gibs\\spybot_gib_head.mdl",
  319. "models/bots\\gibs\\engineerbot_gib_head.mdl",
  320. };
  321. const char *pszHeadLabelNames[] =
  322. {
  323. "effects/speech_voice_red",
  324. "effects/speech_voice_blue"
  325. };
  326. extern SkyBoxMaterials_t s_PyroSkyboxMaterials;
  327. #define TF_PLAYER_HEAD_LABEL_RED 0
  328. #define TF_PLAYER_HEAD_LABEL_BLUE 1
  329. CLIENTEFFECT_REGISTER_BEGIN( PrecacheInvuln )
  330. CLIENTEFFECT_MATERIAL( "models/effects/invulnfx_blue.vmt" )
  331. CLIENTEFFECT_MATERIAL( "models/effects/invulnfx_red.vmt" )
  332. CLIENTEFFECT_REGISTER_END()
  333. // *********************************************************************************************************
  334. // KillStreak Effect Data
  335. // *********************************************************************************************************
  336. struct killstreak_params_t
  337. {
  338. killstreak_params_t ( int iShaderIndex, const char *pTexture, bool bHasTeamColor,
  339. int sheen_r, int sheen_g, int sheen_b, int sheen_a,
  340. int color1_r, int color1_g, int color1_b, int color1_a,
  341. int color2_r, int color2_g, int color2_b, int color2_a
  342. ) {
  343. m_iShaderIndex = iShaderIndex;
  344. m_pTexture = pTexture;
  345. m_bHasTeamColor = bHasTeamColor;
  346. m_sheen_r = (float)sheen_r / 255.0f;
  347. m_sheen_g = (float)sheen_g / 255.0f;
  348. m_sheen_b = (float)sheen_b / 255.0f;
  349. m_sheen_a = (float)sheen_a / 255.0f;
  350. m_color1_r = (float)color1_r / 255.0f;
  351. m_color1_g = (float)color1_g / 255.0f;
  352. m_color1_b = (float)color1_b / 255.0f;
  353. m_color1_a = (float)color1_a / 255.0f;
  354. m_color2_r = (float)color2_r / 255.0f;
  355. m_color2_g = (float)color2_g / 255.0f;
  356. m_color2_b = (float)color2_b / 255.0f;
  357. m_color2_a = (float)color2_a / 255.0f;
  358. }
  359. int m_iShaderIndex;
  360. bool m_bHasTeamColor;
  361. const char *m_pTexture;
  362. float m_sheen_r, m_sheen_g, m_sheen_b, m_sheen_a;
  363. float m_color1_r, m_color1_g, m_color1_b, m_color1_a;
  364. float m_color2_r, m_color2_g, m_color2_b, m_color2_a;
  365. };
  366. // *********************************************************************************************************
  367. // Base Sheen Colors (Team Red)
  368. static const killstreak_params_t g_KillStreakEffectsBase[] =
  369. {
  370. killstreak_params_t( 0, NULL, false, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0 ), // Empty (Index 0)
  371. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", true, 200, 20, 15, 255, 255, 118, 118, 255, 255, 35, 28, 255 ),
  372. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 242, 172, 10, 255, 255, 237, 138, 255, 255, 213, 65, 255 ),
  373. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 255, 75, 5, 255, 255, 111, 5, 255, 255, 137, 31, 255 ),
  374. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 100, 255, 10, 255, 230, 255, 60, 255, 193, 255, 61, 255 ),
  375. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 40, 255, 70, 255, 103, 255, 121, 255, 165, 255, 193, 255 ),
  376. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 105, 20, 255, 255, 105, 20, 255, 255, 185, 145, 255, 255 ),
  377. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", false, 255, 30, 255, 255, 255, 120, 255, 255, 255, 176, 217, 255 ),
  378. };
  379. // *********************************************************************************************************
  380. // Optional Team Color
  381. static const killstreak_params_t g_KillStreakEffectsBlue[] =
  382. {
  383. killstreak_params_t( 0, NULL, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // Empty (Index 0)
  384. killstreak_params_t( 0, "Effects/AnimatedSheen/animatedsheen0", true, 40, 98, 200, 255, 0, 92, 255, 255, 134, 203, 243, 255 ),
  385. };
  386. // thirdperson medieval
  387. static ConVar tf_medieval_thirdperson( "tf_medieval_thirdperson", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE , "Turns on third-person camera in medieval mode." );
  388. static ConVar tf_medieval_cam_idealdist( "tf_medieval_cam_idealdist", "125", FCVAR_CLIENTDLL | FCVAR_CHEAT ); // thirdperson distance
  389. static ConVar tf_medieval_cam_idealdistright( "tf_medieval_cam_idealdistright", "25", FCVAR_CLIENTDLL | FCVAR_CHEAT ); // thirdperson distance
  390. static ConVar tf_medieval_cam_idealdistup( "tf_medieval_cam_idealdistup", "-10", FCVAR_CLIENTDLL | FCVAR_CHEAT ); // thirdperson distance
  391. static ConVar tf_medieval_cam_idealpitch( "tf_medieval_cam_idealpitch", "0", FCVAR_CLIENTDLL | FCVAR_CHEAT ); // thirdperson pitch
  392. extern ConVar cam_idealpitch;
  393. extern ConVar tf_allow_taunt_switch;
  394. static void PromptAcceptReviveCallback( bool bCancel, void *pContext )
  395. {
  396. if ( bCancel )
  397. {
  398. KeyValues *kv = new KeyValues( "MVM_Revive_Response" );
  399. kv->SetBool( "accepted", false );
  400. engine->ServerCmdKeyValues( kv );
  401. }
  402. }
  403. C_TFPlayerPreviewEffect g_PlayerPreviewEffect;
  404. C_TFPlayerPreviewEffect::C_TFPlayerPreviewEffect() :
  405. m_nPreviewEffect(PREVIEW_EFFECT_NONE)
  406. , m_nTeam(-1)
  407. {
  408. }
  409. void C_TFPlayerPreviewEffect::SetTeam(int nTeam)
  410. {
  411. if ( nTeam == m_nTeam )
  412. return;
  413. m_nTeam = nTeam;
  414. const char *pszMaterial = NULL;
  415. switch ( m_nTeam )
  416. {
  417. case TF_TEAM_BLUE:
  418. pszMaterial = "models/effects/invulnfx_blue.vmt";
  419. break;
  420. case TF_TEAM_RED:
  421. pszMaterial = "models/effects/invulnfx_red.vmt";
  422. break;
  423. default:
  424. break;
  425. }
  426. if ( pszMaterial )
  427. {
  428. m_InvulnerableMaterial.Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS );
  429. }
  430. else
  431. {
  432. m_InvulnerableMaterial.Shutdown();
  433. }
  434. }
  435. void C_TFPlayerPreviewEffect::Reset()
  436. {
  437. SetEffect(PREVIEW_EFFECT_NONE);
  438. SetTeam(-1);
  439. }
  440. C_EntityDissolve *DissolveEffect( C_BaseEntity *pTarget, float flTime );
  441. void SetAppropriateCamera( C_TFPlayer *pPlayer )
  442. {
  443. if ( pPlayer->IsLocalPlayer() == false )
  444. return;
  445. if ( TFGameRules() &&
  446. ( ( TFGameRules()->IsInMedievalMode() && tf_medieval_thirdperson.GetBool() )
  447. || pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ) )
  448. {
  449. g_ThirdPersonManager.SetForcedThirdPerson( true );
  450. Vector offset( tf_medieval_cam_idealdist.GetFloat(), tf_medieval_cam_idealdistright.GetFloat(), tf_medieval_cam_idealdistup.GetFloat() );
  451. g_ThirdPersonManager.SetDesiredCameraOffset( offset );
  452. cam_idealpitch.SetValue( tf_medieval_cam_idealpitch.GetFloat() );
  453. ::input->CAM_ToThirdPerson();
  454. pPlayer->ThirdPersonSwitch( true );
  455. }
  456. else
  457. {
  458. g_ThirdPersonManager.SetForcedThirdPerson( false );
  459. }
  460. }
  461. // -------------------------------------------------------------------------------- //
  462. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  463. // -------------------------------------------------------------------------------- //
  464. class C_TEPlayerAnimEvent : public C_BaseTempEntity
  465. {
  466. public:
  467. DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
  468. DECLARE_CLIENTCLASS();
  469. virtual void PostDataUpdate( DataUpdateType_t updateType )
  470. {
  471. VPROF( "C_TEPlayerAnimEvent::PostDataUpdate" );
  472. // Create the effect.
  473. if ( m_iPlayerIndex == TF_PLAYER_INDEX_NONE )
  474. return;
  475. EHANDLE hPlayer = cl_entitylist->GetNetworkableHandle( m_iPlayerIndex );
  476. if ( !hPlayer )
  477. return;
  478. C_TFPlayer *pPlayer = dynamic_cast< C_TFPlayer* >( hPlayer.Get() );
  479. if ( pPlayer && !pPlayer->IsDormant() )
  480. {
  481. // Ignore anim events that are also played on the client.
  482. PlayerAnimEvent_t animEvent = (PlayerAnimEvent_t) m_iEvent.Get();
  483. switch ( animEvent )
  484. {
  485. case PLAYERANIMEVENT_STUN_BEGIN:
  486. case PLAYERANIMEVENT_STUN_MIDDLE:
  487. case PLAYERANIMEVENT_STUN_END:
  488. case PLAYERANIMEVENT_PASSTIME_THROW_BEGIN:
  489. case PLAYERANIMEVENT_PASSTIME_THROW_MIDDLE:
  490. case PLAYERANIMEVENT_PASSTIME_THROW_END:
  491. case PLAYERANIMEVENT_PASSTIME_THROW_CANCEL:
  492. break; // ignore these
  493. default:
  494. pPlayer->DoAnimationEvent( animEvent, m_nData );
  495. break;
  496. };
  497. }
  498. }
  499. public:
  500. CNetworkVar( int, m_iPlayerIndex );
  501. CNetworkVar( int, m_iEvent );
  502. CNetworkVar( int, m_nData );
  503. };
  504. IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
  505. //-----------------------------------------------------------------------------
  506. // Data tables and prediction tables.
  507. //-----------------------------------------------------------------------------
  508. BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  509. RecvPropInt( RECVINFO( m_iPlayerIndex ) ),
  510. RecvPropInt( RECVINFO( m_iEvent ) ),
  511. RecvPropInt( RECVINFO( m_nData ) )
  512. END_RECV_TABLE()
  513. //=============================================================================
  514. //
  515. // Ragdoll
  516. //
  517. // ----------------------------------------------------------------------------- //
  518. // Client ragdoll entity.
  519. // ----------------------------------------------------------------------------- //
  520. ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." );
  521. ConVar cl_ragdoll_fade_time( "cl_ragdoll_fade_time", "15", FCVAR_CLIENTDLL );
  522. ConVar cl_ragdoll_forcefade( "cl_ragdoll_forcefade", "0", FCVAR_CLIENTDLL );
  523. ConVar cl_ragdoll_pronecheck_distance( "cl_ragdoll_pronecheck_distance", "64", FCVAR_GAMEDLL );
  524. class C_TFRagdoll : public C_BaseFlex
  525. {
  526. public:
  527. DECLARE_CLASS( C_TFRagdoll, C_BaseFlex );
  528. DECLARE_CLIENTCLASS();
  529. C_TFRagdoll();
  530. ~C_TFRagdoll();
  531. virtual void OnDataChanged( DataUpdateType_t type );
  532. IRagdoll* GetIRagdoll() const;
  533. void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
  534. void ClientThink( void );
  535. // Deal with recording
  536. virtual void GetToolRecordingState( KeyValues *msg );
  537. void StartFadeOut( float fDelay );
  538. void EndFadeOut();
  539. void DissolveEntity( CBaseEntity* pEnt );
  540. EHANDLE GetPlayerHandle( void )
  541. {
  542. if ( m_iPlayerIndex == TF_PLAYER_INDEX_NONE )
  543. return NULL;
  544. return cl_entitylist->GetNetworkableHandle( m_iPlayerIndex );
  545. }
  546. bool IsRagdollVisible();
  547. float GetBurnStartTime() { return m_flBurnEffectStartTime; }
  548. virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed );
  549. virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
  550. bool IsDeathAnim() { return m_bDeathAnim; }
  551. int GetDamageCustom() { return m_iDamageCustom; }
  552. virtual bool GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld );
  553. int GetClass() { return m_iClass; }
  554. float GetPercentInvisible( void ) { return m_flPercentInvisible; }
  555. bool IsCloaked( void ) { return m_bCloaked; }
  556. int GetRagdollTeam( void ){ return m_iTeam; }
  557. float GetHeadScale() const { return m_flHeadScale; }
  558. float GetTorsoScale() const { return m_flTorsoScale; }
  559. float GetHandScale() const { return m_flHandScale; }
  560. private:
  561. C_TFRagdoll( const C_TFRagdoll & ) {}
  562. void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
  563. void CreateTFRagdoll();
  564. void CreateTFGibs( bool bDestroyRagdoll = true, bool bCurrentPosition = false );
  565. void CreateWearableGibs( bool bDisguiseWearables );
  566. void CreateTFHeadGib();
  567. virtual float FrameAdvance( float flInterval );
  568. bool IsDecapitation();
  569. bool IsHeadSmash();
  570. virtual int InternalDrawModel( int flags );
  571. private:
  572. CNetworkVector( m_vecRagdollVelocity );
  573. CNetworkVector( m_vecRagdollOrigin );
  574. int m_iPlayerIndex;
  575. float m_fDeathTime;
  576. bool m_bFadingOut;
  577. bool m_bGib;
  578. bool m_bBurning;
  579. bool m_bElectrocuted;
  580. bool m_bBatted;
  581. bool m_bDissolving;
  582. bool m_bFeignDeath;
  583. bool m_bWasDisguised;
  584. bool m_bCloaked;
  585. bool m_bBecomeAsh;
  586. int m_iDamageCustom;
  587. bool m_bGoldRagdoll;
  588. bool m_bIceRagdoll;
  589. CountdownTimer m_freezeTimer;
  590. CountdownTimer m_frozenTimer;
  591. int m_iTeam;
  592. int m_iClass;
  593. float m_flBurnEffectStartTime; // start time of burning, or 0 if not burning
  594. bool m_bRagdollOn;
  595. bool m_bDeathAnim;
  596. bool m_bOnGround;
  597. bool m_bFixedConstraints;
  598. matrix3x4_t m_mHeadAttachment;
  599. bool m_bBaseTransform;
  600. float m_flPercentInvisible;
  601. float m_flTimeToDissolve;
  602. bool m_bCritOnHardHit; // plays the red mist particle effect
  603. float m_flHeadScale;
  604. float m_flTorsoScale;
  605. float m_flHandScale;
  606. CMaterialReference m_MaterialOverride;
  607. CUtlVector<CHandle<CEconWearable > > m_hRagWearables; // These look like they are no longer used?
  608. CUtlVector< CHandle< CEconWearable > > m_hClientWearables; // wearables on the ragdoll that are "following" it
  609. bool m_bCreatedWhilePlaybackSkipping;
  610. };
  611. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_TFRagdoll, DT_TFRagdoll, CTFRagdoll )
  612. RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
  613. RecvPropInt( RECVINFO( m_iPlayerIndex ) ),
  614. RecvPropVector( RECVINFO(m_vecForce) ),
  615. RecvPropVector( RECVINFO(m_vecRagdollVelocity) ),
  616. RecvPropInt( RECVINFO( m_nForceBone ) ),
  617. RecvPropBool( RECVINFO( m_bGib ) ),
  618. RecvPropBool( RECVINFO( m_bBurning ) ),
  619. RecvPropBool( RECVINFO( m_bElectrocuted ) ),
  620. RecvPropBool( RECVINFO( m_bFeignDeath ) ),
  621. RecvPropBool( RECVINFO( m_bWasDisguised ) ),
  622. RecvPropBool( RECVINFO( m_bOnGround ) ),
  623. RecvPropBool( RECVINFO( m_bCloaked ) ),
  624. RecvPropBool( RECVINFO( m_bBecomeAsh ) ),
  625. RecvPropInt( RECVINFO( m_iDamageCustom ) ),
  626. RecvPropInt( RECVINFO( m_iTeam ) ),
  627. RecvPropInt( RECVINFO( m_iClass ) ),
  628. RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hRagWearables ), 8, RecvPropEHandle(NULL, 0, 0) ),
  629. RecvPropBool( RECVINFO( m_bGoldRagdoll ) ),
  630. RecvPropBool( RECVINFO( m_bIceRagdoll ) ),
  631. RecvPropBool( RECVINFO( m_bCritOnHardHit ) ),
  632. RecvPropFloat( RECVINFO( m_flHeadScale ) ),
  633. RecvPropFloat( RECVINFO( m_flTorsoScale ) ),
  634. RecvPropFloat( RECVINFO( m_flHandScale ) ),
  635. END_RECV_TABLE()
  636. //-----------------------------------------------------------------------------
  637. // Purpose:
  638. // Input : -
  639. //-----------------------------------------------------------------------------
  640. C_TFRagdoll::C_TFRagdoll()
  641. {
  642. m_iPlayerIndex = TF_PLAYER_INDEX_NONE;
  643. m_fDeathTime = -1;
  644. m_bFadingOut = false;
  645. m_bGib = false;
  646. m_bBurning = false;
  647. m_bElectrocuted = false;
  648. m_bBatted = false;
  649. m_bDissolving = false;
  650. m_bFeignDeath = false;
  651. m_bWasDisguised = false;
  652. m_bCloaked = false;
  653. m_bBecomeAsh = false;
  654. m_flBurnEffectStartTime = 0.0f;
  655. m_iDamageCustom = 0;
  656. m_bGoldRagdoll = false;
  657. m_bIceRagdoll = false;
  658. m_freezeTimer.Invalidate();
  659. m_frozenTimer.Invalidate();
  660. m_iTeam = -1;
  661. m_iClass = -1;
  662. m_nForceBone = -1;
  663. m_bRagdollOn = false;
  664. m_bDeathAnim = false;
  665. m_bOnGround = false;
  666. m_bBaseTransform = false;
  667. m_bFixedConstraints = false;
  668. m_flTimeToDissolve = 0.3f;
  669. m_bCritOnHardHit = false;
  670. m_flHeadScale = 1.f;
  671. m_flTorsoScale = 1.f;
  672. m_flHandScale = 1.f;
  673. UseClientSideAnimation();
  674. m_bCreatedWhilePlaybackSkipping = engine->IsSkippingPlayback();
  675. }
  676. //-----------------------------------------------------------------------------
  677. // Purpose:
  678. // Input : -
  679. //-----------------------------------------------------------------------------
  680. C_TFRagdoll::~C_TFRagdoll()
  681. {
  682. PhysCleanupFrictionSounds( this );
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Purpose:
  686. // Input : *pSourceEntity -
  687. //-----------------------------------------------------------------------------
  688. void C_TFRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
  689. {
  690. if ( !pSourceEntity )
  691. return;
  692. VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
  693. VarMapping_t *pDest = GetVarMapping();
  694. // Find all the VarMapEntry_t's that represent the same variable.
  695. for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
  696. {
  697. VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
  698. for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
  699. {
  700. VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
  701. if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pDestEntry->watcher->GetDebugName() ) )
  702. {
  703. pDestEntry->watcher->Copy( pSrcEntry->watcher );
  704. break;
  705. }
  706. }
  707. }
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose: Setup vertex weights for drawing
  711. //-----------------------------------------------------------------------------
  712. void C_TFRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
  713. {
  714. // While we're dying, we want to mimic the facial animation of the player. Once they're dead, we just stay as we are.
  715. EHANDLE hPlayer = GetPlayerHandle();
  716. if ( ( hPlayer && hPlayer->IsAlive()) || !hPlayer )
  717. {
  718. BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
  719. }
  720. else if ( hPlayer )
  721. {
  722. hPlayer->SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
  723. }
  724. }
  725. //-----------------------------------------------------------------------------
  726. // Purpose:
  727. // Input : *pTrace -
  728. // iDamageType -
  729. // *pCustomImpactName -
  730. //-----------------------------------------------------------------------------
  731. void C_TFRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  732. {
  733. VPROF( "C_TFRagdoll::ImpactTrace" );
  734. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  735. if( !pPhysicsObject )
  736. return;
  737. Vector vecDir;
  738. VectorSubtract( pTrace->endpos, pTrace->startpos, vecDir );
  739. if ( iDamageType == DMG_BLAST )
  740. {
  741. // Adjust the impact strength and apply the force at the center of mass.
  742. vecDir *= 4000;
  743. pPhysicsObject->ApplyForceCenter( vecDir );
  744. }
  745. else
  746. {
  747. // Find the apporx. impact point.
  748. Vector vecHitPos;
  749. VectorMA( pTrace->startpos, pTrace->fraction, vecDir, vecHitPos );
  750. VectorNormalize( vecDir );
  751. // Adjust the impact strength and apply the force at the impact point..
  752. vecDir *= 4000;
  753. pPhysicsObject->ApplyForceOffset( vecDir, vecHitPos );
  754. }
  755. m_pRagdoll->ResetRagdollSleepAfterTime();
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose:
  759. // Input : -
  760. //-----------------------------------------------------------------------------
  761. void C_TFRagdoll::CreateTFRagdoll()
  762. {
  763. // Get the player.
  764. C_TFPlayer *pPlayer = NULL;
  765. EHANDLE hPlayer = GetPlayerHandle();
  766. if ( hPlayer )
  767. {
  768. pPlayer = dynamic_cast<C_TFPlayer*>( hPlayer.Get() );
  769. }
  770. int nModelIndex = -1;
  771. if ( pPlayer && pPlayer->GetPlayerClass() && !pPlayer->ShouldDrawSpyAsDisguised() )
  772. {
  773. nModelIndex = modelinfo->GetModelIndex( pPlayer->GetPlayerClass()->GetModelName() );
  774. }
  775. else
  776. {
  777. TFPlayerClassData_t *pData = GetPlayerClassData( m_iClass );
  778. if ( pData )
  779. {
  780. nModelIndex = modelinfo->GetModelIndex( pData->GetModelName() );
  781. }
  782. }
  783. if ( pPlayer )
  784. {
  785. m_flHeadScale = pPlayer->GetHeadScale();
  786. m_flTorsoScale = pPlayer->GetTorsoScale();
  787. m_flHandScale = pPlayer->GetHandScale();
  788. }
  789. if ( nModelIndex != -1 )
  790. {
  791. SetModelIndex( nModelIndex );
  792. if ( m_iTeam == TF_TEAM_RED )
  793. {
  794. m_nSkin = 0;
  795. }
  796. else
  797. {
  798. m_nSkin = 1;
  799. }
  800. }
  801. // Check for any special player skin override behaviour.
  802. if ( pPlayer && pPlayer->BRenderAsZombie() )
  803. {
  804. C_TFPlayer::AdjustSkinIndexForZombie( m_iClass, m_nSkin );
  805. }
  806. // We check against new-style (special flag to indicate goldification) and old style (custom damage type)
  807. // to maintain old demos involving the golden wrench.
  808. if ( m_bGoldRagdoll || m_iDamageCustom == TF_DMG_CUSTOM_GOLD_WRENCH )
  809. {
  810. EmitSound( "Saxxy.TurnGold" );
  811. m_bFixedConstraints = true;
  812. }
  813. if ( m_bIceRagdoll )
  814. {
  815. EmitSound( "Icicle.TurnToIce" );
  816. ParticleProp()->Create( "xms_icicle_impact_dryice", PATTACH_ABSORIGIN_FOLLOW );
  817. m_freezeTimer.Start( RandomFloat( 0.1f, 0.75f ) );
  818. m_frozenTimer.Start( RandomFloat( 9.0f, 11.0f ) );
  819. }
  820. #ifdef _DEBUG
  821. DevMsg( 2, "CreateTFRagdoll %d %d\n", gpGlobals->framecount, pPlayer ? pPlayer->entindex() : 0 );
  822. #endif
  823. if ( pPlayer && !pPlayer->IsDormant() )
  824. {
  825. // Move my current model instance to the ragdoll's so decals are preserved.
  826. pPlayer->SnatchModelInstance( this );
  827. VarMapping_t *varMap = GetVarMapping();
  828. // Copy all the interpolated vars from the player entity.
  829. // The entity uses the interpolated history to get bone velocity.
  830. if ( !pPlayer->IsLocalPlayer() && pPlayer->IsInterpolationEnabled() )
  831. {
  832. Interp_Copy( pPlayer );
  833. SetAbsAngles( pPlayer->GetRenderAngles() );
  834. GetRotationInterpolator().Reset();
  835. m_flAnimTime = pPlayer->m_flAnimTime;
  836. SetSequence( pPlayer->GetSequence() );
  837. m_flPlaybackRate = pPlayer->GetPlaybackRate();
  838. }
  839. else
  840. {
  841. // This is the local player, so set them in a default
  842. // pose and slam their velocity, angles and origin
  843. SetAbsOrigin( /* m_vecRagdollOrigin : */ pPlayer->GetRenderOrigin() );
  844. SetAbsAngles( pPlayer->GetRenderAngles() );
  845. SetAbsVelocity( m_vecRagdollVelocity );
  846. // Hack! Find a neutral standing pose or use the idle.
  847. int iSeq = LookupSequence( "RagdollSpawn" );
  848. if ( iSeq == -1 )
  849. {
  850. Assert( false );
  851. iSeq = 0;
  852. }
  853. SetSequence( iSeq );
  854. SetCycle( 0.0 );
  855. Interp_Reset( varMap );
  856. }
  857. if ( !m_bFeignDeath || m_bWasDisguised )
  858. {
  859. pPlayer->RecalcBodygroupsIfDirty();
  860. m_nBody = pPlayer->GetBody();
  861. }
  862. }
  863. else
  864. {
  865. // Overwrite network origin so later interpolation will use this position.
  866. SetNetworkOrigin( m_vecRagdollOrigin );
  867. SetAbsOrigin( m_vecRagdollOrigin );
  868. SetAbsVelocity( m_vecRagdollVelocity );
  869. Interp_Reset( GetVarMapping() );
  870. }
  871. if ( IsCloaked() )
  872. {
  873. AddEffects( EF_NOSHADOW );
  874. }
  875. // Play a death anim depending on the custom damage type.
  876. bool bPlayDeathInAir = false;
  877. int iDeathSeq = -1;
  878. if ( pPlayer && !m_bGoldRagdoll )
  879. {
  880. iDeathSeq = pPlayer->m_Shared.GetSequenceForDeath( this, m_bBurning, m_iDamageCustom );
  881. if ( m_bDissolving && !m_bGib )
  882. {
  883. bPlayDeathInAir = true;
  884. iDeathSeq = LookupSequence( "dieviolent" );
  885. }
  886. // did we find a death sequence?
  887. if ( iDeathSeq > -1 && (m_iDamageCustom != TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING) &&
  888. (m_iDamageCustom != TF_DMG_CUSTOM_TAUNTATK_ENGINEER_GUITAR_SMASH) && (m_iDamageCustom != TF_DMG_CUSTOM_TAUNTATK_ALLCLASS_GUITAR_RIFF) )
  889. {
  890. // we only want to show the death anims 25% of the time, unless this is a demoman kill taunt
  891. // always play backstab animations for the ice ragdoll
  892. if ( !m_bIceRagdoll && !tf_always_deathanim.GetBool() && (RandomFloat( 0, 1 ) > 0.25f) )
  893. {
  894. iDeathSeq = -1;
  895. }
  896. }
  897. }
  898. bool bPlayDeathAnim = cl_ragdoll_physics_enable.GetBool() && (iDeathSeq > -1) && pPlayer;
  899. if ( !m_bOnGround && bPlayDeathAnim && !bPlayDeathInAir )
  900. bPlayDeathAnim = false; // Don't play most death anims in the air (headshot, etc).
  901. if ( bPlayDeathAnim )
  902. {
  903. // Set our position for a death anim.
  904. SetAbsOrigin( pPlayer->GetRenderOrigin() );
  905. SetAbsAngles( pPlayer->GetRenderAngles() );
  906. SetAbsVelocity( Vector(0,0,0) );
  907. m_vecForce = Vector(0,0,0);
  908. // Play the death anim.
  909. ResetSequence( iDeathSeq );
  910. m_bDeathAnim = true;
  911. }
  912. else if ( m_bIceRagdoll )
  913. {
  914. // couldn't play death anim because we were in midair - go ridig immediately
  915. m_freezeTimer.Invalidate();
  916. m_frozenTimer.Invalidate();
  917. m_bFixedConstraints = true;
  918. }
  919. // Fade out the ragdoll in a while
  920. StartFadeOut( cl_ragdoll_fade_time.GetFloat() );
  921. SetNextClientThink( CLIENT_THINK_ALWAYS );
  922. // Copy over impact attachments.
  923. if ( pPlayer )
  924. {
  925. pPlayer->CreateBoneAttachmentsFromWearables( this, m_bWasDisguised );
  926. pPlayer->MoveBoneAttachments( this );
  927. }
  928. if ( m_iDamageCustom == TF_DMG_CUSTOM_KART )
  929. {
  930. m_vecForce *= 100.0f;
  931. SetAbsVelocity( GetAbsVelocity() + m_vecForce );
  932. ApplyAbsVelocityImpulse( m_vecForce );
  933. }
  934. // Save ragdoll information.
  935. if ( cl_ragdoll_physics_enable.GetBool() && !m_bDeathAnim )
  936. {
  937. // Make us a ragdoll..
  938. m_nRenderFX = kRenderFxRagdoll;
  939. matrix3x4_t boneDelta0[MAXSTUDIOBONES];
  940. matrix3x4_t boneDelta1[MAXSTUDIOBONES];
  941. matrix3x4_t currentBones[MAXSTUDIOBONES];
  942. const float boneDt = 0.05f;
  943. // We have to make sure that we're initting this client ragdoll off of the same model.
  944. // GetRagdollInitBoneArrays uses the *player* Hdr, which may be a different model than
  945. // the ragdoll Hdr, if we try to create a ragdoll in the same frame that the player
  946. // changes their player model.
  947. CStudioHdr *pRagdollHdr = GetModelPtr();
  948. CStudioHdr *pPlayerHdr = pPlayer ? pPlayer->GetModelPtr() : NULL;
  949. bool bChangedModel = false;
  950. if ( pRagdollHdr && pPlayerHdr )
  951. {
  952. bChangedModel = pRagdollHdr->GetVirtualModel() != pPlayerHdr->GetVirtualModel();
  953. // Assert( !bChangedModel && "C_TFRagdoll::CreateTFRagdoll: Trying to create ragdoll with a different model than the player it's based on" );
  954. }
  955. bool bBoneArraysInited;
  956. if ( pPlayer && !pPlayer->IsDormant() && !bChangedModel )
  957. {
  958. bBoneArraysInited = pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  959. }
  960. else
  961. {
  962. bBoneArraysInited = GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  963. }
  964. if ( bBoneArraysInited )
  965. {
  966. InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt, m_bFixedConstraints );
  967. }
  968. }
  969. else
  970. {
  971. ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY );
  972. }
  973. if ( m_bBurning )
  974. {
  975. m_flBurnEffectStartTime = gpGlobals->curtime;
  976. ParticleProp()->Create( "burningplayer_corpse", PATTACH_ABSORIGIN_FOLLOW );
  977. }
  978. if ( m_bElectrocuted )
  979. {
  980. const char *pEffectName = ( m_iTeam == TF_TEAM_RED ) ? "electrocuted_red" : "electrocuted_blue";
  981. ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW );
  982. C_BaseEntity::EmitSound( "TFPlayer.MedicChargedDeath" );
  983. }
  984. if ( m_bBecomeAsh && !m_bDissolving && !m_bGib )
  985. {
  986. ParticleProp()->Create( "drg_fiery_death", PATTACH_ABSORIGIN_FOLLOW );
  987. m_flTimeToDissolve = 0.5f;
  988. }
  989. if ( pPlayer->HasBombinomiconEffectOnDeath() && !m_bGib && !m_bDissolving )
  990. {
  991. m_flTimeToDissolve = 1.2f;
  992. }
  993. // Birthday mode.
  994. if ( pPlayer && TFGameRules() && TFGameRules()->IsBirthday() )
  995. {
  996. AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
  997. breakablepropparams_t breakParams( m_vecRagdollOrigin, GetRenderAngles(), m_vecRagdollVelocity, angularImpulse );
  998. breakParams.impactEnergyScale = 1.0f;
  999. pPlayer->DropPartyHat( breakParams, m_vecRagdollVelocity.GetForModify() );
  1000. }
  1001. const char *materialOverrideFilename = NULL;
  1002. if ( m_bFixedConstraints )
  1003. {
  1004. if ( m_bGoldRagdoll )
  1005. {
  1006. // Gold texture...we've been turned into a golden corpse!
  1007. materialOverrideFilename = "models/player/shared/gold_player.vmt";
  1008. }
  1009. }
  1010. if ( m_bIceRagdoll )
  1011. {
  1012. // Ice texture...we've been turned into an ice statue!
  1013. materialOverrideFilename = "models/player/shared/ice_player.vmt";
  1014. }
  1015. if ( materialOverrideFilename )
  1016. {
  1017. // Ice texture...we've been turned into an ice statue!
  1018. m_MaterialOverride.Init( materialOverrideFilename, TEXTURE_GROUP_CLIENT_EFFECTS );
  1019. // override all of our wearables, too
  1020. for ( C_BaseEntity *pEntity = ClientEntityList().FirstBaseEntity(); pEntity; pEntity = ClientEntityList().NextBaseEntity(pEntity) )
  1021. {
  1022. if ( pEntity->GetFollowedEntity() == this )
  1023. {
  1024. CEconEntity *pItem = dynamic_cast< CEconEntity * >( pEntity );
  1025. if ( pItem )
  1026. {
  1027. pItem->SetMaterialOverride( m_iTeam, materialOverrideFilename );
  1028. }
  1029. }
  1030. }
  1031. }
  1032. }
  1033. float C_TFRagdoll::FrameAdvance( float flInterval )
  1034. {
  1035. // if we're in the process of becoming an ice statue, freeze
  1036. if ( m_freezeTimer.HasStarted() && !m_freezeTimer.IsElapsed() )
  1037. {
  1038. // play the backstab anim until the timer is up
  1039. return BaseClass::FrameAdvance( flInterval );
  1040. }
  1041. if ( m_frozenTimer.HasStarted() )
  1042. {
  1043. if ( m_frozenTimer.IsElapsed() )
  1044. {
  1045. // holding frozen time is up - turn to a stiff ragdoll and fall over
  1046. m_frozenTimer.Invalidate();
  1047. m_nRenderFX = kRenderFxRagdoll;
  1048. matrix3x4_t boneDelta0[MAXSTUDIOBONES];
  1049. matrix3x4_t boneDelta1[MAXSTUDIOBONES];
  1050. matrix3x4_t currentBones[MAXSTUDIOBONES];
  1051. const float boneDt = 0.1f;
  1052. GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  1053. InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt, true );
  1054. SetAbsVelocity( Vector( 0,0,0 ) );
  1055. m_bRagdollOn = true;
  1056. }
  1057. else
  1058. {
  1059. // don't move at all
  1060. return 0.0f;
  1061. }
  1062. }
  1063. float fRes = BaseClass::FrameAdvance( flInterval );
  1064. if ( !m_bRagdollOn && IsSequenceFinished() && m_bDeathAnim )
  1065. {
  1066. m_nRenderFX = kRenderFxRagdoll;
  1067. matrix3x4_t boneDelta0[MAXSTUDIOBONES];
  1068. matrix3x4_t boneDelta1[MAXSTUDIOBONES];
  1069. matrix3x4_t currentBones[MAXSTUDIOBONES];
  1070. const float boneDt = 0.1f;
  1071. if ( !GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ) )
  1072. {
  1073. Warning( "C_TFRagdoll::FrameAdvance GetRagdollInitBoneArrays failed.\n" );
  1074. }
  1075. else
  1076. {
  1077. InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
  1078. }
  1079. SetAbsVelocity( Vector( 0,0,0 ) );
  1080. m_bRagdollOn = true;
  1081. // Make it fade out.
  1082. StartFadeOut( cl_ragdoll_fade_time.GetFloat() );
  1083. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1084. }
  1085. return fRes;
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. // Purpose:
  1089. //-----------------------------------------------------------------------------
  1090. void C_TFRagdoll::CreateTFHeadGib( void )
  1091. {
  1092. C_TFPlayer *pPlayer = NULL;
  1093. EHANDLE hPlayer = GetPlayerHandle();
  1094. if ( hPlayer )
  1095. {
  1096. pPlayer = dynamic_cast<C_TFPlayer*>( hPlayer.Get() );
  1097. }
  1098. if ( pPlayer && ((pPlayer->m_hFirstGib == NULL) || m_bFeignDeath) )
  1099. {
  1100. Vector vecVelocity = m_vecForce + m_vecRagdollVelocity;
  1101. VectorNormalize( vecVelocity );
  1102. pPlayer->CreatePlayerGibs( m_vecRagdollOrigin, vecVelocity, m_vecForce.Length(), m_bBurning, false, true );
  1103. // Decap Death Camera is disorienting on range Decaps (aka bullets)
  1104. // Use normal Deathcam
  1105. if ( m_iDamageCustom == TF_DMG_CUSTOM_HEADSHOT_DECAPITATION )
  1106. {
  1107. pPlayer->m_hHeadGib = NULL;
  1108. }
  1109. }
  1110. }
  1111. //-----------------------------------------------------------------------------
  1112. // Purpose:
  1113. //-----------------------------------------------------------------------------
  1114. void C_TFRagdoll::CreateTFGibs( bool bDestroyRagdoll, bool bCurrentPosition )
  1115. {
  1116. C_TFPlayer *pPlayer = NULL;
  1117. EHANDLE hPlayer = GetPlayerHandle();
  1118. if ( hPlayer )
  1119. {
  1120. pPlayer = dynamic_cast<C_TFPlayer*>( hPlayer.Get() );
  1121. }
  1122. if ( pPlayer && pPlayer->HasBombinomiconEffectOnDeath() )
  1123. {
  1124. m_vecForce *= 2.0f;
  1125. m_vecForce.z *= 3.0f;
  1126. DispatchParticleEffect( TFGameRules()->IsHolidayActive( kHoliday_Halloween ) ? "bombinomicon_burningdebris_halloween" : "bombinomicon_burningdebris",
  1127. bCurrentPosition ? GetAbsOrigin() : m_vecRagdollOrigin, GetAbsAngles() );
  1128. EmitSound( "Bombinomicon.Explode" );
  1129. }
  1130. if ( pPlayer && ((pPlayer->m_hFirstGib == NULL) || m_bFeignDeath) )
  1131. {
  1132. Vector vecVelocity = m_vecForce + m_vecRagdollVelocity;
  1133. VectorNormalize( vecVelocity );
  1134. pPlayer->CreatePlayerGibs( bCurrentPosition ? pPlayer->GetRenderOrigin() : m_vecRagdollOrigin, vecVelocity, m_vecForce.Length(), m_bBurning );
  1135. }
  1136. if ( pPlayer )
  1137. {
  1138. if ( TFGameRules() && TFGameRules()->IsBirthdayOrPyroVision() )
  1139. {
  1140. DispatchParticleEffect( "bday_confetti", pPlayer->GetAbsOrigin() + Vector(0,0,32), vec3_angle );
  1141. if ( TFGameRules() && TFGameRules()->IsBirthday() )
  1142. {
  1143. C_BaseEntity::EmitSound( "Game.HappyBirthday" );
  1144. }
  1145. }
  1146. else if ( m_bCritOnHardHit && !UTIL_IsLowViolence() )
  1147. {
  1148. DispatchParticleEffect( "tfc_sniper_mist", pPlayer->WorldSpaceCenter(), vec3_angle );
  1149. }
  1150. }
  1151. if ( bDestroyRagdoll )
  1152. {
  1153. EndFadeOut();
  1154. }
  1155. else
  1156. {
  1157. SetRenderMode( kRenderNone );
  1158. UpdateVisibility();
  1159. }
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. // Purpose: Separate from CreateTFGibs so we can easily remove it if we don't like it...
  1163. //-----------------------------------------------------------------------------
  1164. void C_TFRagdoll::CreateWearableGibs( bool bDisguiseWearables )
  1165. {
  1166. C_TFPlayer *pPlayer = NULL;
  1167. EHANDLE hPlayer = GetPlayerHandle();
  1168. if ( hPlayer )
  1169. {
  1170. pPlayer = dynamic_cast<C_TFPlayer*>( hPlayer.Get() );
  1171. }
  1172. if ( !pPlayer )
  1173. return;
  1174. Vector vecVelocity = m_vecForce + m_vecRagdollVelocity;
  1175. VectorNormalize( vecVelocity );
  1176. pPlayer->CreatePlayerGibs( m_vecRagdollOrigin, vecVelocity, m_vecForce.Length(), m_bBurning, true, false, bDisguiseWearables );
  1177. }
  1178. //-----------------------------------------------------------------------------
  1179. // Purpose:
  1180. // Input : type -
  1181. //-----------------------------------------------------------------------------
  1182. void C_TFRagdoll::OnDataChanged( DataUpdateType_t type )
  1183. {
  1184. BaseClass::OnDataChanged( type );
  1185. if ( type == DATA_UPDATE_CREATED )
  1186. {
  1187. bool bCreateRagdoll = true;
  1188. // Get the player.
  1189. EHANDLE hPlayer = GetPlayerHandle();
  1190. if ( hPlayer )
  1191. {
  1192. // If we're getting the initial update for this player (e.g., after resetting entities after
  1193. // lots of packet loss, then don't create gibs, ragdolls if the player and it's gib/ragdoll
  1194. // both show up on same frame.
  1195. if ( abs( hPlayer->GetCreationTick() - gpGlobals->tickcount ) < TIME_TO_TICKS( 1.0f ) )
  1196. {
  1197. bCreateRagdoll = false;
  1198. }
  1199. }
  1200. else if ( C_BasePlayer::GetLocalPlayer() )
  1201. {
  1202. // Ditto for recreation of the local player
  1203. if ( abs( C_BasePlayer::GetLocalPlayer()->GetCreationTick() - gpGlobals->tickcount ) < TIME_TO_TICKS( 1.0f ) )
  1204. {
  1205. bCreateRagdoll = false;
  1206. }
  1207. }
  1208. // Prevent replays from creating ragdolls on the first frame of playback after skipping through playback.
  1209. // If a player died (leaving a ragdoll) previous to the first frame of replay playback,
  1210. // their ragdoll wasn't yet initialized because OnDataChanged events are queued but not processed
  1211. // until the first render.
  1212. if ( engine->IsPlayingDemo() )
  1213. {
  1214. bCreateRagdoll = !m_bCreatedWhilePlaybackSkipping;
  1215. }
  1216. if ( GetDamageCustom() == TF_DMG_CUSTOM_TAUNTATK_GRAND_SLAM )
  1217. {
  1218. m_bBatted = true;
  1219. }
  1220. C_TFPlayer *pPlayer = ToTFPlayer( hPlayer.Get() );
  1221. bool bMiniBoss = ( pPlayer && pPlayer->IsMiniBoss() ) ? true : false;
  1222. if ( GetDamageCustom() == TF_DMG_CUSTOM_PLASMA )
  1223. {
  1224. if ( !m_bBecomeAsh && !bMiniBoss )
  1225. {
  1226. m_bDissolving = true;
  1227. }
  1228. m_bGib = false;
  1229. }
  1230. if ( GetDamageCustom() == TF_DMG_CUSTOM_PLASMA_CHARGED )
  1231. {
  1232. if ( !m_bBecomeAsh && !bMiniBoss )
  1233. {
  1234. m_bDissolving = true;
  1235. }
  1236. m_bGib = true;
  1237. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1238. }
  1239. // Don't gib zombies, always just ragdoll
  1240. if ( pPlayer )
  1241. {
  1242. if ( pPlayer->BRenderAsZombie() )
  1243. {
  1244. m_bGib = false;
  1245. }
  1246. pPlayer->UpdateMVMEyeGlowEffect( false );
  1247. }
  1248. if ( bCreateRagdoll )
  1249. {
  1250. if ( m_bGib )
  1251. {
  1252. CreateTFGibs( !m_bDissolving );
  1253. }
  1254. else
  1255. {
  1256. CreateTFRagdoll();
  1257. if ( IsDecapitation() )
  1258. {
  1259. CreateTFHeadGib();
  1260. EmitSound( "TFPlayer.Decapitated" );
  1261. bool bBlood = true;
  1262. if ( TFGameRules() && ( TFGameRules()->UseSillyGibs() ||
  1263. ( TFGameRules()->IsMannVsMachineMode() && hPlayer && hPlayer->GetTeamNumber() == TF_TEAM_PVE_INVADERS ) ) )
  1264. {
  1265. bBlood = false;
  1266. }
  1267. if ( bBlood )
  1268. {
  1269. ParticleProp()->Create( "blood_decap", PATTACH_POINT_FOLLOW, "head" );
  1270. }
  1271. }
  1272. }
  1273. m_bNoModelParticles = true;
  1274. // Drop wearables (hats, etc)
  1275. CreateWearableGibs( m_bWasDisguised );
  1276. }
  1277. }
  1278. else
  1279. {
  1280. if ( !cl_ragdoll_physics_enable.GetBool() )
  1281. {
  1282. // Don't let it set us back to a ragdoll with data from the server.
  1283. m_nRenderFX = kRenderFxNone;
  1284. }
  1285. }
  1286. }
  1287. //-----------------------------------------------------------------------------
  1288. //
  1289. //-----------------------------------------------------------------------------
  1290. int C_TFRagdoll::InternalDrawModel( int flags )
  1291. {
  1292. if ( m_MaterialOverride.IsValid() )
  1293. {
  1294. modelrender->ForcedMaterialOverride( m_MaterialOverride );
  1295. }
  1296. int ret = BaseClass::InternalDrawModel( flags );
  1297. if ( m_MaterialOverride.IsValid() )
  1298. {
  1299. modelrender->ForcedMaterialOverride( NULL );
  1300. }
  1301. return ret;
  1302. }
  1303. //-----------------------------------------------------------------------------
  1304. //
  1305. //-----------------------------------------------------------------------------
  1306. bool C_TFRagdoll::IsDecapitation()
  1307. {
  1308. return (cl_ragdoll_fade_time.GetFloat() > 5.f) &&
  1309. ((m_iDamageCustom == TF_DMG_CUSTOM_DECAPITATION)
  1310. || (m_iDamageCustom == TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING)
  1311. || (m_iDamageCustom == TF_DMG_CUSTOM_DECAPITATION_BOSS)
  1312. || (m_iDamageCustom == TF_DMG_CUSTOM_HEADSHOT_DECAPITATION)
  1313. || (m_iDamageCustom == TF_DMG_CUSTOM_MERASMUS_DECAPITATION) );
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. //
  1317. //-----------------------------------------------------------------------------
  1318. bool C_TFRagdoll::IsHeadSmash()
  1319. {
  1320. return ((cl_ragdoll_fade_time.GetFloat() > 5.f) && (m_iDamageCustom == TF_DMG_CUSTOM_TAUNTATK_ENGINEER_GUITAR_SMASH));
  1321. }
  1322. //-----------------------------------------------------------------------------
  1323. //
  1324. //-----------------------------------------------------------------------------
  1325. bool C_TFRagdoll::GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld )
  1326. {
  1327. int iHeadAttachment = LookupAttachment( "head" );
  1328. if ( IsDecapitation() && (iAttachment == iHeadAttachment) )
  1329. {
  1330. MatrixCopy( m_mHeadAttachment, attachmentToWorld );
  1331. return true;
  1332. }
  1333. else
  1334. {
  1335. return BaseClass::GetAttachment( iAttachment, attachmentToWorld );
  1336. }
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Purpose:
  1340. // Input : -
  1341. // Output : IRagdoll*
  1342. //-----------------------------------------------------------------------------
  1343. IRagdoll* C_TFRagdoll::GetIRagdoll() const
  1344. {
  1345. return m_pRagdoll;
  1346. }
  1347. //-----------------------------------------------------------------------------
  1348. // Purpose:
  1349. // Input : -
  1350. // Output : Returns true on success, false on failure.
  1351. //-----------------------------------------------------------------------------
  1352. bool C_TFRagdoll::IsRagdollVisible()
  1353. {
  1354. Vector vMins = Vector(-1,-1,-1); //WorldAlignMins();
  1355. Vector vMaxs = Vector(1,1,1); //WorldAlignMaxs();
  1356. Vector origin = GetAbsOrigin();
  1357. if( !engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) )
  1358. {
  1359. return false;
  1360. }
  1361. else if( engine->CullBox( vMins + origin, vMaxs + origin ) )
  1362. {
  1363. return false;
  1364. }
  1365. return true;
  1366. }
  1367. #define DISSOLVE_FADE_IN_START_TIME 0.0f
  1368. #define DISSOLVE_FADE_IN_END_TIME 1.0f
  1369. #define DISSOLVE_FADE_OUT_MODEL_START_TIME 1.9f
  1370. #define DISSOLVE_FADE_OUT_MODEL_END_TIME 2.0f
  1371. #define DISSOLVE_FADE_OUT_START_TIME 2.0f
  1372. #define DISSOLVE_FADE_OUT_END_TIME 2.0f
  1373. void C_TFRagdoll::ClientThink( void )
  1374. {
  1375. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1376. // Store off the un-shrunken head location for blood spurts.
  1377. if ( IsDecapitation() )
  1378. {
  1379. int iAttach = LookupAttachment( "head" );
  1380. m_bBaseTransform = true;
  1381. BaseClass::GetAttachment( iAttach, m_mHeadAttachment );
  1382. m_bBaseTransform = false;
  1383. m_BoneAccessor.SetReadableBones( 0 );
  1384. SetupBones( NULL, -1, BONE_USED_BY_ATTACHMENT, gpGlobals->curtime );
  1385. }
  1386. if ( m_bCloaked && m_flPercentInvisible < 1.f )
  1387. {
  1388. m_flPercentInvisible += gpGlobals->frametime;
  1389. if ( m_flPercentInvisible > 1.f )
  1390. {
  1391. m_flPercentInvisible = 1.f;
  1392. }
  1393. }
  1394. C_TFPlayer *pPlayer = ToTFPlayer( GetPlayerHandle() );
  1395. bool bBombinomicon = ( pPlayer && pPlayer->HasBombinomiconEffectOnDeath() );
  1396. if ( !m_bGib )
  1397. {
  1398. if ( m_bDissolving )
  1399. {
  1400. m_bDissolving = false;
  1401. m_flTimeToDissolve = 1.2f;
  1402. DissolveEntity( this );
  1403. EmitSound( "TFPlayer.Dissolve" );
  1404. // Dissolve all cosmetics as well
  1405. for ( C_BaseEntity *pEntity = ClientEntityList().FirstBaseEntity(); pEntity; pEntity = ClientEntityList().NextBaseEntity(pEntity) )
  1406. {
  1407. if ( pEntity->GetFollowedEntity() == this )
  1408. {
  1409. CEconEntity *pItem = dynamic_cast< CEconEntity * >( pEntity );
  1410. if ( pItem )
  1411. {
  1412. DissolveEntity( pItem );
  1413. }
  1414. }
  1415. }
  1416. }
  1417. else if ( bBombinomicon && ( GetFlags() & FL_DISSOLVING ) )
  1418. {
  1419. m_flTimeToDissolve -= gpGlobals->frametime;
  1420. if ( m_flTimeToDissolve <= 0 )
  1421. {
  1422. CreateTFGibs( true, true );
  1423. }
  1424. }
  1425. else if ( m_bBecomeAsh )
  1426. {
  1427. m_flTimeToDissolve -= gpGlobals->frametime;
  1428. if ( m_flTimeToDissolve <= 0 )
  1429. {
  1430. if ( bBombinomicon )
  1431. {
  1432. CreateTFGibs( true, true );
  1433. }
  1434. else
  1435. {
  1436. // Hide the ragdoll and stop everything but the ash particle effect
  1437. AddEffects( EF_NODRAW );
  1438. ParticleProp()->StopParticlesNamed( "drg_fiery_death", true, true );
  1439. // Hide all cosmetics
  1440. for ( C_BaseEntity *pEntity = ClientEntityList().FirstBaseEntity(); pEntity; pEntity = ClientEntityList().NextBaseEntity(pEntity) )
  1441. {
  1442. if ( pEntity->GetFollowedEntity() == this )
  1443. {
  1444. CEconEntity *pItem = dynamic_cast< CEconEntity * >( pEntity );
  1445. if ( pItem )
  1446. {
  1447. pItem->AddEffects( EF_NODRAW );
  1448. }
  1449. }
  1450. }
  1451. }
  1452. return;
  1453. }
  1454. }
  1455. else if ( bBombinomicon )
  1456. {
  1457. m_flTimeToDissolve -= gpGlobals->frametime;
  1458. if ( m_flTimeToDissolve <= 0 )
  1459. {
  1460. CreateTFGibs( true, true );
  1461. return;
  1462. }
  1463. }
  1464. }
  1465. // Gibbing
  1466. else
  1467. {
  1468. if ( m_bDissolving )
  1469. {
  1470. m_flTimeToDissolve -= gpGlobals->frametime;
  1471. if ( m_flTimeToDissolve <= 0 )
  1472. {
  1473. m_bDissolving = false;
  1474. if ( pPlayer )
  1475. {
  1476. if ( bBombinomicon )
  1477. {
  1478. CreateTFGibs( true, true );
  1479. }
  1480. else
  1481. {
  1482. for ( int i=0; i<pPlayer->m_hSpawnedGibs.Count(); i++ )
  1483. {
  1484. C_BaseEntity* pGib = pPlayer->m_hSpawnedGibs[i].Get();
  1485. if ( pGib )
  1486. {
  1487. pGib->SetAbsVelocity( vec3_origin );
  1488. DissolveEntity( pGib );
  1489. pGib->ParticleProp()->StopParticlesInvolving( pGib );
  1490. }
  1491. }
  1492. }
  1493. }
  1494. EndFadeOut();
  1495. }
  1496. return;
  1497. }
  1498. }
  1499. // Fade us away...
  1500. if ( m_bFadingOut == true )
  1501. {
  1502. int iAlpha = GetRenderColor().a;
  1503. int iFadeSpeed = 600.0f;
  1504. iAlpha = MAX( iAlpha - ( iFadeSpeed * gpGlobals->frametime ), 0 );
  1505. SetRenderMode( kRenderTransAlpha );
  1506. SetRenderColorA( iAlpha );
  1507. if ( iAlpha == 0 )
  1508. {
  1509. // Remove clientside ragdoll.
  1510. EndFadeOut();
  1511. }
  1512. return;
  1513. }
  1514. // If the player is looking at us, delay the fade.
  1515. if ( IsRagdollVisible() )
  1516. {
  1517. if ( cl_ragdoll_forcefade.GetBool() )
  1518. {
  1519. m_bFadingOut = true;
  1520. float flDelay = cl_ragdoll_fade_time.GetFloat() * 0.33f;
  1521. m_fDeathTime = gpGlobals->curtime + flDelay;
  1522. RemoveAllDecals();
  1523. }
  1524. // Fade out after the specified delay.
  1525. StartFadeOut( cl_ragdoll_fade_time.GetFloat() * 0.33f );
  1526. return;
  1527. }
  1528. // Remove us if our death time has passed.
  1529. if ( m_fDeathTime < gpGlobals->curtime )
  1530. {
  1531. EndFadeOut();
  1532. return;
  1533. }
  1534. // Fire an event if we were batted by the scout's taunt kill and we have come to rest.
  1535. if ( m_bBatted )
  1536. {
  1537. Vector vVelocity;
  1538. EstimateAbsVelocity( vVelocity );
  1539. if ( vVelocity.LengthSqr() == 0.f )
  1540. {
  1541. m_bBatted = false;
  1542. IGameEvent *event = gameeventmanager->CreateEvent( "scout_slamdoll_landed" );
  1543. if ( event )
  1544. {
  1545. Vector absOrigin = GetAbsOrigin();
  1546. event->SetInt( "target_index", m_iPlayerIndex );
  1547. event->SetFloat( "x", absOrigin.x );
  1548. event->SetFloat( "y", absOrigin.y );
  1549. event->SetFloat( "z", absOrigin.z );
  1550. gameeventmanager->FireEventClientSide( event );
  1551. }
  1552. }
  1553. }
  1554. }
  1555. // Deal with recording
  1556. void C_TFRagdoll::GetToolRecordingState( KeyValues *msg )
  1557. {
  1558. #ifndef _XBOX
  1559. BaseClass::GetToolRecordingState( msg );
  1560. if ( m_MaterialOverride.IsValid() )
  1561. {
  1562. msg->SetString( "materialOverride", m_MaterialOverride->GetName() );
  1563. }
  1564. #endif
  1565. }
  1566. void C_TFRagdoll::DissolveEntity( CBaseEntity* pEnt )
  1567. {
  1568. C_EntityDissolve *pDissolve = DissolveEffect( pEnt, gpGlobals->curtime );
  1569. if ( pDissolve )
  1570. {
  1571. pDissolve->SetRenderMode( kRenderTransColor );
  1572. pDissolve->m_nRenderFX = kRenderFxNone;
  1573. pDissolve->SetRenderColor( 255, 255, 255, 255 );
  1574. Vector vColor;
  1575. if ( m_iTeam == TF_TEAM_BLUE )
  1576. {
  1577. vColor = TF_PARTICLE_WEAPON_RED_1 * 255;
  1578. pDissolve->SetEffectColor( vColor );
  1579. }
  1580. else
  1581. {
  1582. vColor = TF_PARTICLE_WEAPON_BLUE_1 * 255;
  1583. pDissolve->SetEffectColor( vColor );
  1584. }
  1585. pDissolve->m_vDissolverOrigin = GetAbsOrigin();
  1586. pDissolve->m_flFadeInStart = DISSOLVE_FADE_IN_START_TIME;
  1587. pDissolve->m_flFadeInLength = DISSOLVE_FADE_IN_END_TIME - DISSOLVE_FADE_IN_START_TIME;
  1588. pDissolve->m_flFadeOutModelStart = DISSOLVE_FADE_OUT_MODEL_START_TIME;
  1589. pDissolve->m_flFadeOutModelLength = DISSOLVE_FADE_OUT_MODEL_END_TIME - DISSOLVE_FADE_OUT_MODEL_START_TIME;
  1590. pDissolve->m_flFadeOutStart = DISSOLVE_FADE_OUT_START_TIME;
  1591. pDissolve->m_flFadeOutLength = DISSOLVE_FADE_OUT_END_TIME - DISSOLVE_FADE_OUT_START_TIME;
  1592. }
  1593. }
  1594. void C_TFRagdoll::StartFadeOut( float fDelay )
  1595. {
  1596. if ( !cl_ragdoll_forcefade.GetBool() )
  1597. {
  1598. m_fDeathTime = gpGlobals->curtime + fDelay;
  1599. }
  1600. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1601. }
  1602. void C_TFRagdoll::EndFadeOut()
  1603. {
  1604. SetNextClientThink( CLIENT_THINK_NEVER );
  1605. ClearRagdoll();
  1606. SetRenderMode( kRenderNone );
  1607. UpdateVisibility();
  1608. DestroyBoneAttachments();
  1609. // Remove attached effect entity
  1610. C_BaseEntity *pEffect = GetEffectEntity();
  1611. if ( pEffect )
  1612. {
  1613. pEffect->SUB_Remove();
  1614. }
  1615. ParticleProp()->StopEmission();
  1616. // Hide attached wearables.
  1617. // These are server objects so they'll go away when the actual server ragdoll dies.
  1618. for ( int i=0; i<m_hRagWearables.Count(); ++i )
  1619. {
  1620. if ( m_hRagWearables[i] )
  1621. {
  1622. m_hRagWearables[i]->AddEffects( EF_NODRAW );
  1623. m_hRagWearables[i]->SetMoveType( MOVETYPE_NONE );
  1624. }
  1625. }
  1626. }
  1627. //-----------------------------------------------------------------------------
  1628. // Purpose: Used for spy invisiblity material
  1629. //-----------------------------------------------------------------------------
  1630. class CSpyInvisProxy : public CBaseInvisMaterialProxy
  1631. {
  1632. public:
  1633. CSpyInvisProxy( void );
  1634. virtual bool Init( IMaterial *pMaterial, KeyValues* pKeyValues ) OVERRIDE;
  1635. virtual void OnBind( C_BaseEntity *pBaseEntity ) OVERRIDE;
  1636. virtual void OnBindNotEntity( void *pRenderable ) OVERRIDE;
  1637. private:
  1638. IMaterialVar *m_pCloakColorTint;
  1639. };
  1640. //-----------------------------------------------------------------------------
  1641. // Purpose:
  1642. //-----------------------------------------------------------------------------
  1643. CSpyInvisProxy::CSpyInvisProxy( void )
  1644. {
  1645. m_pCloakColorTint = NULL;
  1646. }
  1647. //-----------------------------------------------------------------------------
  1648. // Purpose: Get pointer to the color value
  1649. // Input : *pMaterial -
  1650. //-----------------------------------------------------------------------------
  1651. bool CSpyInvisProxy::Init( IMaterial *pMaterial, KeyValues* pKeyValues )
  1652. {
  1653. // Need to get the material var
  1654. bool bInvis = CBaseInvisMaterialProxy::Init( pMaterial, pKeyValues );
  1655. bool bTint;
  1656. m_pCloakColorTint = pMaterial->FindVar( "$cloakColorTint", &bTint );
  1657. return ( bInvis && bTint );
  1658. }
  1659. ConVar tf_teammate_max_invis( "tf_teammate_max_invis", "0.95", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  1660. //-----------------------------------------------------------------------------
  1661. // Purpose:
  1662. // Input :
  1663. //-----------------------------------------------------------------------------
  1664. void CSpyInvisProxy::OnBind( C_BaseEntity *pBaseEntity )
  1665. {
  1666. if( !m_pPercentInvisible || !m_pCloakColorTint )
  1667. return;
  1668. float fInvis = 0.0f;
  1669. C_TFPlayer *pPlayer = ToTFPlayer( pBaseEntity );
  1670. if ( !pPlayer )
  1671. {
  1672. C_TFPlayer *pOwningPlayer = ToTFPlayer( pBaseEntity->GetOwnerEntity() );
  1673. C_TFRagdoll *pRagdoll = dynamic_cast< C_TFRagdoll* >( pBaseEntity );
  1674. if ( pRagdoll && pRagdoll->IsCloaked() )
  1675. {
  1676. fInvis = pRagdoll->GetPercentInvisible();
  1677. }
  1678. else if ( pOwningPlayer )
  1679. {
  1680. // mimic the owner's invisibility
  1681. fInvis = pOwningPlayer->GetEffectiveInvisibilityLevel();
  1682. }
  1683. }
  1684. else
  1685. {
  1686. float r = 1.0f, g = 1.0f, b = 1.0f;
  1687. fInvis = pPlayer->GetEffectiveInvisibilityLevel();
  1688. switch( pPlayer->GetTeamNumber() )
  1689. {
  1690. case TF_TEAM_RED:
  1691. r = 1.0; g = 0.5; b = 0.4;
  1692. break;
  1693. case TF_TEAM_BLUE:
  1694. default:
  1695. r = 0.4; g = 0.5; b = 1.0;
  1696. break;
  1697. }
  1698. m_pCloakColorTint->SetVecValue( r, g, b );
  1699. }
  1700. m_pPercentInvisible->SetFloatValue( fInvis );
  1701. }
  1702. void CSpyInvisProxy::OnBindNotEntity( void *pRenderable )
  1703. {
  1704. CBaseInvisMaterialProxy::OnBindNotEntity( pRenderable );
  1705. if ( m_pCloakColorTint )
  1706. {
  1707. m_pCloakColorTint->SetVecValue( 1.f, 1.f, 1.f );
  1708. }
  1709. }
  1710. EXPOSE_INTERFACE( CSpyInvisProxy, IMaterialProxy, "spy_invis" IMATERIAL_PROXY_INTERFACE_VERSION );
  1711. //-----------------------------------------------------------------------------
  1712. // Purpose: Used for invulnerability material
  1713. // Returns 1 if the player is invulnerable, and 0 if the player is losing / doesn't have invuln.
  1714. //-----------------------------------------------------------------------------
  1715. class CProxyInvulnLevel : public CResultProxy
  1716. {
  1717. public:
  1718. void OnBind( void *pC_BaseEntity )
  1719. {
  1720. Assert( m_pResult );
  1721. float flResult = 1.0;
  1722. C_TFPlayer *pPlayer = NULL;
  1723. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  1724. if ( !pEntity )
  1725. {
  1726. if ( g_PlayerPreviewEffect.GetEffect() == C_TFPlayerPreviewEffect::PREVIEW_EFFECT_UBER )
  1727. {
  1728. flResult = 1.0f;
  1729. }
  1730. m_pResult->SetFloatValue( flResult );
  1731. return;
  1732. }
  1733. if ( pEntity->IsPlayer() )
  1734. {
  1735. pPlayer = dynamic_cast< C_TFPlayer* >( pEntity );
  1736. }
  1737. else
  1738. {
  1739. IHasOwner *pOwnerInterface = dynamic_cast< IHasOwner* >( pEntity );
  1740. if ( pOwnerInterface )
  1741. {
  1742. pPlayer = ToTFPlayer( pOwnerInterface->GetOwnerViaInterface() );
  1743. }
  1744. }
  1745. if ( pPlayer && pPlayer->m_Shared.IsInvulnerable() && pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
  1746. {
  1747. flResult = 0.0;
  1748. }
  1749. m_pResult->SetFloatValue( flResult );
  1750. if ( ToolsEnabled() )
  1751. {
  1752. ToolFramework_RecordMaterialParams( GetMaterial() );
  1753. }
  1754. }
  1755. };
  1756. EXPOSE_INTERFACE( CProxyInvulnLevel, IMaterialProxy, "InvulnLevel" IMATERIAL_PROXY_INTERFACE_VERSION );
  1757. //-----------------------------------------------------------------------------
  1758. // Purpose: Used for burning material on player models
  1759. // Returns 0.0->1.0 for level of burn to show on player skin
  1760. //-----------------------------------------------------------------------------
  1761. class CProxyBurnLevel : public CResultProxy
  1762. {
  1763. public:
  1764. void OnBind( void *pC_BaseEntity )
  1765. {
  1766. Assert( m_pResult );
  1767. float flResult = 0.0;
  1768. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  1769. if ( !pEntity )
  1770. {
  1771. if ( g_PlayerPreviewEffect.GetEffect() == C_TFPlayerPreviewEffect::PREVIEW_EFFECT_BURN )
  1772. {
  1773. flResult = 1.0f;
  1774. }
  1775. m_pResult->SetFloatValue( flResult );
  1776. return;
  1777. }
  1778. // default to zero
  1779. float flBurnStartTime = 0;
  1780. if ( pEntity->IsPlayer() )
  1781. {
  1782. C_TFPlayer *pPlayer = assert_cast< C_TFPlayer* >( pEntity );
  1783. // is the player burning?
  1784. if ( pPlayer->m_Shared.InCond( TF_COND_BURNING ) )
  1785. {
  1786. flBurnStartTime = pPlayer->m_flBurnEffectStartTime;
  1787. }
  1788. }
  1789. else
  1790. {
  1791. // is the ragdoll burning?
  1792. C_TFRagdoll *pRagDoll = dynamic_cast< C_TFRagdoll* >( pEntity );
  1793. if ( pRagDoll )
  1794. {
  1795. flBurnStartTime = pRagDoll->GetBurnStartTime();
  1796. }
  1797. }
  1798. // if player/ragdoll is burning, set the burn level on the skin
  1799. if ( flBurnStartTime > 0 )
  1800. {
  1801. float flBurnPeakTime = flBurnStartTime + 0.3;
  1802. float flTempResult;
  1803. if ( gpGlobals->curtime < flBurnPeakTime )
  1804. {
  1805. // fade in from 0->1 in 0.3 seconds
  1806. flTempResult = RemapValClamped( gpGlobals->curtime, flBurnStartTime, flBurnPeakTime, 0.0, 1.0 );
  1807. }
  1808. else
  1809. {
  1810. // fade out from 1->0 in the remaining time until flame extinguished
  1811. flTempResult = RemapValClamped( gpGlobals->curtime, flBurnPeakTime, flBurnStartTime + TF_BURNING_FLAME_LIFE, 1.0, 0.0 );
  1812. }
  1813. // We have to do some more calc here instead of in materialvars.
  1814. flResult = 1.0 - abs( flTempResult - 1.0 );
  1815. }
  1816. m_pResult->SetFloatValue( flResult );
  1817. if ( ToolsEnabled() )
  1818. {
  1819. ToolFramework_RecordMaterialParams( GetMaterial() );
  1820. }
  1821. }
  1822. };
  1823. EXPOSE_INTERFACE( CProxyBurnLevel, IMaterialProxy, "BurnLevel" IMATERIAL_PROXY_INTERFACE_VERSION );
  1824. //-----------------------------------------------------------------------------
  1825. // Purpose: Used for turning player models yellow (jarate)
  1826. // Returns 0.0->1.0 for level of yellow to show on player skin
  1827. //-----------------------------------------------------------------------------
  1828. class CProxyUrineLevel : public CResultProxy
  1829. {
  1830. public:
  1831. void OnBind( void *pC_BaseEntity )
  1832. {
  1833. Assert( m_pResult );
  1834. // default to zero
  1835. Vector vResult = Vector( 1, 1, 1 );
  1836. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  1837. if ( !pEntity )
  1838. {
  1839. if ( g_PlayerPreviewEffect.GetEffect() == C_TFPlayerPreviewEffect::PREVIEW_EFFECT_URINE )
  1840. {
  1841. if ( g_PlayerPreviewEffect.GetTeam() == TF_TEAM_RED )
  1842. {
  1843. vResult = Vector ( 6, 9, 2 );
  1844. }
  1845. else
  1846. {
  1847. vResult = Vector ( 7, 5, 1 );
  1848. }
  1849. }
  1850. m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
  1851. return;
  1852. }
  1853. C_TFPlayer *pPlayer = NULL;
  1854. if ( pEntity->IsPlayer() )
  1855. {
  1856. pPlayer = assert_cast< C_TFPlayer* >( pEntity );
  1857. }
  1858. else if ( pEntity->GetOwnerEntity() && pEntity->GetOwnerEntity()->IsPlayer() )
  1859. {
  1860. pPlayer = assert_cast< C_TFPlayer* >( pEntity->GetOwnerEntity() );
  1861. }
  1862. if ( pPlayer )
  1863. {
  1864. // is the player peed on?
  1865. if ( pPlayer->m_Shared.InCond( TF_COND_URINE ) )
  1866. {
  1867. int iVisibleTeam = pPlayer->GetTeamNumber();
  1868. if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  1869. {
  1870. if ( !pPlayer->IsLocalPlayer() && iVisibleTeam != GetLocalPlayerTeam() )
  1871. {
  1872. iVisibleTeam = pPlayer->m_Shared.GetDisguiseTeam();
  1873. }
  1874. }
  1875. if ( iVisibleTeam == TF_TEAM_RED )
  1876. {
  1877. vResult = Vector ( 6, 9, 2 );
  1878. }
  1879. else
  1880. {
  1881. vResult = Vector ( 7, 5, 1 );
  1882. }
  1883. }
  1884. else
  1885. {
  1886. vResult = Vector( 1, 1, 1 );
  1887. }
  1888. }
  1889. m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
  1890. if ( ToolsEnabled() )
  1891. {
  1892. ToolFramework_RecordMaterialParams( GetMaterial() );
  1893. }
  1894. }
  1895. };
  1896. EXPOSE_INTERFACE( CProxyUrineLevel, IMaterialProxy, "YellowLevel" IMATERIAL_PROXY_INTERFACE_VERSION );
  1897. //-----------------------------------------------------------------------------
  1898. // Purpose: CritBoosted FX
  1899. //
  1900. //-----------------------------------------------------------------------------
  1901. class CProxyModelGlowColor : public CResultProxy
  1902. {
  1903. public:
  1904. void OnBind( void *pC_BaseEntity )
  1905. {
  1906. Assert( m_pResult );
  1907. C_TFPlayer *pPlayer = NULL;
  1908. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  1909. if ( !pEntity )
  1910. {
  1911. Vector vResult = Vector( 1, 1, 1 );
  1912. #if 0 // It looks like this code path is never used
  1913. if ( g_PlayerPreviewEffect.GetEffect() == C_TFPlayerPreviewEffect::PREVIEW_EFFECT_CRIT )
  1914. {
  1915. if ( g_PlayerPreviewEffect.GetTeam() == TF_TEAM_RED )
  1916. {
  1917. vResult = Vector ( 80, 8, 5 );
  1918. }
  1919. else
  1920. {
  1921. vResult = Vector ( 5, 20, 80 );
  1922. }
  1923. }
  1924. #endif // 0
  1925. m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
  1926. return;
  1927. }
  1928. // default to [1 1 1]
  1929. Vector vResult = Vector( 1, 1, 1 );
  1930. int iVisibleTeam = 0;
  1931. IHasOwner *pOwnerInterface = dynamic_cast< IHasOwner* >( pEntity );
  1932. if ( pOwnerInterface )
  1933. {
  1934. pPlayer = ToTFPlayer( pOwnerInterface->GetOwnerViaInterface() );
  1935. }
  1936. if ( pPlayer )
  1937. {
  1938. iVisibleTeam = pPlayer->GetTeamNumber();
  1939. if ( pPlayer->m_Shared.IsCritBoosted() )
  1940. {
  1941. // never show critboosted effect on a disguised spy (unless it's me)
  1942. if ( !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) || pPlayer->IsLocalPlayer() )
  1943. {
  1944. if ( iVisibleTeam == TF_TEAM_RED )
  1945. {
  1946. vResult = Vector ( 80, 8, 5 );
  1947. }
  1948. else
  1949. {
  1950. vResult = Vector ( 5, 20, 80 );
  1951. }
  1952. }
  1953. pPlayer->m_Shared.m_bChargeGlowing = false;
  1954. }
  1955. else if ( pPlayer->m_Shared.IsHypeBuffed() )
  1956. {
  1957. vResult = Vector( 50, 2, 48 );
  1958. pPlayer->m_Shared.m_bChargeGlowing = false;
  1959. }
  1960. else if ( pPlayer->m_Shared.InCond( TF_COND_OFFENSEBUFF ) || pPlayer->m_Shared.InCond( TF_COND_ENERGY_BUFF ) )
  1961. {
  1962. // Temporarily hijacking this proxy for buff FX.
  1963. if ( iVisibleTeam == TF_TEAM_RED )
  1964. {
  1965. vResult = Vector ( 226, 150, 62 );
  1966. }
  1967. else
  1968. {
  1969. vResult = Vector( 29, 202, 135 );
  1970. }
  1971. pPlayer->m_Shared.m_bChargeGlowing = false;
  1972. }
  1973. else
  1974. {
  1975. if ( pPlayer->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) || (pPlayer->m_Shared.GetNextMeleeCrit() != MELEE_NOCRIT) )
  1976. {
  1977. float flGlow;
  1978. if ( pPlayer->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  1979. {
  1980. // Ramp up the charge glow while charging.
  1981. flGlow = (100.f - pPlayer->m_Shared.GetDemomanChargeMeter()) / 100.f;
  1982. }
  1983. else
  1984. {
  1985. // Cool down the charge glow after charging.
  1986. flGlow = 1.f - MIN((gpGlobals->curtime - pPlayer->m_Shared.m_flLastNoChargeTime - 1.5f) / 0.3f,1);
  1987. }
  1988. if ( iVisibleTeam == TF_TEAM_RED )
  1989. {
  1990. vResult = Vector( MAX(80*flGlow,1), MAX(8*flGlow,1), MAX(5*flGlow,1) );
  1991. }
  1992. else
  1993. {
  1994. vResult = Vector( MAX(5*flGlow,1), MAX(20*flGlow,1), MAX(80*flGlow,1) );
  1995. }
  1996. pPlayer->m_Shared.m_bChargeGlowing = true;
  1997. }
  1998. else if ( pPlayer->m_Shared.m_bChargeGlowing )
  1999. {
  2000. // Cool down the charge glow after charging.
  2001. float flGlow = 1.f - MIN( (gpGlobals->curtime - pPlayer->m_Shared.m_flLastNoChargeTime) / 0.3f, 1.f );
  2002. if ( flGlow <= 0 )
  2003. {
  2004. pPlayer->m_Shared.m_bChargeGlowing = false;
  2005. }
  2006. if ( iVisibleTeam == TF_TEAM_RED )
  2007. {
  2008. vResult = Vector( MAX(80*flGlow,1), MAX(8*flGlow,1), MAX(5*flGlow,1) );
  2009. }
  2010. else
  2011. {
  2012. vResult = Vector( MAX(5*flGlow,1), MAX(20*flGlow,1), MAX(80*flGlow,1) );
  2013. }
  2014. }
  2015. else
  2016. {
  2017. vResult = Vector( 1, 1, 1 );
  2018. }
  2019. }
  2020. }
  2021. m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
  2022. if ( ToolsEnabled() )
  2023. {
  2024. ToolFramework_RecordMaterialParams( GetMaterial() );
  2025. }
  2026. }
  2027. };
  2028. EXPOSE_INTERFACE( CProxyModelGlowColor, IMaterialProxy, "ModelGlowColor" IMATERIAL_PROXY_INTERFACE_VERSION );
  2029. //-----------------------------------------------------------------------------
  2030. // Purpose: Proxy used to tell the material it's on a community weapon
  2031. //-----------------------------------------------------------------------------
  2032. class CProxyCommunityWeapon : public CResultProxy
  2033. {
  2034. public:
  2035. void OnBind( void *pC_BaseEntity )
  2036. {
  2037. Assert( m_pResult );
  2038. if ( pC_BaseEntity )
  2039. {
  2040. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  2041. if ( pEntity )
  2042. {
  2043. CEconEntity *pItem = dynamic_cast< CEconEntity* >( pEntity );
  2044. if ( pItem )
  2045. {
  2046. CEconItemView *pScriptItem = pItem->GetAttributeContainer()->GetItem();
  2047. if ( pScriptItem && pScriptItem->GetStaticData() )
  2048. {
  2049. if ( pScriptItem->GetItemQuality() == AE_COMMUNITY )
  2050. {
  2051. m_pResult->SetIntValue( 1 );
  2052. return;
  2053. }
  2054. }
  2055. }
  2056. }
  2057. }
  2058. m_pResult->SetIntValue( 0 );
  2059. if ( ToolsEnabled() )
  2060. {
  2061. ToolFramework_RecordMaterialParams( GetMaterial() );
  2062. }
  2063. }
  2064. };
  2065. EXPOSE_INTERFACE( CProxyCommunityWeapon, IMaterialProxy, "CommunityWeapon" IMATERIAL_PROXY_INTERFACE_VERSION );
  2066. //-----------------------------------------------------------------------------
  2067. // Purpose: Used for scaling the beating heart texture to make it pulse
  2068. //-----------------------------------------------------------------------------
  2069. class CProxyHeartbeatScale : public CResultProxy
  2070. {
  2071. public:
  2072. void OnBind( void *pC_BaseEntity )
  2073. {
  2074. Assert( m_pResult );
  2075. const float pi = 3.141592f;
  2076. const float twoPI = 2.0f * pi;
  2077. float s1 = sin( gpGlobals->curtime * twoPI );
  2078. s1 = clamp( s1, 0.0f, 1.0f );
  2079. s1 *= s1;
  2080. s1 *= s1;
  2081. s1 = clamp( s1, 0.5f, 1.0f );
  2082. s1 -= 0.5f;
  2083. s1 *= 2.0f;
  2084. float s2 = sin( ( gpGlobals->curtime + 0.25f ) * twoPI );
  2085. s2 = clamp( s2, 0.0f, 1.0f );
  2086. s2 *= s2;
  2087. s2 *= s2;
  2088. s2 = clamp( s2, 0.5f, 1.0f );
  2089. s2 -= 0.5f;
  2090. s2 *= 2.0f;
  2091. float beat = MAX( s1, s2 );
  2092. const float scale = 0.6f;
  2093. const float loBeat = 1.0f * scale;
  2094. const float hiBeat = 0.8f * scale;
  2095. float scaledBeat = loBeat + ( hiBeat - loBeat ) * beat;
  2096. m_pResult->SetFloatValue( scaledBeat );
  2097. if ( ToolsEnabled() )
  2098. {
  2099. ToolFramework_RecordMaterialParams( GetMaterial() );
  2100. }
  2101. }
  2102. };
  2103. EXPOSE_INTERFACE( CProxyHeartbeatScale, IMaterialProxy, "HeartbeatScale" IMATERIAL_PROXY_INTERFACE_VERSION );
  2104. #ifdef _DEBUG
  2105. ConVar tf_benefactor_gift_count( "tf_benefactor_gift_count", "-1", FCVAR_CHEAT, "For testing" );
  2106. #endif
  2107. //-----------------------------------------------------------------------------
  2108. // Purpose: Used for scaling the beating heart based on the number of gifts player has given
  2109. // Returns a texture scale factor where 1 is max size (big benefactor) and factor gets larger the fewer gifts you've given
  2110. //-----------------------------------------------------------------------------
  2111. class CProxyBenefactorLevel : public CResultProxy
  2112. {
  2113. public:
  2114. void OnBind( void *pC_BaseEntity )
  2115. {
  2116. Assert( m_pResult );
  2117. CEconItemView *pScriptItem = NULL;
  2118. IClientRenderable *pRend = (IClientRenderable *)pC_BaseEntity;
  2119. C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
  2120. if ( pEntity )
  2121. {
  2122. CEconEntity *pItem = dynamic_cast< CEconEntity* >( pEntity );
  2123. if ( pItem )
  2124. {
  2125. pScriptItem = pItem->GetAttributeContainer()->GetItem();
  2126. }
  2127. }
  2128. if ( pScriptItem )
  2129. {
  2130. static CSchemaAttributeDefHandle pAttrDef_KillEater( "kill eater" );
  2131. // Use the kill-eater prefix if the weapon has one.
  2132. uint32 unKillCount;
  2133. if ( !pScriptItem->FindAttribute( pAttrDef_KillEater, &unKillCount ) )
  2134. return;
  2135. #ifdef _DEBUG
  2136. int testCount = tf_benefactor_gift_count.GetInt();
  2137. if ( testCount >= 0 )
  2138. {
  2139. unKillCount = (uint32)testCount;
  2140. }
  2141. #endif
  2142. if ( unKillCount == 0 )
  2143. {
  2144. // heartless
  2145. m_pResult->SetFloatValue( 1000.0f );
  2146. }
  2147. else
  2148. {
  2149. const int maxBenefatorLevel = 250;
  2150. float value = (float)unKillCount / (float)maxBenefatorLevel;
  2151. value = clamp( value, 0.0f, 1.0f );
  2152. // a linear scale doesn't show the size change until the very end - nonlinearize it
  2153. value = sin( value * 1.57f );
  2154. value = sin( value * 1.57f );
  2155. // this seems backwards because this is a texture scaling factor
  2156. // the bigger the number, the smaller the textured image is (tiles more in same space)
  2157. const float minValue = 5.0f;
  2158. const float maxValue = 1.0f;
  2159. m_pResult->SetFloatValue( minValue + ( maxValue - minValue ) * value );
  2160. }
  2161. }
  2162. if ( ToolsEnabled() )
  2163. {
  2164. ToolFramework_RecordMaterialParams( GetMaterial() );
  2165. }
  2166. }
  2167. };
  2168. EXPOSE_INTERFACE( CProxyBenefactorLevel, IMaterialProxy, "BenefactorLevel" IMATERIAL_PROXY_INTERFACE_VERSION );
  2169. //-----------------------------------------------------------------------------
  2170. // Purpose: Used for scaling the oscilloscope on the Building Rescue Gun
  2171. // Flattens the Wave when the player has no energy
  2172. //-----------------------------------------------------------------------------
  2173. class CProxyBuildingRescueLevel : public CResultProxy
  2174. {
  2175. public:
  2176. void OnBind( void *pC_BaseEntity )
  2177. {
  2178. Assert( m_pResult );
  2179. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  2180. if ( !pPlayer )
  2181. return;
  2182. int iIncreasedRangeCost = 0;
  2183. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iIncreasedRangeCost, building_teleporting_pickup );
  2184. if ( iIncreasedRangeCost == 0 )
  2185. return;
  2186. CTFWeaponBase * pWeapon = pPlayer->GetActiveTFWeapon();
  2187. if ( !pWeapon )
  2188. return;
  2189. int iAmmo = pPlayer->GetAmmoCount( TF_AMMO_METAL );
  2190. float scale = 1.0f;
  2191. if ( iAmmo < iIncreasedRangeCost )
  2192. {
  2193. scale = 10.0f;
  2194. }
  2195. else
  2196. {
  2197. scale = ( 3.0f - ((float)(iAmmo - iIncreasedRangeCost) / (float)(pPlayer->GetMaxAmmo( TF_AMMO_METAL ) - iIncreasedRangeCost) * 3.0f ) + 1.0f );
  2198. }
  2199. VMatrix mat, temp;
  2200. Vector2D center( 0.5, 0.5 );
  2201. MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
  2202. // scale
  2203. {
  2204. MatrixBuildScale( temp, 1.0f, scale, 1.0f );
  2205. MatrixMultiply( temp, mat, mat );
  2206. }
  2207. MatrixBuildTranslation( temp, center.x, center.y, 0.0f );
  2208. MatrixMultiply( temp, mat, mat );
  2209. m_pResult->SetMatrixValue( mat );
  2210. if ( ToolsEnabled() )
  2211. {
  2212. ToolFramework_RecordMaterialParams( GetMaterial() );
  2213. }
  2214. }
  2215. };
  2216. EXPOSE_INTERFACE( CProxyBuildingRescueLevel, IMaterialProxy, "BuildingRescueLevel" IMATERIAL_PROXY_INTERFACE_VERSION );
  2217. //-----------------------------------------------------------------------------
  2218. // Used to pulse the Vaccinator's uber shield
  2219. //-----------------------------------------------------------------------------
  2220. class CProxyResistShield : public CResultProxy
  2221. {
  2222. public:
  2223. void OnBind( void *pC_BaseEntity )
  2224. {
  2225. Assert( m_pResult );
  2226. IClientRenderable *pRend = (IClientRenderable *)pC_BaseEntity;
  2227. C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
  2228. if ( pEntity )
  2229. {
  2230. C_LocalTempEntity* pTempEnt = dynamic_cast<C_LocalTempEntity*>(pEntity);
  2231. if ( pTempEnt )
  2232. {
  2233. C_BaseEntity *pBaseEnt = cl_entitylist->GetEnt( pTempEnt->clientIndex );
  2234. // This should be the owning player
  2235. C_TFPlayer* pTFPlayer = ToTFPlayer( pBaseEnt );
  2236. if( pTFPlayer )
  2237. {
  2238. float flTimeSince = gpGlobals->curtime - pTFPlayer->GetLastResistTime();
  2239. float flOut = RemapValClamped( flTimeSince, 0, 0.4f, 7.f, -4.f );
  2240. m_pResult->SetVecValue( flOut, flOut, flOut, 1.f );
  2241. return;
  2242. }
  2243. }
  2244. }
  2245. m_pResult->SetVecValue( 1.0, 1.0, 1.0, 1.0 );
  2246. }
  2247. };
  2248. EXPOSE_INTERFACE( CProxyResistShield, IMaterialProxy, "ShieldFalloff" IMATERIAL_PROXY_INTERFACE_VERSION );
  2249. //-----------------------------------------------------------------------------
  2250. // Purpose: Used for pulsing the Wheatly Sappers eye glow when he talks
  2251. //-----------------------------------------------------------------------------
  2252. class CProxyWheatlyEyeGlow : public CResultProxy
  2253. {
  2254. public:
  2255. void OnBind( void *pC_BaseEntity )
  2256. {
  2257. Assert( m_pResult );
  2258. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  2259. if ( !pPlayer )
  2260. return;
  2261. const float flBase = 0.4f;
  2262. const float flMinTalking = 0.6f;
  2263. const float flMaxTalking = 1.3f;
  2264. const float flAdjustUp = 0.01f;
  2265. const float flAdjustDown = -0.006f;
  2266. static float s_flCurr = 0.2f;
  2267. static float s_flDir = 0.2f;
  2268. const float flEyeHold = 1.9f;
  2269. const float flMinEyes = 0.1f;
  2270. const float flMaxEyes = 0.35f;
  2271. const float flEyeAdjust = 0.005f;
  2272. static float s_flEyePose = 0.0f;
  2273. static float s_flNextEyeChange = 0;
  2274. static float s_flEyeDir = 0.005f;
  2275. C_TFWeaponSapper *pWeapon = dynamic_cast< C_TFWeaponSapper* >( pPlayer->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING ) );
  2276. if ( pWeapon )
  2277. {
  2278. if ( pWeapon->IsWheatleyTalking() )
  2279. {
  2280. float flNoise = RandomGaussianFloat( 0.0f, 0.01f );
  2281. s_flCurr += s_flDir + flNoise;
  2282. if ( s_flCurr > flMaxTalking && s_flDir > 0 )
  2283. {
  2284. s_flDir = flAdjustDown;
  2285. }
  2286. else if ( s_flCurr < flMinTalking && s_flDir < 0 )
  2287. {
  2288. s_flDir = flAdjustUp;
  2289. }
  2290. // Animate to eye's and hold for a few seconds
  2291. float currTime = gpGlobals->curtime;
  2292. if ( currTime > s_flNextEyeChange)
  2293. {
  2294. s_flEyePose += s_flEyeDir;
  2295. if ( s_flEyePose > flMaxEyes && s_flEyeDir > 0 )
  2296. {
  2297. s_flNextEyeChange = currTime + flEyeHold;
  2298. s_flEyeDir = -flEyeAdjust;
  2299. }
  2300. if ( s_flEyePose < flMinEyes && s_flEyeDir < 0 )
  2301. {
  2302. s_flNextEyeChange = currTime + flEyeHold;
  2303. s_flEyeDir = flEyeAdjust;
  2304. }
  2305. }
  2306. }
  2307. else
  2308. {
  2309. // adjust towards base
  2310. s_flCurr += flAdjustDown;
  2311. if ( s_flCurr < flBase )
  2312. {
  2313. s_flCurr = flBase;
  2314. s_flDir = flAdjustUp;
  2315. }
  2316. if ( s_flEyePose > 0 )
  2317. {
  2318. s_flEyePose -= flEyeAdjust;
  2319. }
  2320. s_flEyeDir = flEyeAdjust;
  2321. }
  2322. CBaseViewModel *pViewModel = pPlayer->GetViewModel(0);
  2323. if ( pViewModel )
  2324. {
  2325. pViewModel->SetPoseParameter( "eyelids", s_flEyePose );
  2326. }
  2327. }
  2328. m_pResult->SetFloatValue( s_flCurr );
  2329. if ( ToolsEnabled() )
  2330. {
  2331. ToolFramework_RecordMaterialParams( GetMaterial() );
  2332. }
  2333. }
  2334. };
  2335. EXPOSE_INTERFACE( CProxyWheatlyEyeGlow, IMaterialProxy, "WheatlyEyeGlow" IMATERIAL_PROXY_INTERFACE_VERSION );
  2336. //-----------------------------------------------------------------------------
  2337. CEconItemView *GetEconItemViewFromProxyEntity( void *pEntity )
  2338. {
  2339. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2340. IClientRenderable *pRend = (IClientRenderable *)pEntity;
  2341. CBaseEntity *pBaseEntity = pRend ? pRend->GetIClientUnknown()->GetBaseEntity() : NULL;
  2342. // If an entity, find out what types it is and get the econ item view
  2343. if ( pBaseEntity )
  2344. {
  2345. // Generic Entity that has an Item ( Weapons / Hats )
  2346. IHasAttributes *pAttribInterface = GetAttribInterface( pBaseEntity );
  2347. if ( pAttribInterface )
  2348. return pAttribInterface->GetAttributeContainer()->GetItem();
  2349. // ViewModel Attachment (aka view model Weapon)
  2350. C_ViewmodelAttachmentModel *pViewModelAttachment = dynamic_cast<C_ViewmodelAttachmentModel*>( pBaseEntity );
  2351. if ( pViewModelAttachment && pViewModelAttachment->GetOuter() )
  2352. {
  2353. return pViewModelAttachment->GetOuter()->GetAttributeContainer()->GetItem();
  2354. }
  2355. CTFViewModel *pViewModel = dynamic_cast<CTFViewModel*>( pBaseEntity );
  2356. if ( pViewModel && pViewModel->GetWeapon() )
  2357. {
  2358. return pViewModel->GetWeapon()->GetAttributeContainer()->GetItem();
  2359. }
  2360. CTFDroppedWeapon *pDroppedWeapon = dynamic_cast<CTFDroppedWeapon*>( pBaseEntity );
  2361. if ( pDroppedWeapon && pDroppedWeapon->GetItem() && pDroppedWeapon->GetItem()->GetItemDefIndex() != INVALID_ITEM_DEF_INDEX )
  2362. {
  2363. return pDroppedWeapon->GetItem();
  2364. }
  2365. }
  2366. // No direct entity, might be a EconItem (PlayerModelPanels)
  2367. else
  2368. {
  2369. CEconItemView *pItem = dynamic_cast< CEconItemView* >( pRend );
  2370. if ( pItem )
  2371. {
  2372. return pItem;
  2373. }
  2374. }
  2375. return NULL;
  2376. }
  2377. //-----------------------------------------------------------------------------
  2378. C_TFPlayer *GetOwnerFromProxyEntity( void *pEntity )
  2379. {
  2380. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2381. IClientRenderable *pRend = ( IClientRenderable * ) pEntity;
  2382. CBaseEntity *pBaseEntity = pRend ? pRend->GetIClientUnknown()->GetBaseEntity() : NULL;
  2383. // If an entity, find out what types it is and get the econ item view
  2384. if ( pBaseEntity )
  2385. {
  2386. CBaseEntity* pOwner = pBaseEntity->GetOwnerEntity();
  2387. if ( pOwner )
  2388. return dynamic_cast<C_TFPlayer*>( pOwner->GetOwnerEntity() );
  2389. }
  2390. return NULL;
  2391. }
  2392. //-----------------------------------------------------------------------------
  2393. // Purpose: Used to animate the weapon sheen effect for kill streak attr items
  2394. //-----------------------------------------------------------------------------
  2395. class CProxyAnimatedWeaponSheen : public CBaseAnimatedTextureProxy
  2396. {
  2397. public:
  2398. CProxyAnimatedWeaponSheen() {}
  2399. virtual ~CProxyAnimatedWeaponSheen() {}
  2400. bool Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  2401. {
  2402. bool foundVar = false;
  2403. m_flNextStartTime = 0;
  2404. m_flScaleX = -1;
  2405. m_flScaleY = -1;
  2406. m_flSheenOffsetX = 0;
  2407. m_flSheenOffsetY = 0;
  2408. m_iSheenDir = 0;
  2409. m_pScaleXVar = NULL;
  2410. m_pScaleYVar = NULL;
  2411. m_pOffsetXVar = NULL;
  2412. m_pOffsetYVar = NULL;
  2413. m_pDirectionVar = NULL;
  2414. m_pSheenIndexVar = NULL;
  2415. // Get Tint
  2416. m_pTintVar = pMaterial->FindVar( "$sheenmaptint", &foundVar );
  2417. if( !foundVar )
  2418. return false;
  2419. m_pSheenIndexVar = pMaterial->FindVar( "$sheenindex", &foundVar );
  2420. if( !foundVar )
  2421. return false;
  2422. // Material vars for scale and offset
  2423. // Need to get the material var
  2424. m_pScaleXVar = pMaterial->FindVar( "$sheenmapmaskscaleX", &foundVar );
  2425. if( !foundVar )
  2426. return false;
  2427. m_pScaleYVar = pMaterial->FindVar( "$sheenmapmaskscaleY", &foundVar );
  2428. if( !foundVar )
  2429. return false;
  2430. m_pOffsetXVar = pMaterial->FindVar( "$sheenmapmaskoffsetX", &foundVar );
  2431. if( !foundVar )
  2432. return false;
  2433. m_pOffsetYVar = pMaterial->FindVar( "$sheenmapmaskoffsetY", &foundVar );
  2434. if( !foundVar )
  2435. return false;
  2436. m_pDirectionVar = pMaterial->FindVar( "$sheenmapmaskdirection", &foundVar );
  2437. if( !foundVar )
  2438. return false;
  2439. m_pSheenVar = pMaterial->FindVar( "$sheenmap", &foundVar );
  2440. if ( !foundVar )
  2441. return false;
  2442. m_pSheenMaskVar = pMaterial->FindVar( "$sheenmapmask", &foundVar );
  2443. if ( !foundVar )
  2444. return false;
  2445. return CBaseAnimatedTextureProxy::Init( pMaterial, pKeyValues );
  2446. }
  2447. void OnBind( void *pEntity )
  2448. {
  2449. Assert( m_AnimatedTextureVar );
  2450. if ( m_AnimatedTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
  2451. return;
  2452. // if no entity, just check the owner
  2453. if ( !pEntity )
  2454. {
  2455. // Might be gunslinger, hard to get this item so
  2456. return;
  2457. }
  2458. static CSchemaAttributeDefHandle pAttr_killstreak( "killstreak idleeffect" );
  2459. if ( !pAttr_killstreak )
  2460. return;
  2461. IClientRenderable *pRend = (IClientRenderable *)pEntity;
  2462. // Find the weapon and player and see if it has the attribute
  2463. if ( !pRend )
  2464. return;
  2465. ITexture *pTexture;
  2466. pTexture = m_AnimatedTextureVar->GetTextureValue();
  2467. int numFrames = pTexture->GetNumAnimationFrames();
  2468. if ( numFrames <= 0 )
  2469. {
  2470. Assert( !"0 frames in material calling animated texture proxy" );
  2471. return;
  2472. }
  2473. C_BaseEntity *pBaseEntity = pRend->GetIClientUnknown()->GetBaseEntity();
  2474. const CEconItemView *pItem = dynamic_cast< CEconItemView* >( pRend );
  2475. uint32 unAttrValue = 0;
  2476. uint32 unEffectValue = 0;
  2477. // !TEST!
  2478. #ifdef STAGING_ONLY
  2479. if ( tf_killstreak_alwayson.GetBool() )
  2480. {
  2481. unEffectValue = 1;
  2482. }
  2483. if ( tf_sheen_all.GetFloat() != 0 )
  2484. {
  2485. unEffectValue = tf_sheen_all.GetInt();
  2486. }
  2487. #endif // STAGING_ONLY
  2488. // Find the tf player owner
  2489. bool bIsFirstPerson = false;
  2490. C_TFPlayer* pTFPlayer = NULL;
  2491. if ( pItem ) // ItemModelPanels
  2492. {
  2493. if ( !pBaseEntity )
  2494. {
  2495. pItem->FindAttribute( pAttr_killstreak, &unAttrValue );
  2496. unEffectValue = (int)((float&)unAttrValue);
  2497. }
  2498. else
  2499. {
  2500. CALL_ATTRIB_HOOK_INT_ON_OTHER( pBaseEntity, unAttrValue, killstreak_idleeffect );
  2501. }
  2502. if ( ( unEffectValue ) && pBaseEntity && pBaseEntity->GetOwnerEntity() )
  2503. {
  2504. pTFPlayer = ToTFPlayer( pBaseEntity->GetOwnerEntity() );
  2505. }
  2506. else
  2507. {
  2508. pTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  2509. }
  2510. }
  2511. else
  2512. {
  2513. CTFWeaponBase* pWeapon = dynamic_cast<CTFWeaponBase*>( pBaseEntity );
  2514. if ( !pWeapon )
  2515. {
  2516. for ( int i = 0; i < 1; ++i )
  2517. {
  2518. CEconWearable* pWearable = dynamic_cast<CEconWearable*>( pBaseEntity );
  2519. if ( pWearable )
  2520. {
  2521. pItem = pWearable->GetAttributeContainer()->GetItem();
  2522. pTFPlayer = ToTFPlayer( pWearable->GetOwnerEntity() );
  2523. break;
  2524. }
  2525. C_ViewmodelAttachmentModel *pModel = dynamic_cast<C_ViewmodelAttachmentModel*>( pBaseEntity );
  2526. if ( pModel )
  2527. {
  2528. if ( pModel->GetOuter() )
  2529. {
  2530. pItem = pModel->GetOuter()->GetAttributeContainer()->GetItem();
  2531. pBaseEntity = pBaseEntity->GetOwnerEntity();
  2532. if ( pItem )
  2533. {
  2534. pTFPlayer = ToTFPlayer( pModel->GetOuter()->GetOwnerEntity() );
  2535. }
  2536. }
  2537. break;
  2538. }
  2539. // not a weapon, is a viewmodel
  2540. IHasOwner *pHasOwner = dynamic_cast<IHasOwner*>( pBaseEntity );
  2541. if ( pHasOwner )
  2542. {
  2543. // View model owner is player, so get the players active weapon
  2544. CBaseEntity *pOwner = pHasOwner->GetOwnerViaInterface();
  2545. pTFPlayer = ToTFPlayer( pOwner );
  2546. if ( pTFPlayer )
  2547. {
  2548. pWeapon = pTFPlayer->GetActiveTFWeapon();
  2549. if ( pWeapon )
  2550. {
  2551. pItem = pWeapon->GetAttributeContainer()->GetItem();
  2552. pBaseEntity = pWeapon;
  2553. }
  2554. bIsFirstPerson = true;
  2555. }
  2556. }
  2557. else if ( pBaseEntity && pBaseEntity->IsPlayer( ) )
  2558. {
  2559. pTFPlayer = ToTFPlayer( pBaseEntity );
  2560. pWeapon = pTFPlayer->GetActiveTFWeapon();
  2561. if ( pWeapon )
  2562. {
  2563. pItem = pWeapon->GetAttributeContainer()->GetItem();
  2564. pBaseEntity = pWeapon;
  2565. }
  2566. }
  2567. } // for
  2568. }
  2569. else
  2570. {
  2571. pItem = pWeapon->GetAttributeContainer()->GetItem();
  2572. pBaseEntity = pWeapon;
  2573. pTFPlayer = ToTFPlayer( pWeapon->GetOwner() );
  2574. }
  2575. // I have an econ item, does it have the attr
  2576. if ( ( pBaseEntity && pItem ) || unEffectValue )
  2577. {
  2578. CALL_ATTRIB_HOOK_INT_ON_OTHER( pBaseEntity, unAttrValue, killstreak_idleeffect );
  2579. if ( !unEffectValue )
  2580. {
  2581. unEffectValue = unAttrValue;
  2582. }
  2583. // Use the Spies target if disguised and we're on different teams
  2584. if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTFPlayer->GetTeamNumber() != GetLocalPlayerTeam() )
  2585. {
  2586. pTFPlayer = ToTFPlayer( pTFPlayer->m_Shared.GetDisguiseTarget() );
  2587. if ( pTFPlayer && pTFPlayer->m_Shared.GetDisguiseWeapon() )
  2588. {
  2589. pItem = pTFPlayer->m_Shared.GetDisguiseWeapon()->GetAttributeContainer()->GetItem();
  2590. pBaseEntity = pTFPlayer->m_Shared.GetDisguiseWeapon();
  2591. }
  2592. }
  2593. }
  2594. }
  2595. float flNextStartTime = 0;
  2596. if ( pTFPlayer )
  2597. {
  2598. // if player is taunting, make sure they can start animating
  2599. if ( pTFPlayer->IsTaunting() && flNextStartTime > gpGlobals->curtime )
  2600. {
  2601. pTFPlayer->m_flNextSheenStartTime = gpGlobals->curtime;
  2602. }
  2603. flNextStartTime = pTFPlayer->m_flNextSheenStartTime;
  2604. }
  2605. else
  2606. {
  2607. flNextStartTime = m_flNextStartTime;
  2608. }
  2609. // !TEST!
  2610. #ifdef STAGING_ONLY
  2611. if ( tf_killstreak_alwayson.GetBool() )
  2612. {
  2613. unEffectValue = 1;
  2614. }
  2615. if ( tf_sheen_all.GetFloat() != 0 )
  2616. {
  2617. unEffectValue = tf_sheen_all.GetInt();
  2618. }
  2619. #endif // STAGING_ONLY
  2620. // Not ready, so just exit
  2621. if ( !pItem || !unEffectValue || flNextStartTime > gpGlobals->curtime || unEffectValue > ARRAYSIZE( g_KillStreakEffectsBase ) - 1 )
  2622. {
  2623. RunNoProxy();
  2624. return;
  2625. }
  2626. // Negative Value implies it has not been set, set it now
  2627. if ( m_flScaleX < 0 )
  2628. {
  2629. if ( !InitParams( pRend, pBaseEntity ) )
  2630. {
  2631. RunNoProxy();
  2632. return;
  2633. }
  2634. }
  2635. // NOTE: Must not use relative time based methods here
  2636. // because the bind proxy can be called many times per frame.
  2637. // Prevent multiple Wrap callbacks to be sent for no wrap mode
  2638. float startTime = pTFPlayer ? pTFPlayer->m_flNextSheenStartTime : 0;
  2639. float deltaTime = gpGlobals->curtime - startTime;
  2640. float prevTime = deltaTime - gpGlobals->frametime;
  2641. // Clamp..
  2642. if (deltaTime < 0.0f)
  2643. deltaTime = 0.0f;
  2644. if (prevTime < 0.0f)
  2645. prevTime = 0.0f;
  2646. // Code Frame rate to be 25
  2647. float frame = tf_sheen_framerate.GetInt() * deltaTime;
  2648. float prevFrame = tf_sheen_framerate.GetInt() * prevTime;
  2649. int intFrame = ((int)frame) % numFrames;
  2650. int intPrevFrame = ((int)prevFrame) % numFrames;
  2651. // Report wrap situation...
  2652. if ( intPrevFrame > intFrame )
  2653. {
  2654. // Set frame to zero and set the time for the next
  2655. intFrame = 0;
  2656. if ( pTFPlayer )
  2657. {
  2658. pTFPlayer->m_flNextSheenStartTime = gpGlobals->curtime + GetTimeBetweenAnims( pTFPlayer );
  2659. }
  2660. else
  2661. {
  2662. m_flNextStartTime = gpGlobals->curtime + GetTimeBetweenAnims( NULL );
  2663. }
  2664. }
  2665. float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
  2666. killstreak_params_t sheenParams = GetSheenParams( unEffectValue, pTFPlayer ? pTFPlayer->GetTeamNumber() == TF_TEAM_BLUE : false );
  2667. color[ 0 ] = sheenParams.m_sheen_r;
  2668. color[ 1 ] = sheenParams.m_sheen_g;
  2669. color[ 2 ] = sheenParams.m_sheen_b;
  2670. color[ 3 ] = sheenParams.m_sheen_a;
  2671. #ifdef STAGING_ONLY
  2672. if ( tf_sheen_color_override_a.GetFloat() > 0 )
  2673. {
  2674. color[0] = tf_sheen_color_override_r.GetFloat();
  2675. color[1] = tf_sheen_color_override_g.GetFloat();
  2676. color[2] = tf_sheen_color_override_b.GetFloat();
  2677. color[3] = tf_sheen_color_override_a.GetFloat();
  2678. }
  2679. #endif // STAGING_ONLY
  2680. if ( bIsFirstPerson )
  2681. {
  2682. color[ 3 ] = tf_sheen_alpha_firstperson.GetFloat();
  2683. m_pTintVar->SetVecValue( color, 4 );
  2684. }
  2685. else
  2686. {
  2687. m_pTintVar->SetVecValue( color, 4 );
  2688. }
  2689. // Set vars
  2690. m_AnimatedTextureFrameNumVar->SetIntValue( intFrame );
  2691. m_pScaleXVar->SetFloatValue( m_flScaleX ); // Only need to set once?
  2692. m_pScaleYVar->SetFloatValue( m_flScaleY );
  2693. m_pOffsetXVar->SetFloatValue( m_flSheenOffsetX );
  2694. m_pOffsetYVar->SetFloatValue( m_flSheenOffsetY );
  2695. m_pDirectionVar->SetIntValue( m_iSheenDir );
  2696. int iShaderIndex = sheenParams.m_iShaderIndex;
  2697. // Australium weapons always use iShaderIndex 1
  2698. const CEconStyleInfo *pStyle = pItem->GetStaticData()->GetStyleInfo( pItem->GetItemStyle() );
  2699. if ( pStyle && !pStyle->IsSelectable() )
  2700. {
  2701. iShaderIndex = 1;
  2702. }
  2703. #ifdef STAGING_ONLY
  2704. if ( tf_sheen_shader_override.GetInt() > 0 )
  2705. {
  2706. iShaderIndex = tf_sheen_shader_override.GetInt();
  2707. }
  2708. #endif // staging_only
  2709. m_pSheenIndexVar->SetIntValue( iShaderIndex );
  2710. if ( ToolsEnabled() )
  2711. {
  2712. ToolFramework_RecordMaterialParams( GetMaterial() );
  2713. }
  2714. }
  2715. // the last time the animation was run (or will allowed to be run)
  2716. float GetAnimationStartTime( void* pBaseEntity )
  2717. {
  2718. return 0;
  2719. }
  2720. float GetTimeBetweenAnims ( C_TFPlayer* pTFPlayer )
  2721. {
  2722. const float MAX_SHEEN_WAIT = 5.0f;
  2723. const float MAX_KILLS = 5.0f;
  2724. #ifdef STAGING_ONLY
  2725. if ( tf_sheen_fast.GetBool() )
  2726. return 0;
  2727. #endif
  2728. if ( !pTFPlayer )
  2729. return MAX_SHEEN_WAIT;
  2730. if ( pTFPlayer->IsTaunting() )
  2731. return 0;
  2732. // Set the time between sheens based on kill streak
  2733. int iStreak = pTFPlayer->m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills );
  2734. if ( iStreak >= MAX_KILLS )
  2735. return 0;
  2736. if ( iStreak == 0 )
  2737. return MAX_SHEEN_WAIT;
  2738. // as player gets more kills, time decreases
  2739. return ( 1.0f - ( iStreak / MAX_KILLS ) ) * MAX_SHEEN_WAIT;
  2740. }
  2741. killstreak_params_t GetSheenParams( uint32 unEffectValue, bool bIsTeamBlue )
  2742. {
  2743. Assert( unEffectValue > 0 && unEffectValue < ARRAYSIZE( g_KillStreakEffectsBase ) );
  2744. killstreak_params_t params = g_KillStreakEffectsBase[ unEffectValue ];
  2745. if ( bIsTeamBlue && params.m_bHasTeamColor )
  2746. {
  2747. Assert( unEffectValue > 0 && unEffectValue < ARRAYSIZE( g_KillStreakEffectsBlue ) );
  2748. return g_KillStreakEffectsBlue[ unEffectValue ];
  2749. }
  2750. return params;
  2751. }
  2752. bool InitParams( IClientRenderable *pRend, C_BaseEntity *pBaseEntity )
  2753. {
  2754. // Negative Value implies it has not been set, set it now
  2755. if ( m_flScaleX < 0 )
  2756. {
  2757. Vector vMin, vMax;
  2758. // Check if the baseEntity is ready
  2759. if ( pBaseEntity )
  2760. {
  2761. CBaseAnimating *pAnimating = dynamic_cast< CBaseAnimating * > ( pBaseEntity );
  2762. if ( !pAnimating || !pAnimating->GetModelPtr() || pAnimating->GetModelPtr()->GetNumSeq() < pAnimating->GetSequence() )
  2763. {
  2764. return false;
  2765. }
  2766. }
  2767. pRend->GetRenderBounds( vMin, vMax );
  2768. m_flScaleX = vMax.x - vMin.x;
  2769. m_flSheenOffsetX = vMin.x;
  2770. m_flScaleY = vMax.z - vMin.z;
  2771. m_flSheenOffsetY = vMin.z;
  2772. m_iSheenDir = 0;
  2773. if ( vMax.y - vMin.y > m_flScaleX )
  2774. {
  2775. m_flScaleX = vMax.y - vMin.y;
  2776. m_flSheenOffsetX = vMin.y;
  2777. m_flScaleY = vMax.x - vMin.x;
  2778. m_flSheenOffsetY = vMin.x;
  2779. m_iSheenDir = 1;
  2780. }
  2781. if ( vMax.z - vMin.z > m_flScaleX )
  2782. {
  2783. m_flScaleX = vMax.z - vMin.z;
  2784. m_flSheenOffsetX = vMin.z;
  2785. m_flScaleY = vMax.y - vMin.y;
  2786. m_flSheenOffsetY = vMin.y;
  2787. m_iSheenDir = 2;
  2788. }
  2789. }
  2790. return true;
  2791. }
  2792. void Cleanup()
  2793. {
  2794. m_pTintVar = NULL;
  2795. CBaseAnimatedTextureProxy::Cleanup();
  2796. }
  2797. void RunNoProxy ()
  2798. {
  2799. m_pTintVar->SetVecValue( 0, 0, 0 );
  2800. m_AnimatedTextureFrameNumVar->SetIntValue( 0 );
  2801. m_pSheenIndexVar->SetIntValue( 0 );
  2802. }
  2803. private:
  2804. IMaterialVar *m_pSheenIndexVar;
  2805. IMaterialVar *m_pTintVar;
  2806. IMaterialVar *m_pSheenVar; // Overloaded for Weapon Pattern
  2807. IMaterialVar *m_pSheenMaskVar; // Weapon Pattern Mask
  2808. IMaterialVar *m_pScaleXVar;
  2809. IMaterialVar *m_pScaleYVar;
  2810. IMaterialVar *m_pOffsetXVar;
  2811. IMaterialVar *m_pOffsetYVar;
  2812. IMaterialVar *m_pDirectionVar;
  2813. float m_flNextStartTime; // Used in the rare case of playermodelpanels with no local player
  2814. float m_flScaleX;
  2815. float m_flScaleY;
  2816. float m_flSheenOffsetX;
  2817. float m_flSheenOffsetY;
  2818. int m_iSheenDir;
  2819. };
  2820. EXPOSE_INTERFACE( CProxyAnimatedWeaponSheen, IMaterialProxy, "AnimatedWeaponSheen" IMATERIAL_PROXY_INTERFACE_VERSION );
  2821. // StatTrack Proxy
  2822. //-----------------------------------------------------------------------------
  2823. // StatTrakIllum proxy
  2824. //-----------------------------------------------------------------------------
  2825. class CStatTrakIllumProxy : public CResultProxy
  2826. {
  2827. public:
  2828. virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  2829. virtual void OnBind( void *pC_BaseEntity );
  2830. private:
  2831. CFloatInput m_flMinVal;
  2832. CFloatInput m_flMaxVal;
  2833. };
  2834. bool CStatTrakIllumProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  2835. {
  2836. if ( !CResultProxy::Init( pMaterial, pKeyValues ) )
  2837. return false;
  2838. if ( !m_flMinVal.Init( pMaterial, pKeyValues, "minVal", 0.5 ) )
  2839. return false;
  2840. if ( !m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", 1 ) )
  2841. return false;
  2842. return true;
  2843. }
  2844. void CStatTrakIllumProxy::OnBind( void *pC_BaseEntity )
  2845. {
  2846. if ( !pC_BaseEntity )
  2847. return;
  2848. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  2849. if ( pEntity )
  2850. {
  2851. // StatTrak modules are children of their accompanying viewmodels
  2852. C_BaseViewModel *pViewModel = dynamic_cast<C_BaseViewModel*>( pEntity->GetMoveParent() );
  2853. if ( pViewModel )
  2854. {
  2855. //SetFloatResult( Lerp( pViewModel->GetStatTrakGlowMultiplier(), m_flMinVal.GetFloat(), m_flMaxVal.GetFloat() ) );
  2856. SetFloatResult( 0.75f );
  2857. return;
  2858. }
  2859. }
  2860. }
  2861. EXPOSE_INTERFACE( CStatTrakIllumProxy, IMaterialProxy, "StatTrakIllum" IMATERIAL_PROXY_INTERFACE_VERSION );
  2862. //-----------------------------------------------------------------------------
  2863. // StatTrak 'kill odometer' support: given a numerical value expressed as a string, pick a texture frame to represent a given digit
  2864. //-----------------------------------------------------------------------------
  2865. class CStatTrakDigitProxy : public CResultProxy
  2866. {
  2867. public:
  2868. virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  2869. virtual void OnBind( void *pC_BaseEntity );
  2870. virtual bool HelperOnBindGetStatTrakScore( void *pC_BaseEntity, int *piScore );
  2871. private:
  2872. CFloatInput m_flDisplayDigit; // the particular digit we want to display
  2873. CFloatInput m_flTrimZeros;
  2874. };
  2875. bool CStatTrakDigitProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  2876. {
  2877. if ( !CResultProxy::Init( pMaterial, pKeyValues ) )
  2878. return false;
  2879. if ( !m_flDisplayDigit.Init( pMaterial, pKeyValues, "displayDigit", 0 ) )
  2880. return false;
  2881. if ( !m_flTrimZeros.Init( pMaterial, pKeyValues, "trimZeros", 0 ) )
  2882. return false;
  2883. return true;
  2884. }
  2885. bool CStatTrakDigitProxy::HelperOnBindGetStatTrakScore( void *pC_BaseEntity, int *piScore )
  2886. {
  2887. if ( !pC_BaseEntity )
  2888. return false;
  2889. if ( !piScore )
  2890. return false;
  2891. bool bReturnValue = false;
  2892. uint32 unScore = 0;
  2893. C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  2894. if ( pEntity )
  2895. {
  2896. // StatTrak modules are children of their accompanying viewmodels
  2897. C_ViewmodelAttachmentModel *pViewModel = dynamic_cast<C_ViewmodelAttachmentModel*>( pEntity->GetMoveParent() );
  2898. if ( pViewModel )
  2899. {
  2900. //C_TFPlayer *pPlayer = ToTFPlayer( pViewModel->GetOwnerEntity() );
  2901. //if ( pPlayer )
  2902. CTFWeaponBase *pWeap = dynamic_cast<CTFWeaponBase*>( pViewModel->GetOwnerEntity() );
  2903. if ( pWeap )
  2904. {
  2905. // Use the strange prefix if the weapon has one.
  2906. if ( pWeap->GetAttributeContainer()->GetItem()->FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
  2907. {
  2908. *piScore = unScore;
  2909. bReturnValue = true;
  2910. }
  2911. }
  2912. }
  2913. }
  2914. else
  2915. {
  2916. // No Base entity, may be a straight econ item view (item model panel)
  2917. IClientRenderable *pRend = (IClientRenderable *)pC_BaseEntity;
  2918. if ( pRend )
  2919. {
  2920. const CEconItemView *pItem = dynamic_cast< CEconItemView* >( pRend );
  2921. if ( pItem && pItem->FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
  2922. {
  2923. *piScore = unScore;
  2924. bReturnValue = true;
  2925. }
  2926. }
  2927. }
  2928. return bReturnValue;
  2929. }
  2930. #ifdef STAGING_ONLY
  2931. ConVar tf_stattrak_test_score( "tf_stattrak_test_score", "-1", FCVAR_CHEAT, "" );
  2932. #endif // STAGING_ONLY
  2933. void CStatTrakDigitProxy::OnBind( void *pC_BaseEntity )
  2934. {
  2935. int nKillEaterAltScore = 0;
  2936. bool bHasScoreToDisplay = HelperOnBindGetStatTrakScore( pC_BaseEntity, &nKillEaterAltScore );
  2937. if ( !bHasScoreToDisplay )
  2938. { // Error?
  2939. //SetFloatResult( (int)fmod( gpGlobals->curtime, 10.0f ) );
  2940. SetFloatResult( 0 );
  2941. return;
  2942. }
  2943. #ifdef STAGING_ONLY
  2944. if ( tf_stattrak_test_score.GetInt() > -1 )
  2945. {
  2946. nKillEaterAltScore = tf_stattrak_test_score.GetInt();
  2947. }
  2948. #endif // STAGING_ONLY
  2949. int iDesiredDigit = (int)m_flDisplayDigit.GetFloat();
  2950. // trim preceding zeros
  2951. if ( m_flTrimZeros.GetFloat() > 0 )
  2952. {
  2953. if ( pow( 10.0f, iDesiredDigit ) > nKillEaterAltScore )
  2954. {
  2955. SetFloatResult( 10.0f ); //assumed blank frame
  2956. return;
  2957. }
  2958. }
  2959. // get the [0-9] value of the digit we want
  2960. int iDigitCount = MIN( iDesiredDigit, 10 );
  2961. for ( int i = 0; i < iDigitCount; i++ )
  2962. {
  2963. nKillEaterAltScore /= 10;
  2964. }
  2965. nKillEaterAltScore %= 10;
  2966. SetFloatResult( nKillEaterAltScore );
  2967. }
  2968. EXPOSE_INTERFACE( CStatTrakDigitProxy, IMaterialProxy, "StatTrakDigit" IMATERIAL_PROXY_INTERFACE_VERSION );
  2969. //-----------------------------------------------------------------------------
  2970. // Stattrak Icon proxy
  2971. //-----------------------------------------------------------------------------
  2972. class CStatTrakIconProxy : public CResultProxy
  2973. {
  2974. public:
  2975. virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  2976. virtual void OnBind( void *pC_BaseEntity );
  2977. private:
  2978. //CFloatInput m_flMinVal;
  2979. };
  2980. bool CStatTrakIconProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  2981. {
  2982. /*if ( !CResultProxy::Init( pMaterial, pKeyValues ) )
  2983. return false;
  2984. if ( !m_flMinVal.Init( pMaterial, pKeyValues, "minVal", 0.5 ) )
  2985. return false;
  2986. if ( !m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", 1 ) )
  2987. return false;*/
  2988. return CResultProxy::Init( pMaterial, pKeyValues );
  2989. }
  2990. ConVar tf_stattrak_icon_offset_x( "tf_stattrak_icon_offset_x", "0", FCVAR_DEVELOPMENTONLY );
  2991. ConVar tf_stattrak_icon_offset_y( "tf_stattrak_icon_offset_y", "0", FCVAR_DEVELOPMENTONLY );
  2992. ConVar tf_stattrak_icon_scale( "tf_stattrak_icon_scale", "1.0", FCVAR_DEVELOPMENTONLY );
  2993. void CStatTrakIconProxy::OnBind( void *pC_BaseEntity )
  2994. {
  2995. // Find the StatTracker Type and Lookup the offset, for now hacks!
  2996. //if ( !pC_BaseEntity )
  2997. // return;
  2998. //C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
  2999. //if ( pEntity )
  3000. //{
  3001. // // StatTrak modules are children of their accompanying viewmodels
  3002. // C_BaseViewModel *pViewModel = dynamic_cast<C_BaseViewModel*>( pEntity->GetMoveParent() );
  3003. // if ( pViewModel )
  3004. // {
  3005. // //SetFloatResult( Lerp( pViewModel->GetStatTrakGlowMultiplier(), m_flMinVal.GetFloat(), m_flMaxVal.GetFloat() ) );
  3006. // SetFloatResult( 0.75f );
  3007. // return;
  3008. // }
  3009. //}
  3010. Vector2D center( 0.5, 0.5 );
  3011. Vector2D translation( 0, 0 );
  3012. VMatrix mat, temp;
  3013. mat.Identity();
  3014. //if ( m_pCenterVar )
  3015. //{
  3016. // m_pCenterVar->GetVecValue( center.Base(), 2 );
  3017. //}
  3018. //MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
  3019. //if ( m_pScaleVar )
  3020. //{
  3021. // Vector2D scale;
  3022. // m_pScaleVar->GetVecValue( scale.Base(), 2 );
  3023. // MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
  3024. // MatrixMultiply( temp, mat, mat );
  3025. //}
  3026. //if ( m_pRotateVar )
  3027. //{
  3028. // float angle = m_pRotateVar->GetFloatValue();
  3029. // MatrixBuildRotateZ( temp, angle );
  3030. // MatrixMultiply( temp, mat, mat );
  3031. //}
  3032. //MatrixBuildTranslation( temp, center.x, center.y, 0.0f );
  3033. //MatrixMultiply( temp, mat, mat );
  3034. //if ( m_pTranslateVar )
  3035. {
  3036. //m_pTranslateVar->GetVecValue( translation.Base(), 2 );
  3037. MatrixBuildTranslation( temp, tf_stattrak_icon_offset_x.GetFloat(), tf_stattrak_icon_offset_y.GetFloat(), 0.0f );
  3038. MatrixMultiply( temp, mat, mat );
  3039. }
  3040. m_pResult->SetMatrixValue( mat );
  3041. if ( ToolsEnabled() )
  3042. {
  3043. ToolFramework_RecordMaterialParams( GetMaterial() );
  3044. }
  3045. }
  3046. EXPOSE_INTERFACE( CStatTrakIconProxy, IMaterialProxy, "StatTrakIcon" IMATERIAL_PROXY_INTERFACE_VERSION );
  3047. struct TextureVarSetter
  3048. {
  3049. TextureVarSetter( IMaterialVar* pDestVar, ITexture* pDefaultTexture )
  3050. : m_pDestVar( pDestVar )
  3051. , m_pTexture( pDefaultTexture )
  3052. {
  3053. Assert( pDestVar && pDefaultTexture );
  3054. }
  3055. void SetTexture( ITexture* pTexture ) { m_pTexture = pTexture; }
  3056. ~TextureVarSetter()
  3057. {
  3058. m_pDestVar->SetTextureValue( m_pTexture );
  3059. }
  3060. IMaterialVar* m_pDestVar;
  3061. ITexture* m_pTexture;
  3062. };
  3063. //-----------------------------------------------------------------------------
  3064. // Purpose: Used for weapon skins.
  3065. //-----------------------------------------------------------------------------
  3066. class CWeaponSkinProxy : public IMaterialProxy
  3067. {
  3068. public:
  3069. CWeaponSkinProxy( void )
  3070. : m_pMaterial( NULL )
  3071. , m_pBaseTextureVar( NULL )
  3072. , m_pBaseTextureOrig( NULL )
  3073. , m_nGeneration( CRTime::RTime32TimeCur() )
  3074. {
  3075. }
  3076. ~CWeaponSkinProxy()
  3077. {
  3078. SafeRelease( &m_pBaseTextureOrig );
  3079. }
  3080. inline ITexture* GetWeaponSkinBaseLowRes( bool bPlayerIsLocalPlayer, itemid_t nID, int iTeam ) const
  3081. {
  3082. if ( !bPlayerIsLocalPlayer )
  3083. return NULL;
  3084. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  3085. if ( !pLocalInv )
  3086. return NULL;
  3087. return pLocalInv->GetWeaponSkinBaseLowRes( nID, iTeam );
  3088. }
  3089. inline bool TestAndSetBaseTexture()
  3090. {
  3091. if ( m_pBaseTextureOrig )
  3092. {
  3093. return true;
  3094. }
  3095. Assert( m_pBaseTextureVar != NULL );
  3096. // If the material is in the process of being async loaded, the var won't be a
  3097. // texture yet, it'll be a string.
  3098. if ( !m_pBaseTextureVar->IsTexture() )
  3099. return false;
  3100. ITexture* baseTexture = m_pBaseTextureVar->GetTextureValue();
  3101. Assert( baseTexture != NULL );
  3102. SafeAssign( &m_pBaseTextureOrig, baseTexture );
  3103. return true;
  3104. }
  3105. virtual bool Init( IMaterial *pMaterial, KeyValues* pKeyValues )
  3106. {
  3107. // We don't support DX8
  3108. ConVarRef mat_dxlevel( "mat_dxlevel" );
  3109. if ( mat_dxlevel.GetInt() < 90 )
  3110. return false;
  3111. Assert( pMaterial );
  3112. m_pMaterial = pMaterial;
  3113. bool bFound = false;
  3114. m_pBaseTextureVar = m_pMaterial->FindVar( "$basetexture", &bFound );
  3115. if ( !bFound )
  3116. return false;
  3117. // If we are doing load on demand, this might not be ready yet.
  3118. // If not, then don't set it so the OnBind code knows not to rely on it.
  3119. // We don't actually care if the code succeeds here.
  3120. TestAndSetBaseTexture();
  3121. return true;
  3122. }
  3123. virtual void OnBind( void *pC_BaseEntity )
  3124. {
  3125. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  3126. // If the base texture isn't ready yet, we cannot composite. So just bail out. Not even sure what
  3127. // we could feasibly do in this case to workaround this, we don't have a texture to use yet.
  3128. if ( !TestAndSetBaseTexture() )
  3129. return;
  3130. Assert( m_pBaseTextureVar );
  3131. // This will set the texture when it goes out of scope. We can override with other textures along the way.
  3132. // This handles the return cases gracefully.
  3133. TextureVarSetter setter( m_pBaseTextureVar, m_pBaseTextureOrig );
  3134. #ifdef STAGING_ONLY
  3135. // If we're not doing paintkits, exit out now (to set the base texture correctly).
  3136. if ( tf_paint_kit_disable.GetBool() )
  3137. return;
  3138. #endif // STAGING_ONLY
  3139. CEconItemView *pItem = GetEconItemViewFromProxyEntity( pC_BaseEntity );
  3140. if ( !pItem )
  3141. return;
  3142. C_TFPlayer *pOwner = GetOwnerFromProxyEntity( pC_BaseEntity );
  3143. int desiredW = m_pBaseTextureOrig->GetActualWidth();
  3144. int desiredH = m_pBaseTextureOrig->GetActualHeight();
  3145. const bool cbPlayerIsLocalPlayer = C_TFPlayer::GetLocalTFPlayer() && pOwner == C_TFPlayer::GetLocalTFPlayer();
  3146. // Doing material overrides from the econ definitions can cause the same
  3147. // item to be referred to from multiple materials. The code treats the
  3148. // override material as the controller material.
  3149. const IMaterial* pMaterialOverride = pItem->GetMaterialOverride( pItem->GetTeamNumber() );
  3150. const bool cbIsControllingMaterial = pMaterialOverride == NULL || pMaterialOverride == m_pMaterial;
  3151. // if we're not using high res, check if we should down res
  3152. // We may force low res for some composites.
  3153. if ( pItem->ShouldWeaponSkinUseLowRes() || ( !pItem->ShouldWeaponSkinUseHighRes() && !cbPlayerIsLocalPlayer ) )
  3154. {
  3155. const int cDropMips = 2;
  3156. desiredW = Max( 1, desiredW >> cDropMips );
  3157. desiredH = Max( 1, desiredH >> cDropMips );
  3158. }
  3159. // If the object's generation isn't equal to when we told it, we need to regenerate it.
  3160. if ( cbIsControllingMaterial && ( pItem->GetWeaponSkinGeneration() != m_nGeneration || pItem->GetWeaponSkinGenerationTeam() != pItem->GetTeamNumber() ) )
  3161. {
  3162. // Skip this so we dont see a pop in staging
  3163. #ifdef STAGING_ONLY
  3164. if ( !tf_paint_kit_force_regen.GetBool() )
  3165. #endif
  3166. {
  3167. pItem->SetWeaponSkinBase( NULL );
  3168. pItem->SetWeaponSkinBaseCompositor( NULL );
  3169. }
  3170. }
  3171. ITexture* pWeaponSkinBase = pItem->GetWeaponSkinBase();
  3172. // If we have already completed the composite and stored it (or if there was an error)
  3173. // indicate that here.
  3174. if ( pWeaponSkinBase )
  3175. {
  3176. {
  3177. setter.SetTexture( pWeaponSkinBase );
  3178. // If the texture is the correct res already, we're done!
  3179. #ifdef STAGING_ONLY
  3180. if ( !tf_paint_kit_force_regen.GetBool() && desiredW == pWeaponSkinBase->GetActualWidth() && desiredH == pWeaponSkinBase->GetActualHeight() )
  3181. #else
  3182. if ( desiredW == pWeaponSkinBase->GetActualWidth() && desiredH == pWeaponSkinBase->GetActualHeight() )
  3183. #endif
  3184. return;
  3185. }
  3186. }
  3187. // If we're doing material overrides, we may get in twice--but the non-controlling material should
  3188. // bail out now.
  3189. if ( !cbIsControllingMaterial )
  3190. return;
  3191. ITextureCompositor* pWeaponSkinBaseCompositor = pItem->GetWeaponSkinBaseCompositor();
  3192. bool bUseLowRes = false;
  3193. if ( pWeaponSkinBaseCompositor )
  3194. {
  3195. ECompositeResolveStatus status = pWeaponSkinBaseCompositor->GetResolveStatus();
  3196. bool cleanupCompositor = false;
  3197. switch ( status )
  3198. {
  3199. case ECRS_Idle:
  3200. Assert( !"Unexpected state, shouldn't be idle here." );
  3201. break;
  3202. case ECRS_Scheduled:
  3203. // This is fine, this happens when multiple views ask for the same composite on the
  3204. // same frame. For example, a Model Panel and the world view.
  3205. bUseLowRes = true;
  3206. break;
  3207. case ECRS_PendingTextureLoads:
  3208. // Totally fine, try again later.
  3209. bUseLowRes = true;
  3210. break;
  3211. case ECRS_PendingComposites:
  3212. // Totally fine, try again later.
  3213. bUseLowRes = true;
  3214. break;
  3215. case ECRS_Error:
  3216. // Had an error, just show the current base texture forever.
  3217. // Is this a reasonable error handler? Seems like it is, though maybe
  3218. // we want to show the error texture for at least dev mode.
  3219. Assert( !"Error while compositing, should figure out wtf.");
  3220. pItem->SetWeaponSkinBase( m_pBaseTextureOrig );
  3221. cleanupCompositor = true;
  3222. break;
  3223. case ECRS_Complete:
  3224. // Success! Use the new texture for all time. Or whatever.
  3225. pWeaponSkinBase = pWeaponSkinBaseCompositor->GetResultTexture();
  3226. pItem->SetWeaponSkinBase( pWeaponSkinBase );
  3227. setter.SetTexture( pWeaponSkinBase );
  3228. cleanupCompositor = true;
  3229. break;
  3230. default:
  3231. Assert( !"Unexpected return value from ITextureCompositor::GetResolveStatus" );
  3232. break;
  3233. };
  3234. if ( cleanupCompositor )
  3235. {
  3236. pItem->SetWeaponSkinBaseCompositor( NULL );
  3237. pWeaponSkinBaseCompositor = NULL;
  3238. }
  3239. if ( bUseLowRes )
  3240. {
  3241. ITexture* pTex = GetWeaponSkinBaseLowRes( cbPlayerIsLocalPlayer, pItem->GetItemID(), pItem->GetTeamNumber() );
  3242. if ( pTex )
  3243. setter.SetTexture( pTex );
  3244. }
  3245. return;
  3246. }
  3247. // Start the composite.
  3248. KeyValues* rootKV = NULL;
  3249. float flWear = 0;
  3250. if ( !pItem->GetCustomPaintKitWear( flWear ) )
  3251. {
  3252. return;
  3253. }
  3254. int nWear = EconWear_ToIntCategory( flWear );
  3255. #ifdef STAGING_ONLY
  3256. if ( tf_paint_kit_force_wear.GetInt() > 0 )
  3257. {
  3258. nWear = Min( tf_paint_kit_force_wear.GetInt(), 5 );
  3259. }
  3260. #endif // STAGING_ONLY
  3261. const char* pItemName = "unknown";
  3262. const GameItemDefinition_t* tfItemDef = pItem->GetItemDefinition();
  3263. if ( tfItemDef )
  3264. {
  3265. const char* pMaybeName = tfItemDef->GetPaintKitName();
  3266. if ( pMaybeName )
  3267. pItemName = pMaybeName;
  3268. rootKV = tfItemDef->GetPaintKitWearDefinition( nWear );
  3269. }
  3270. uint32 nCompositeFlags = 0;
  3271. #ifdef STAGING_ONLY
  3272. if ( s_bIsPaintkitOverrideSet == true )
  3273. {
  3274. // Fetch the wear level KV
  3275. const char *vArgs = VarArgs( "wear_level_%d", nWear );
  3276. FOR_EACH_SUBKEY( s_kvOverridePaintkit, wearKv )
  3277. {
  3278. if ( !V_strcmp( vArgs, wearKv->GetName() ) )
  3279. {
  3280. rootKV = wearKv;
  3281. break;
  3282. }
  3283. }
  3284. nCompositeFlags = TEX_COMPOSITE_CREATE_FLAGS_FORCE;
  3285. }
  3286. if ( tf_paint_kit_generating_icons.GetBool() )
  3287. {
  3288. nCompositeFlags = TEX_COMPOSITE_CREATE_FLAGS_FORCE;
  3289. }
  3290. #endif // STAGING_ONLY
  3291. if ( rootKV )
  3292. {
  3293. uint64 seed = pItem->GetOriginalID();
  3294. #ifdef STAGING_ONLY
  3295. if ( tf_paint_kit_seed_override.GetInt() != 0 )
  3296. {
  3297. seed = tf_paint_kit_seed_override.GetInt();
  3298. }
  3299. #endif // STAGING_ONLY
  3300. Assert( pItem->GetTeamNumber() != TEAM_UNASSIGNED );
  3301. int teamNum = pItem->GetTeamNumber() != TEAM_UNASSIGNED ? pItem->GetTeamNumber() : TF_TEAM_RED;
  3302. #ifdef STAGING_ONLY
  3303. if ( tf_paint_kit_team_override.GetInt() >= 0 )
  3304. {
  3305. teamNum = tf_paint_kit_team_override.GetInt() == 0 ? TF_TEAM_RED : TF_TEAM_BLUE;
  3306. pItem->SetTeamNumber( teamNum );
  3307. }
  3308. #endif // STAGING_ONLY
  3309. char finalItemName[_MAX_PATH];
  3310. V_sprintf_safe( finalItemName, "%s_wear_%02d", pItemName, nWear );
  3311. SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( desiredW, desiredH, finalItemName, teamNum, seed, rootKV, nCompositeFlags ) );
  3312. if ( pWeaponSkinBaseCompositor )
  3313. {
  3314. pWeaponSkinBaseCompositor->ScheduleResolve();
  3315. pItem->SetWeaponSkinGeneration( m_nGeneration );
  3316. pItem->SetWeaponSkinGenerationTeam( teamNum );
  3317. if ( pWeaponSkinBaseCompositor->GetResolveStatus() != ECRS_Complete )
  3318. {
  3319. // Normal case
  3320. pItem->SetWeaponSkinBaseCompositor( pWeaponSkinBaseCompositor );
  3321. // Try to sub out the low res, if it's ready.
  3322. ITexture* pTex = GetWeaponSkinBaseLowRes( cbPlayerIsLocalPlayer, pItem->GetItemID(), pItem->GetTeamNumber() );
  3323. if ( pTex )
  3324. setter.SetTexture( pTex );
  3325. }
  3326. else
  3327. {
  3328. // Had a cache hit, so add the texture here.
  3329. pWeaponSkinBase = pWeaponSkinBaseCompositor->GetResultTexture();
  3330. pItem->SetWeaponSkinBase( pWeaponSkinBase );
  3331. setter.SetTexture( pWeaponSkinBase );
  3332. }
  3333. SafeRelease( pWeaponSkinBaseCompositor );
  3334. return;
  3335. }
  3336. }
  3337. }
  3338. virtual void Release() { delete this; }
  3339. virtual IMaterial * GetMaterial() { return m_pMaterial; }
  3340. private:
  3341. IMaterial *m_pMaterial;
  3342. IMaterialVar *m_pBaseTextureVar;
  3343. ITexture *m_pBaseTextureOrig;
  3344. RTime32 m_nGeneration;
  3345. bool m_bForceUpdate;
  3346. };
  3347. EXPOSE_INTERFACE( CWeaponSkinProxy, IMaterialProxy, "WeaponSkin" IMATERIAL_PROXY_INTERFACE_VERSION );
  3348. //-----------------------------------------------------------------------------
  3349. // Purpose: RecvProxy that converts the Player's object UtlVector to entindexes
  3350. //-----------------------------------------------------------------------------
  3351. void RecvProxy_PlayerObjectList( const CRecvProxyData *pData, void *pStruct, void *pOut )
  3352. {
  3353. C_TFPlayer *pPlayer = (C_TFPlayer*)pStruct;
  3354. CBaseHandle *pHandle = (CBaseHandle*)(&(pPlayer->m_aObjects[pData->m_iElement]));
  3355. RecvProxy_IntToEHandle( pData, pStruct, pHandle );
  3356. }
  3357. void RecvProxyArrayLength_PlayerObjects( void *pStruct, int objectID, int currentArrayLength )
  3358. {
  3359. C_TFPlayer *pPlayer = (C_TFPlayer*)pStruct;
  3360. if ( pPlayer->m_aObjects.Count() != currentArrayLength )
  3361. {
  3362. pPlayer->m_aObjects.SetSize( currentArrayLength );
  3363. }
  3364. pPlayer->ForceUpdateObjectHudState();
  3365. }
  3366. EXTERN_RECV_TABLE( DT_ScriptCreatedItem );
  3367. // specific to the local player
  3368. BEGIN_RECV_TABLE_NOBASE( C_TFPlayer, DT_TFLocalPlayerExclusive )
  3369. RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  3370. RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ) ),
  3371. RecvPropArray2(
  3372. RecvProxyArrayLength_PlayerObjects,
  3373. RecvPropInt( "player_object_array_element", 0, SIZEOF_IGNORE, 0, RecvProxy_PlayerObjectList ),
  3374. MAX_OBJECTS_PER_PLAYER,
  3375. 0,
  3376. "player_object_array" ),
  3377. RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ), // No longer used by the local player, could be omitted. Preserved for backwards-compat for now.
  3378. // RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
  3379. RecvPropBool( RECVINFO( m_bIsCoaching ) ),
  3380. RecvPropEHandle( RECVINFO( m_hCoach ) ),
  3381. RecvPropEHandle( RECVINFO( m_hStudent ) ),
  3382. RecvPropInt( RECVINFO( m_nCurrency ) ),
  3383. RecvPropInt( RECVINFO( m_nExperienceLevel ) ),
  3384. RecvPropInt( RECVINFO( m_nExperienceLevelProgress ) ),
  3385. RecvPropInt( RECVINFO( m_bMatchSafeToLeave ) ),
  3386. END_RECV_TABLE()
  3387. // all players except the local player
  3388. BEGIN_RECV_TABLE_NOBASE( C_TFPlayer, DT_TFNonLocalPlayerExclusive )
  3389. RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  3390. RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ) ),
  3391. RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
  3392. RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
  3393. END_RECV_TABLE()
  3394. //-----------------------------------------------------------------------------
  3395. // Purpose: Data that gets sent to attached medics
  3396. //-----------------------------------------------------------------------------
  3397. BEGIN_RECV_TABLE_NOBASE( C_TFPlayer, DT_TFSendHealersDataTable )
  3398. RecvPropInt( RECVINFO( m_nActiveWpnClip ) ),
  3399. END_RECV_TABLE()
  3400. IMPLEMENT_CLIENTCLASS_DT( C_TFPlayer, DT_TFPlayer, CTFPlayer )
  3401. RecvPropBool(RECVINFO(m_bSaveMeParity)),
  3402. RecvPropBool(RECVINFO(m_bIsMiniBoss)),
  3403. RecvPropBool(RECVINFO(m_bIsABot)),
  3404. RecvPropInt(RECVINFO(m_nBotSkill)),
  3405. // This will create a race condition will the local player, but the data will be the same so.....
  3406. RecvPropInt( RECVINFO( m_nWaterLevel ) ),
  3407. RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
  3408. RecvPropDataTable( RECVINFO_DT( m_PlayerClass ), 0, &REFERENCE_RECV_TABLE( DT_TFPlayerClassShared ) ),
  3409. RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_TFPlayerShared ) ),
  3410. RecvPropEHandle( RECVINFO(m_hItem ) ),
  3411. RecvPropDataTable( "tflocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_TFLocalPlayerExclusive) ),
  3412. RecvPropDataTable( "tfnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_TFNonLocalPlayerExclusive) ),
  3413. RecvPropBool( RECVINFO( m_bAllowMoveDuringTaunt ) ),
  3414. RecvPropBool( RECVINFO( m_bIsReadyToHighFive ) ),
  3415. RecvPropEHandle( RECVINFO( m_hHighFivePartner ) ),
  3416. RecvPropInt( RECVINFO( m_nForceTauntCam ) ),
  3417. RecvPropFloat( RECVINFO( m_flTauntYaw ) ),
  3418. RecvPropInt( RECVINFO( m_nActiveTauntSlot ) ),
  3419. RecvPropInt( RECVINFO( m_iTauntItemDefIndex ) ),
  3420. RecvPropFloat( RECVINFO( m_flCurrentTauntMoveSpeed ) ),
  3421. RecvPropFloat( RECVINFO( m_flVehicleReverseTime ) ),
  3422. RecvPropFloat( RECVINFO( m_flLastDamageTime ) ),
  3423. RecvPropBool( RECVINFO( m_bInPowerPlay ) ),
  3424. RecvPropInt( RECVINFO( m_iSpawnCounter ) ),
  3425. RecvPropBool( RECVINFO( m_bArenaSpectator ) ),
  3426. RecvPropDataTable( RECVINFO_DT( m_AttributeManager ), 0, &REFERENCE_RECV_TABLE(DT_AttributeManager) ),
  3427. RecvPropFloat( RECVINFO( m_flHeadScale ) ),
  3428. RecvPropFloat( RECVINFO( m_flTorsoScale ) ),
  3429. RecvPropFloat( RECVINFO( m_flHandScale ) ),
  3430. RecvPropBool( RECVINFO( m_bUseBossHealthBar ) ),
  3431. RecvPropBool( RECVINFO( m_bUsingVRHeadset ) ),
  3432. RecvPropBool( RECVINFO( m_bForcedSkin ) ),
  3433. RecvPropInt( RECVINFO( m_nForcedSkin ) ),
  3434. RecvPropBool( RECVINFO( m_bGlowEnabled ) ),
  3435. RecvPropDataTable("TFSendHealersDataTable", 0, 0, &REFERENCE_RECV_TABLE( DT_TFSendHealersDataTable ) ),
  3436. RecvPropFloat( RECVINFO( m_flKartNextAvailableBoost ) ),
  3437. RecvPropInt( RECVINFO( m_iKartHealth ) ),
  3438. RecvPropInt( RECVINFO( m_iKartState ) ),
  3439. RecvPropEHandle( RECVINFO( m_hGrapplingHookTarget ) ),
  3440. RecvPropEHandle( RECVINFO( m_hSecondaryLastWeapon ) ),
  3441. RecvPropBool( RECVINFO( m_bUsingActionSlot ) ),
  3442. RecvPropFloat( RECVINFO( m_flInspectTime ) ),
  3443. RecvPropInt( RECVINFO( m_iCampaignMedals ) ),
  3444. RecvPropInt( RECVINFO( m_iPlayerSkinOverride ) ),
  3445. END_RECV_TABLE()
  3446. BEGIN_PREDICTION_DATA( C_TFPlayer )
  3447. DEFINE_PRED_TYPEDESCRIPTION( m_Shared, CTFPlayerShared ),
  3448. DEFINE_PRED_FIELD( m_nSkin, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ),
  3449. DEFINE_PRED_FIELD( m_nBody, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ),
  3450. DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  3451. DEFINE_PRED_FIELD( m_flPlaybackRate, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  3452. DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  3453. DEFINE_PRED_ARRAY_TOL( m_flEncodedController, FIELD_FLOAT, MAXSTUDIOBONECTRLS, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE, 0.02f ),
  3454. DEFINE_PRED_FIELD( m_nNewSequenceParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  3455. DEFINE_PRED_FIELD( m_nResetEventsParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  3456. DEFINE_PRED_FIELD( m_nMuzzleFlashParity, FIELD_CHARACTER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ),
  3457. DEFINE_PRED_FIELD( m_hOffHandWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
  3458. DEFINE_PRED_FIELD( m_flTauntYaw, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  3459. DEFINE_PRED_FIELD( m_flCurrentTauntMoveSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  3460. DEFINE_PRED_FIELD( m_flVehicleReverseTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  3461. DEFINE_PRED_FIELD( m_flInspectTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  3462. END_PREDICTION_DATA()
  3463. // ------------------------------------------------------------------------------------------ //
  3464. // C_TFPlayer implementation.
  3465. // ------------------------------------------------------------------------------------------ //
  3466. C_TFPlayer::C_TFPlayer() :
  3467. m_iv_angEyeAngles( "C_TFPlayer::m_iv_angEyeAngles" ),
  3468. m_mapOverheadEffects( DefLessFunc( const char * ) )
  3469. {
  3470. m_pAttributes = this;
  3471. m_PlayerAnimState = CreateTFPlayerAnimState( this );
  3472. m_Shared.Init( this );
  3473. m_iIDEntIndex = 0;
  3474. AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
  3475. memset( m_pKartParticles, NULL, sizeof( m_pKartParticles ) );
  3476. memset( m_pKartSounds, NULL, sizeof( m_pKartSounds ) );
  3477. m_pMVMEyeGlowEffect[ 0 ] = NULL;
  3478. m_pMVMEyeGlowEffect[ 1 ] = NULL;
  3479. m_pEyeGlowEffect[ 0 ] = NULL;
  3480. m_pEyeGlowEffect[ 1 ] = NULL;
  3481. m_pszEyeGlowEffectName[0] = '\0';
  3482. m_vEyeGlowColor1.Zero();
  3483. m_vEyeGlowColor2.Zero();
  3484. m_flNextSheenStartTime = 0;
  3485. m_pTeleporterEffect = NULL;
  3486. m_pBurningSound = NULL;
  3487. m_pBurningEffect = NULL;
  3488. m_pUrineEffect = NULL;
  3489. m_pMilkEffect = NULL;
  3490. m_pSoldierOffensiveBuffEffect = NULL;
  3491. m_pSoldierDefensiveBuffEffect = NULL;
  3492. m_pSoldierOffensiveHealthRegenBuffEffect = NULL;
  3493. m_pSoldierNoHealingDamageBuffEffect = NULL;
  3494. m_pCritBoostEffect = NULL;
  3495. m_flBurnEffectStartTime = 0;
  3496. m_pDisguisingEffect = NULL;
  3497. m_pSaveMeEffect = NULL;
  3498. m_pTauntWithMeEffect = NULL;
  3499. m_hOldObserverTarget = NULL;
  3500. m_iOldObserverMode = OBS_MODE_NONE;
  3501. m_pStunnedEffect = NULL;
  3502. m_pPhaseStandingEffect = NULL;
  3503. m_pRadiusHealEffect = NULL;
  3504. m_pKingRuneRadiusEffect = NULL;
  3505. m_pKingBuffRadiusEffect = NULL;
  3506. m_pRunePlagueEffect = NULL;
  3507. m_pMegaHealEffect = NULL;
  3508. m_pTempShield = NULL;
  3509. m_pMVMBotRadiowave = NULL;
  3510. m_pRuneChargeReadyEffect = NULL;
  3511. m_aGibs.Purge();
  3512. m_aNormalGibs.PurgeAndDeleteElements();
  3513. m_aSillyGibs.Purge();
  3514. m_bCigaretteSmokeActive = false;
  3515. m_hRagdoll.Set( NULL );
  3516. m_iPreviousMetal = 0;
  3517. m_bIsDisplayingNemesisIcon = false;
  3518. m_bIsDisplayingDuelingIcon = false;
  3519. m_bIsDisplayingIconForIT = false;
  3520. m_bShouldShowBirthdayEffect = false;
  3521. m_bWasTaunting = false;
  3522. m_angTauntPredViewAngles.Init();
  3523. m_angTauntEngViewAngles.Init();
  3524. m_pTauntSoundLoop = NULL;
  3525. m_flWaterImpactTime = 0.0f;
  3526. m_rtSpottedInPVSTime = 0;
  3527. m_rtJoinedSpectatorTeam = 0;
  3528. m_rtJoinedNormalTeam = 0;
  3529. m_flWaterEntryTime = 0;
  3530. m_nOldWaterLevel = WL_NotInWater;
  3531. m_bWaterExitEffectActive = false;
  3532. m_bUpdateObjectHudState = false;
  3533. m_flSaveMeExpireTime = 0;
  3534. m_bWasHealedByLocalPlayer = false;
  3535. m_bDuckJumpInterp = false;
  3536. m_flFirstDuckJumpInterp = 0.0f;
  3537. m_flLastDuckJumpInterp = 0.0f;
  3538. m_flDuckJumpInterp = 0.0f;
  3539. m_bIsCoaching = false;
  3540. m_pStudentGlowEffect = NULL;
  3541. m_pPowerupGlowEffect = NULL;
  3542. m_nBotSkill = -1;
  3543. m_nOldBotSkill = -1;
  3544. m_nOldMaxHealth = -1;
  3545. m_bIsCalculatingMaximumSpeed = false;
  3546. m_bBodygroupsDirty = false;
  3547. m_pBlastJumpLoop = NULL;
  3548. m_flBlastJumpLaunchTime = 0.f;
  3549. m_nExperienceLevel = 0;
  3550. m_nExperienceLevelProgress = 0;
  3551. m_nPrevExperienceLevel = 0;
  3552. m_bMatchSafeToLeave = true;
  3553. for( int i=0; i<kBonusEffect_Count; ++i )
  3554. {
  3555. m_flNextMiniCritEffectTime[i] = 0;
  3556. }
  3557. m_flHeadScale = 1.f;
  3558. m_flTorsoScale = 1.f;
  3559. m_flHandScale = 1.f;
  3560. m_bIsMiniBoss = false;
  3561. m_bUseBossHealthBar = false;
  3562. m_bUsingVRHeadset = false;
  3563. m_bForcedSkin = false;
  3564. m_nForcedSkin = 0;
  3565. m_flChangeClassTime = 0.f;
  3566. m_hRevivePrompt = NULL;
  3567. m_bIsDisplayingTranqMark = false;
  3568. m_eDisplayingRuneIcon = RUNE_NONE;
  3569. m_pKart = NULL;
  3570. m_iOldKartHealth = 0;
  3571. m_bUsingActionSlot = false;
  3572. m_iCampaignMedals = 0;
  3573. m_flInspectTime = 0.f;
  3574. m_bNotifiedWeaponInspectThisLife = false;
  3575. m_pPasstimePlayerReticle = NULL;
  3576. m_pPasstimeAskForBallReticle = NULL;
  3577. m_iPlayerSkinOverride = 0;
  3578. ListenForGameEvent( "player_hurt" );
  3579. ListenForGameEvent( "hltv_changed_mode" );
  3580. ListenForGameEvent( "hltv_changed_target" );
  3581. ListenForGameEvent( "post_inventory_application" );
  3582. ListenForGameEvent( "rocket_jump" );
  3583. ListenForGameEvent( "rocket_jump_landed" );
  3584. ListenForGameEvent( "sticky_jump" );
  3585. ListenForGameEvent( "sticky_jump_landed" );
  3586. ListenForGameEvent( "player_spawn" );
  3587. ListenForGameEvent( "damage_resisted" );
  3588. ListenForGameEvent( "revive_player_notify" );
  3589. ListenForGameEvent( "revive_player_stopped" );
  3590. ListenForGameEvent( "player_changeclass" );
  3591. ListenForGameEvent( "player_abandoned_match" );
  3592. #ifdef STAGING_ONLY
  3593. ListenForGameEvent( "player_death" );
  3594. #endif
  3595. //AddPhonemeFile
  3596. engine->AddPhonemeFile( "scripts/game_sounds_vo_phonemes.txt" );
  3597. engine->AddPhonemeFile( "scripts/game_sounds_vo_phonemes_local.txt" ); // Stomp over english for phoneme data
  3598. engine->AddPhonemeFile( NULL ); // Null indicates to engine that we are done loading phonemes if there are any present
  3599. }
  3600. C_TFPlayer::~C_TFPlayer()
  3601. {
  3602. ShowNemesisIcon( false );
  3603. ShowDuelingIcon( false );
  3604. m_PlayerAnimState->Release();
  3605. if ( m_pBlastJumpLoop )
  3606. {
  3607. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  3608. controller.SoundDestroy( m_pBlastJumpLoop );
  3609. m_pBlastJumpLoop = NULL;
  3610. }
  3611. StopTauntSoundLoop();
  3612. delete m_pPasstimePlayerReticle;
  3613. delete m_pPasstimeAskForBallReticle;
  3614. if ( IsLocalPlayer() )
  3615. {
  3616. g_ItemEffectMeterManager.ClearExistingMeters();
  3617. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  3618. {
  3619. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "CompetitiveGame_RestoreChatWindow", false );
  3620. }
  3621. }
  3622. }
  3623. // NOTE: This is NOT called every time the player respawns!!
  3624. // only the first time we spawn a player into the world
  3625. void C_TFPlayer::Spawn( void )
  3626. {
  3627. m_AttributeManager.SetPlayer( this );
  3628. m_AttributeList.SetManager( &m_AttributeManager );
  3629. BaseClass::Spawn();
  3630. /*
  3631. // some extra stuff here because s_pLocalPlayer is not yet initialized
  3632. int iLocalPlayerIndex = engine->GetLocalPlayer();
  3633. if ( entindex() == iLocalPlayerIndex && !m_LeaveServerTimer.HasStarted() )
  3634. {
  3635. ConVarRef random_spec_server_mode( "random_spec_server_mode" );
  3636. if ( random_spec_server_mode.IsValid() && random_spec_server_mode.GetBool() )
  3637. {
  3638. m_LeaveServerTimer.Start( spectate_random_server_basetime.GetFloat() );
  3639. }
  3640. }
  3641. */
  3642. UpdateInventory( true );
  3643. UpdateMVMEyeGlowEffect( true );
  3644. SetShowHudMenuTauntSelection( false );
  3645. CleanUpAnimationOnSpawn();
  3646. }
  3647. //-----------------------------------------------------------------------------
  3648. // Purpose:
  3649. //-----------------------------------------------------------------------------
  3650. void C_TFPlayer::InventoryUpdated( CPlayerInventory *pInventory )
  3651. {
  3652. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  3653. {
  3654. CHudUpgradePanel *pUpgradePanel = GET_HUDELEMENT( CHudUpgradePanel );
  3655. if ( pUpgradePanel && pUpgradePanel->IsVisible() )
  3656. {
  3657. pUpgradePanel->PlayerInventoryChanged( this );
  3658. }
  3659. }
  3660. return;
  3661. }
  3662. //-----------------------------------------------------------------------------
  3663. // Purpose: Request this player's inventories from the steam backend
  3664. //-----------------------------------------------------------------------------
  3665. void C_TFPlayer::UpdateInventory( bool bInit )
  3666. {
  3667. #if !defined(NO_STEAM)
  3668. if ( bInit )
  3669. {
  3670. CSteamID steamIDForPlayer;
  3671. if ( GetSteamID( &steamIDForPlayer ) )
  3672. {
  3673. TFInventoryManager()->SteamRequestInventory( &m_Inventory, steamIDForPlayer, this );
  3674. }
  3675. }
  3676. // If we have an SOCache, we've got a connection to the GC
  3677. bool bInvalid = true;
  3678. if ( m_Inventory.GetSOC() )
  3679. {
  3680. bInvalid = (m_Inventory.GetSOC()->BIsInitialized() == false);
  3681. }
  3682. m_bInventoryReceived = !bInvalid;
  3683. #endif
  3684. }
  3685. C_TFPlayer* C_TFPlayer::GetLocalTFPlayer()
  3686. {
  3687. return ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
  3688. }
  3689. const QAngle& C_TFPlayer::GetRenderAngles()
  3690. {
  3691. if ( IsRagdoll() )
  3692. {
  3693. return vec3_angle;
  3694. }
  3695. else
  3696. {
  3697. return m_PlayerAnimState->GetRenderAngles();
  3698. }
  3699. }
  3700. bool C_TFPlayer::CanDisplayAllSeeEffect( EAttackBonusEffects_t effect ) const
  3701. {
  3702. if( effect >= EAttackBonusEffects_t(0) && effect < kBonusEffect_Count )
  3703. {
  3704. return gpGlobals->curtime > m_flNextMiniCritEffectTime[ effect ];
  3705. }
  3706. return true;
  3707. }
  3708. void C_TFPlayer::SetNextAllSeeEffectTime( EAttackBonusEffects_t effect, float flTime )
  3709. {
  3710. if( effect >= EAttackBonusEffects_t(0) && effect < kBonusEffect_Count )
  3711. {
  3712. if ( gpGlobals->curtime > m_flNextMiniCritEffectTime[ effect ] )
  3713. {
  3714. m_flNextMiniCritEffectTime[ effect ] = flTime;
  3715. }
  3716. }
  3717. }
  3718. //-----------------------------------------------------------------------------
  3719. // Purpose:
  3720. //-----------------------------------------------------------------------------
  3721. void C_TFPlayer::UpdateOnRemove( void )
  3722. {
  3723. // Stop the taunt.
  3724. if ( m_bWasTaunting )
  3725. {
  3726. // Need to go ahead and call both. Otherwise, if we changelevel while we're taunting or
  3727. // otherwise in "game wants us in third person mode", we will stay in third person mode
  3728. // in the new map.
  3729. TurnOffTauntCam();
  3730. TurnOffTauntCam_Finish();
  3731. }
  3732. // HACK!!! ChrisG needs to fix this in the particle system.
  3733. ParticleProp()->OwnerSetDormantTo( true );
  3734. ParticleProp()->StopParticlesInvolving( this );
  3735. m_Shared.RemoveAllCond();
  3736. m_Inventory.RemoveListener( this );
  3737. BaseClass::UpdateOnRemove();
  3738. }
  3739. //-----------------------------------------------------------------------------
  3740. // Purpose: returns max health for this player
  3741. //-----------------------------------------------------------------------------
  3742. int C_TFPlayer::GetMaxHealth( void ) const
  3743. {
  3744. return ( g_TF_PR ) ? g_TF_PR->GetMaxHealth( entindex() ) : 1;
  3745. }
  3746. //-----------------------------------------------------------------------------
  3747. // Purpose: returns max buffed health for this player
  3748. //-----------------------------------------------------------------------------
  3749. int C_TFPlayer::GetMaxHealthForBuffing( void ) const
  3750. {
  3751. return ( g_TF_PR ) ? g_TF_PR->GetMaxHealthForBuffing( entindex() ) : 1;
  3752. }
  3753. //-----------------------------------------------------------------------------
  3754. // Deal with recording
  3755. //-----------------------------------------------------------------------------
  3756. void C_TFPlayer::GetToolRecordingState( KeyValues *msg )
  3757. {
  3758. #ifndef _XBOX
  3759. BaseClass::GetToolRecordingState( msg );
  3760. BaseEntityRecordingState_t *pBaseEntityState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
  3761. bool bDormant = IsDormant();
  3762. bool bDead = !IsAlive();
  3763. bool bSpectator = ( GetTeamNumber() == TEAM_SPECTATOR );
  3764. bool bNoRender = ( GetRenderMode() == kRenderNone );
  3765. bool bDeathCam = (GetObserverMode() == OBS_MODE_DEATHCAM);
  3766. bool bNoDraw = IsEffectActive(EF_NODRAW);
  3767. bool bVisible =
  3768. !bDormant &&
  3769. !bDead &&
  3770. !bSpectator &&
  3771. !bNoRender &&
  3772. !bDeathCam &&
  3773. !bNoDraw;
  3774. bool changed = m_bToolRecordingVisibility != bVisible;
  3775. // Remember state
  3776. m_bToolRecordingVisibility = bVisible;
  3777. pBaseEntityState->m_bVisible = bVisible;
  3778. if ( changed && !bVisible )
  3779. {
  3780. // If the entity becomes invisible this frame, we still want to record a final animation sample so that we have data to interpolate
  3781. // toward just before the logs return "false" for visiblity. Otherwise the animation will freeze on the last frame while the model
  3782. // is still able to render for just a bit.
  3783. pBaseEntityState->m_bRecordFinalVisibleSample = true;
  3784. }
  3785. #endif
  3786. }
  3787. void C_TFPlayer::UpdateClientSideAnimation()
  3788. {
  3789. // Update the animation data. It does the local check here so this works when using
  3790. // a third-person camera (and we don't have valid player angles).
  3791. if ( this == C_TFPlayer::GetLocalTFPlayer() )
  3792. {
  3793. // m_angEyeAngles comes from the server, and updates are infrequent, so use the local values instead.
  3794. QAngle LocalEyeAngles = EyeAngles();
  3795. m_PlayerAnimState->Update( LocalEyeAngles[YAW], LocalEyeAngles[PITCH] );
  3796. }
  3797. else
  3798. {
  3799. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  3800. }
  3801. // StatTrak Module Test
  3802. // Update ViewModels
  3803. // We only update the view model for the local player.
  3804. //if ( IsLocalPlayer() )
  3805. {
  3806. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  3807. if ( pWeapon )
  3808. {
  3809. pWeapon->UpdateAllViewmodelAddons();
  3810. }
  3811. }
  3812. BaseClass::UpdateClientSideAnimation();
  3813. }
  3814. //-----------------------------------------------------------------------------
  3815. // Purpose:
  3816. //-----------------------------------------------------------------------------
  3817. void C_TFPlayer::SetDormant( bool bDormant )
  3818. {
  3819. // If I'm burning, stop the burning sounds
  3820. if ( !IsDormant() && bDormant )
  3821. {
  3822. if ( m_pBurningSound)
  3823. {
  3824. StopBurningSound();
  3825. }
  3826. if ( m_bIsDisplayingNemesisIcon )
  3827. {
  3828. ShowNemesisIcon( false );
  3829. }
  3830. if ( m_bIsDisplayingDuelingIcon )
  3831. {
  3832. ShowDuelingIcon( false );
  3833. }
  3834. if ( m_bIsDisplayingIconForIT )
  3835. {
  3836. ShowIconForIT( false );
  3837. }
  3838. UpdatedMarkedForDeathEffect( true );
  3839. UpdateRuneIcon( true );
  3840. #ifdef STAGING_ONLY
  3841. if ( m_bIsDisplayingTranqMark )
  3842. {
  3843. UpdateTranqMark( false, true );
  3844. }
  3845. #endif // STAGING_ONLY
  3846. if ( m_bShouldShowBirthdayEffect )
  3847. {
  3848. ShowBirthdayEffect( false );
  3849. }
  3850. }
  3851. if ( IsDormant() && !bDormant )
  3852. {
  3853. SetBodygroupsDirty();
  3854. if ( IsTaunting() )
  3855. {
  3856. float flCycle = 0.f;
  3857. if ( m_flTauntDuration > 0.f )
  3858. {
  3859. float dt = gpGlobals->curtime - m_flTauntStartTime;
  3860. while ( dt >= m_flTauntDuration )
  3861. {
  3862. dt -= m_flTauntDuration;
  3863. }
  3864. flCycle = dt / m_flTauntDuration;
  3865. flCycle = clamp( flCycle, 0.f, 1.0f );
  3866. }
  3867. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  3868. m_PlayerAnimState->AddVCDSequenceToGestureSlot( GESTURE_SLOT_VCD, m_nTauntSequence, flCycle, true );
  3869. }
  3870. }
  3871. if ( bDormant == false )
  3872. {
  3873. m_rtSpottedInPVSTime = steamapicontext && steamapicontext->SteamUtils() ? steamapicontext->SteamUtils()->GetServerRealTime() : CRTime::RTime32TimeCur();
  3874. }
  3875. // Deliberately skip base combat weapon
  3876. C_BaseEntity::SetDormant( bDormant );
  3877. }
  3878. //-----------------------------------------------------------------------------
  3879. // Purpose:
  3880. //-----------------------------------------------------------------------------
  3881. void C_TFPlayer::OnPreDataChanged( DataUpdateType_t updateType )
  3882. {
  3883. BaseClass::OnPreDataChanged( updateType );
  3884. m_iOldHealth = m_iHealth;
  3885. m_iOldPlayerClass = m_PlayerClass.GetClassIndex();
  3886. m_iOldSpawnCounter = m_iSpawnCounter;
  3887. m_bOldSaveMeParity = m_bSaveMeParity;
  3888. m_nOldWaterLevel = GetWaterLevel();
  3889. m_iOldTeam = GetTeamNumber();
  3890. C_TFPlayerClass *pClass = GetPlayerClass();
  3891. m_iOldClass = pClass ? pClass->GetClassIndex() : TF_CLASS_UNDEFINED;
  3892. m_bDisguised = m_Shared.InCond( TF_COND_DISGUISED );
  3893. m_iOldDisguiseTeam = m_Shared.GetDisguiseTeam();
  3894. m_iOldDisguiseClass = m_Shared.GetDisguiseClass();
  3895. m_flPrevTauntYaw = m_flTauntYaw;
  3896. m_nPrevTauntSlot = m_nActiveTauntSlot;
  3897. m_iPrevTauntItemDefIndex = m_iTauntItemDefIndex;
  3898. if ( !IsReplay() )
  3899. {
  3900. m_iOldObserverMode = GetObserverMode();
  3901. m_hOldObserverTarget = GetObserverTarget();
  3902. }
  3903. m_nOldCurrency = m_nCurrency;
  3904. m_Shared.OnPreDataChanged();
  3905. m_bOldCustomModelVisible = m_PlayerClass.CustomModelIsVisibleToSelf();
  3906. }
  3907. //-----------------------------------------------------------------------------
  3908. // Purpose:
  3909. //-----------------------------------------------------------------------------
  3910. void C_TFPlayer::OnDataChanged( DataUpdateType_t updateType )
  3911. {
  3912. // C_BaseEntity assumes we're networking the entity's angles, so pretend that it
  3913. // networked the same value we already have.
  3914. SetNetworkAngles( GetLocalAngles() );
  3915. BaseClass::OnDataChanged( updateType );
  3916. if ( updateType == DATA_UPDATE_CREATED )
  3917. {
  3918. SetNextClientThink( CLIENT_THINK_ALWAYS );
  3919. InitInvulnerableMaterial();
  3920. // There used to be code here to switch to first person. This breaks thirdperson mode
  3921. // and karts when we have to get a full client update--don't do it here anymore.
  3922. // We may need to do something more clever if this breaks something.
  3923. }
  3924. else
  3925. {
  3926. if ( m_iOldTeam != GetTeamNumber() || m_iOldDisguiseTeam != m_Shared.GetDisguiseTeam() )
  3927. {
  3928. InitInvulnerableMaterial();
  3929. }
  3930. if ( m_iOldDisguiseClass != m_Shared.GetDisguiseClass() )
  3931. {
  3932. RemoveAllDecals();
  3933. }
  3934. if ( m_nOldBotSkill != m_nBotSkill )
  3935. {
  3936. UpdateMVMEyeGlowEffect( true );
  3937. m_nOldBotSkill = m_nBotSkill;
  3938. }
  3939. if ( m_nOldMaxHealth != GetMaxHealth() )
  3940. {
  3941. UpdateMVMEyeGlowEffect( true );
  3942. m_nOldMaxHealth = GetMaxHealth();
  3943. }
  3944. if ( m_nPrevTauntSlot != m_nActiveTauntSlot || m_iPrevTauntItemDefIndex != m_iTauntItemDefIndex )
  3945. {
  3946. UpdateTauntItem();
  3947. }
  3948. if ( m_iOldKartHealth != m_iKartHealth )
  3949. {
  3950. UpdateKartEffects();
  3951. }
  3952. if ( m_iOldKartState != m_iKartState )
  3953. {
  3954. UpdateKartState();
  3955. }
  3956. }
  3957. GetAttributeManager()->OnDataChanged( updateType );
  3958. // Check for full health and remove decals.
  3959. if ( ( m_iHealth > m_iOldHealth && m_iHealth >= GetMaxHealth() ) || m_Shared.IsInvulnerable() )
  3960. {
  3961. // If we were just fully healed, remove all decals
  3962. RemoveAllDecals();
  3963. }
  3964. if ( ( m_iOldHealth != m_iHealth ) || ( m_iOldTeam != GetTeamNumber() ) )
  3965. {
  3966. UpdateGlowColor();
  3967. }
  3968. bool bNeedsStudentGlow = m_hCoach && m_hCoach->IsLocalPlayer() && m_hCoach->m_bIsCoaching;
  3969. bool bHasStudentGlow = m_pStudentGlowEffect != NULL;
  3970. if ( bNeedsStudentGlow != bHasStudentGlow )
  3971. {
  3972. UpdateGlowEffect();
  3973. }
  3974. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  3975. {
  3976. if ( m_Shared.InCond( TF_COND_KING_BUFFED ) )
  3977. {
  3978. const char *m_szRadiusEffect;
  3979. int nTeamNumber = GetTeamNumber();
  3980. if ( IsPlayerClass( TF_CLASS_SPY ) && m_Shared.InCond( TF_COND_DISGUISED ) )
  3981. {
  3982. if ( !IsLocalPlayer() && GetTeamNumber() == GetLocalPlayerTeam() ) // Always display own team colors even when disguised, unless it's you (same rules as uber skin)
  3983. {
  3984. nTeamNumber = GetLocalPlayerTeam();
  3985. }
  3986. else
  3987. {
  3988. nTeamNumber = m_Shared.GetDisguiseTeam();
  3989. }
  3990. }
  3991. if ( nTeamNumber == TF_TEAM_RED )
  3992. {
  3993. m_szRadiusEffect = "powerup_king_red";
  3994. }
  3995. else
  3996. {
  3997. m_szRadiusEffect = "powerup_king_blue";
  3998. }
  3999. if ( !m_pKingBuffRadiusEffect )
  4000. {
  4001. m_pKingBuffRadiusEffect = ParticleProp()->Create( m_szRadiusEffect, PATTACH_ABSORIGIN_FOLLOW );
  4002. }
  4003. }
  4004. else if ( m_pKingBuffRadiusEffect )
  4005. {
  4006. m_Shared.EndKingBuffRadiusEffect();
  4007. }
  4008. bool bNeedsPowerupGlow = ShouldShowPowerupGlowEffect();
  4009. bool bHasPowerupGlow = m_pPowerupGlowEffect != NULL;
  4010. if ( bNeedsPowerupGlow != bHasPowerupGlow )
  4011. {
  4012. UpdateGlowEffect();
  4013. }
  4014. }
  4015. // Detect class changes
  4016. if ( m_iOldPlayerClass != m_PlayerClass.GetClassIndex() )
  4017. {
  4018. OnPlayerClassChange();
  4019. }
  4020. bool bJustSpawned = false;
  4021. if ( m_iOldSpawnCounter != m_iSpawnCounter )
  4022. {
  4023. ClientPlayerRespawn();
  4024. bJustSpawned = true;
  4025. }
  4026. if ( m_bSaveMeParity != m_bOldSaveMeParity )
  4027. {
  4028. // Player has triggered a save me command
  4029. CreateSaveMeEffect();
  4030. }
  4031. // To better support old demos, which have some screwed up flags, we just ignore various things if we're a SourceTV client.
  4032. if ( !IsHLTV() )
  4033. {
  4034. if ( m_Shared.InCond( TF_COND_BURNING ) && !m_pBurningSound )
  4035. {
  4036. StartBurningSound();
  4037. }
  4038. bool bShouldShowIconForIT = TFGameRules() && TFGameRules()->IsIT( this ) && !IsLocalPlayer();
  4039. if ( bShouldShowIconForIT != m_bIsDisplayingIconForIT )
  4040. {
  4041. ShowIconForIT( bShouldShowIconForIT );
  4042. }
  4043. bool bShouldShowBirthdayEffect = false;//TFGameRules() && ( TFGameRules()->GetBirthdayPlayer() == this ) && !IsLocalPlayer();
  4044. if ( bShouldShowBirthdayEffect != m_bShouldShowBirthdayEffect )
  4045. {
  4046. ShowBirthdayEffect( bShouldShowBirthdayEffect );
  4047. }
  4048. bool bShouldShowDuelingIcon = ShouldShowDuelingIcon();
  4049. if ( bShouldShowDuelingIcon != m_bIsDisplayingDuelingIcon )
  4050. {
  4051. ShowDuelingIcon( bShouldShowDuelingIcon );
  4052. }
  4053. // See if we should show or hide nemesis icon for this player
  4054. bool bShouldDisplayNemesisIcon = ( !bShouldShowDuelingIcon && !m_bIsDisplayingIconForIT && ShouldShowNemesisIcon() );
  4055. if ( bShouldDisplayNemesisIcon != m_bIsDisplayingNemesisIcon )
  4056. {
  4057. ShowNemesisIcon( bShouldDisplayNemesisIcon );
  4058. }
  4059. m_Shared.OnDataChanged();
  4060. if ( m_bDisguised != m_Shared.InCond( TF_COND_DISGUISED ) )
  4061. {
  4062. m_flDisguiseEndEffectStartTime = MAX( m_flDisguiseEndEffectStartTime, gpGlobals->curtime );
  4063. // Update visibility of any worn items.
  4064. UpdateWearables();
  4065. SetBodygroupsDirty();
  4066. // Remove decals.
  4067. RemoveAllDecals();
  4068. if ( GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY )
  4069. {
  4070. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  4071. {
  4072. UpdateMVMEyeGlowEffect( false );
  4073. }
  4074. else
  4075. {
  4076. UpdateMVMEyeGlowEffect( true );
  4077. }
  4078. }
  4079. }
  4080. }
  4081. int nNewWaterLevel = GetWaterLevel();
  4082. if ( nNewWaterLevel != m_nOldWaterLevel )
  4083. {
  4084. if ( ( m_nOldWaterLevel == WL_NotInWater ) && ( nNewWaterLevel > WL_NotInWater ) )
  4085. {
  4086. // Set when we do a transition to/from partially in water to completely out
  4087. m_flWaterEntryTime = gpGlobals->curtime;
  4088. }
  4089. // If player is now up to his eyes in water and has entered the water very recently (not just bobbing eyes in and out), play a bubble effect.
  4090. if ( ( nNewWaterLevel == WL_Eyes ) && ( gpGlobals->curtime - m_flWaterEntryTime ) < 0.5f )
  4091. {
  4092. CNewParticleEffect *pEffect = ParticleProp()->Create( "water_playerdive", PATTACH_ABSORIGIN_FOLLOW );
  4093. ParticleProp()->AddControlPoint( pEffect, 1, NULL, PATTACH_WORLDORIGIN, NULL, WorldSpaceCenter() );
  4094. }
  4095. // If player was up to his eyes in water and is now out to waist level or less, play a water drip effect
  4096. else if ( m_nOldWaterLevel == WL_Eyes && ( nNewWaterLevel < WL_Eyes ) && !bJustSpawned )
  4097. {
  4098. CNewParticleEffect *pWaterExitEffect = ParticleProp()->Create( "water_playeremerge", PATTACH_ABSORIGIN_FOLLOW );
  4099. ParticleProp()->AddControlPoint( pWaterExitEffect, 1, this, PATTACH_ABSORIGIN_FOLLOW );
  4100. m_bWaterExitEffectActive = true;
  4101. }
  4102. }
  4103. if ( IsLocalPlayer() )
  4104. {
  4105. if ( updateType == DATA_UPDATE_CREATED )
  4106. {
  4107. SetupHeadLabelMaterials();
  4108. GetClientVoiceMgr()->SetHeadLabelOffset( 50 );
  4109. }
  4110. if ( m_iOldTeam != GetTeamNumber() )
  4111. {
  4112. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_changeteam" );
  4113. if ( event )
  4114. {
  4115. gameeventmanager->FireEventClientSide( event );
  4116. }
  4117. if ( IsX360() )
  4118. {
  4119. const char *pTeam = NULL;
  4120. switch( GetTeamNumber() )
  4121. {
  4122. case TF_TEAM_RED:
  4123. pTeam = "red";
  4124. break;
  4125. case TF_TEAM_BLUE:
  4126. pTeam = "blue";
  4127. break;
  4128. case TEAM_SPECTATOR:
  4129. pTeam = "spectate";
  4130. break;
  4131. }
  4132. if ( pTeam )
  4133. {
  4134. engine->ChangeTeam( pTeam );
  4135. }
  4136. }
  4137. // let the server know that we're using a VR headset
  4138. if ( UseVR() )
  4139. {
  4140. KeyValues *kv = new KeyValues( "UsingVRHeadset" );
  4141. engine->ServerCmdKeyValues( kv );
  4142. }
  4143. }
  4144. if ( !IsPlayerClass(m_iOldClass) )
  4145. {
  4146. m_flChangeClassTime = gpGlobals->curtime;
  4147. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_changeclass" );
  4148. if ( event )
  4149. {
  4150. event->SetInt( "updateType", updateType );
  4151. gameeventmanager->FireEventClientSide( event );
  4152. }
  4153. #ifdef STAGING_ONLY
  4154. // Spy Tranq Mark
  4155. // Update tranq mark if you were or are a spy
  4156. if ( m_iOldClass == TF_CLASS_SPY || IsPlayerClass( TF_CLASS_SPY ) )
  4157. {
  4158. bool bShowTranqMark = IsPlayerClass( TF_CLASS_SPY );
  4159. for ( int iPlayerIndex = 1; iPlayerIndex <= MAX_PLAYERS; ++iPlayerIndex )
  4160. {
  4161. C_TFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  4162. if ( pTFPlayer )
  4163. {
  4164. pTFPlayer->UpdateTranqMark( bShowTranqMark );
  4165. }
  4166. }
  4167. }
  4168. #endif // STAGING_ONLY
  4169. }
  4170. bool bUpdateAttachedWeapons = (GetObserverTarget() != m_hOldObserverTarget);
  4171. if ( m_iOldObserverMode != GetObserverMode() )
  4172. {
  4173. if ( m_iOldObserverMode == OBS_MODE_NONE )
  4174. {
  4175. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_becameobserver" );
  4176. if ( event )
  4177. {
  4178. gameeventmanager->FireEventClientSide( event );
  4179. }
  4180. // NVNT send spectator nav
  4181. if ( haptics )
  4182. haptics->SetNavigationClass("spectate");
  4183. }
  4184. else if ( m_iOldObserverMode < OBS_MODE_FIXED && GetObserverMode() >= OBS_MODE_FIXED )
  4185. {
  4186. #if defined( REPLAY_ENABLED )
  4187. // If the player is entering a title for a replay, defer displaying the items screen until afterwards
  4188. if ( !IsReplayInputPanelVisible() )
  4189. #endif
  4190. {
  4191. // Show items we've picked up when we exit freezecam, or after deathcam on suicide
  4192. TFInventoryManager()->ShowItemsPickedUp();
  4193. }
  4194. }
  4195. if ( m_iOldObserverMode == OBS_MODE_IN_EYE || GetObserverMode() == OBS_MODE_IN_EYE )
  4196. {
  4197. bUpdateAttachedWeapons = true;
  4198. }
  4199. if ( m_iOldObserverMode == OBS_MODE_IN_EYE )
  4200. {
  4201. CBaseEntity* pObserveTarget = GetObserverTarget();
  4202. if( pObserveTarget )
  4203. {
  4204. pObserveTarget->UpdateVisibility();
  4205. }
  4206. }
  4207. // NVNT send onfoot nav if observer mode is none.
  4208. if(GetObserverMode()==OBS_MODE_NONE &&haptics)
  4209. {
  4210. haptics->SetNavigationClass("on_foot");
  4211. }
  4212. if ( IsReplay() )
  4213. {
  4214. m_iOldObserverMode = GetObserverMode();
  4215. }
  4216. }
  4217. if ( bUpdateAttachedWeapons )
  4218. {
  4219. C_TFPlayer *pTFOldObserverTarget = ToTFPlayer( m_hOldObserverTarget.Get() );
  4220. if ( m_hOldObserverTarget != GetObserverTarget() && pTFOldObserverTarget )
  4221. {
  4222. C_TFWeaponBase *pWeapon = pTFOldObserverTarget->m_Shared.GetActiveTFWeapon();
  4223. if ( pWeapon )
  4224. {
  4225. pWeapon->UpdateAttachmentModels();
  4226. }
  4227. // Update visibility of any worn items.
  4228. pTFOldObserverTarget->UpdateWearables();
  4229. pTFOldObserverTarget->SetBodygroupsDirty();
  4230. if ( IsReplay() )
  4231. {
  4232. m_hOldObserverTarget = GetObserverTarget();
  4233. }
  4234. }
  4235. C_TFPlayer *pTFObserverTarget = ToTFPlayer( GetObserverTarget() );
  4236. if ( pTFObserverTarget )
  4237. {
  4238. C_TFWeaponBase *pWeapon = pTFObserverTarget->m_Shared.GetActiveTFWeapon();
  4239. if ( pWeapon )
  4240. {
  4241. pWeapon->UpdateAttachmentModels();
  4242. }
  4243. // Update visibility of any worn items.
  4244. pTFObserverTarget->UpdateWearables();
  4245. pTFObserverTarget->SetBodygroupsDirty();
  4246. }
  4247. }
  4248. if ( m_iOldClass == TF_CLASS_SPY &&
  4249. ( m_bDisguised != m_Shared.InCond( TF_COND_DISGUISED ) || m_iOldDisguiseClass != m_Shared.GetDisguiseClass() ) )
  4250. {
  4251. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_changedisguise" );
  4252. if ( event )
  4253. {
  4254. event->SetBool( "disguised", m_Shared.InCond( TF_COND_DISGUISED ) );
  4255. gameeventmanager->FireEventClientSide( event );
  4256. }
  4257. }
  4258. // If our metal amount changed, send a game event
  4259. int iCurrentMetal = GetAmmoCount( TF_AMMO_METAL );
  4260. if ( iCurrentMetal != m_iPreviousMetal )
  4261. {
  4262. //msg
  4263. IGameEvent *event = gameeventmanager->CreateEvent( "player_account_changed" );
  4264. if ( event )
  4265. {
  4266. event->SetInt( "old_account", m_iPreviousMetal );
  4267. event->SetInt( "new_account", iCurrentMetal );
  4268. gameeventmanager->FireEventClientSide( event );
  4269. }
  4270. m_iPreviousMetal = iCurrentMetal;
  4271. }
  4272. // did the local player get any health?
  4273. if ( m_iHealth > m_iOldHealth )
  4274. {
  4275. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_healed" );
  4276. if ( event )
  4277. {
  4278. event->SetInt( "amount", m_iHealth - m_iOldHealth );
  4279. gameeventmanager->FireEventClientSide( event );
  4280. }
  4281. }
  4282. if ( m_nOldCurrency != m_nCurrency )
  4283. {
  4284. IGameEvent *event = gameeventmanager->CreateEvent( "player_currency_changed" );
  4285. if ( event )
  4286. {
  4287. event->SetInt( "currency", m_nCurrency );
  4288. gameeventmanager->FireEventClientSide( event );
  4289. }
  4290. }
  4291. if ( m_bOldCustomModelVisible != m_PlayerClass.CustomModelIsVisibleToSelf() )
  4292. {
  4293. UpdateVisibility();
  4294. }
  4295. }
  4296. C_TFWeaponBase *pOldActiveWeapon = assert_cast< CTFWeaponBase* >( m_hOldActiveWeapon.Get() );
  4297. C_TFWeaponBase *pActiveWeapon = GetActiveTFWeapon();
  4298. if ( pOldActiveWeapon != pActiveWeapon )
  4299. {
  4300. // make sure weapons data are up to date before doing anything here
  4301. UpdateClientData();
  4302. if ( pOldActiveWeapon )
  4303. {
  4304. pOldActiveWeapon->UpdateVisibility();
  4305. }
  4306. if ( pActiveWeapon )
  4307. {
  4308. pActiveWeapon->UpdateVisibility();
  4309. if ( GetLocalTFPlayer() == this && pActiveWeapon->CanInspect() )
  4310. {
  4311. HandleInspectHint();
  4312. }
  4313. }
  4314. m_hOldActiveWeapon = pActiveWeapon;
  4315. // Update HandPoses if needed
  4316. CBaseAnimating *pWeaponModel = GetRenderedWeaponModel();
  4317. if ( !pWeaponModel )
  4318. return;
  4319. int iPose = 0;
  4320. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iPose, righthand_pose_parameter );
  4321. float flMin, flMax = 0;
  4322. int iPoseParam = LookupPoseParameter( "r_hand_grip" );
  4323. if ( iPoseParam > 0 )
  4324. {
  4325. // SetPoseParameter() normalize's values. r_hand_grip is special and goes beyond 0..1 so we have to counter act the normalization
  4326. GetPoseParameterRange( iPoseParam, flMin, flMax );
  4327. SetPoseParameter( iPoseParam, iPose * flMax );
  4328. }
  4329. pWeaponModel->SetPoseParameter( "r_hand_grip", iPose );
  4330. }
  4331. // Some time in this network transmit we changed the size of the object array.
  4332. // recalc the whole thing and update the hud
  4333. if ( m_bUpdateObjectHudState )
  4334. {
  4335. IGameEvent *event = gameeventmanager->CreateEvent( "building_info_changed" );
  4336. if ( event )
  4337. {
  4338. event->SetInt( "building_type", -1 );
  4339. gameeventmanager->FireEventClientSide( event );
  4340. }
  4341. m_bUpdateObjectHudState = false;
  4342. }
  4343. if ( m_iOldTeam != GetTeamNumber() )
  4344. {
  4345. if ( GetTeamNumber() == TEAM_SPECTATOR )
  4346. {
  4347. m_rtJoinedSpectatorTeam = steamapicontext && steamapicontext->SteamUtils() ? steamapicontext->SteamUtils()->GetServerRealTime() : CRTime::RTime32TimeCur();
  4348. }
  4349. else if ( m_iOldTeam != TF_TEAM_RED && m_iOldTeam != TF_TEAM_BLUE )
  4350. {
  4351. m_rtJoinedNormalTeam = steamapicontext && steamapicontext->SteamUtils() ? steamapicontext->SteamUtils()->GetServerRealTime() : CRTime::RTime32TimeCur();
  4352. }
  4353. }
  4354. }
  4355. //-----------------------------------------------------------------------------
  4356. // Purpose:
  4357. //-----------------------------------------------------------------------------
  4358. void C_TFPlayer::UpdateTauntItem()
  4359. {
  4360. if ( m_nActiveTauntSlot == LOADOUT_POSITION_INVALID )
  4361. {
  4362. if ( m_iTauntItemDefIndex != INVALID_ITEM_DEF_INDEX )
  4363. {
  4364. m_TauntEconItemView.Init( m_iTauntItemDefIndex, AE_UNIQUE, 1 );
  4365. }
  4366. else
  4367. {
  4368. m_TauntEconItemView.Invalidate();
  4369. }
  4370. }
  4371. else
  4372. {
  4373. int iClass = GetPlayerClass()->GetClassIndex();
  4374. CEconItemView *pMiscItemView = Inventory() ? Inventory()->GetItemInLoadout( iClass, m_nActiveTauntSlot ) : NULL;
  4375. if ( pMiscItemView )
  4376. {
  4377. m_TauntEconItemView = *pMiscItemView;
  4378. }
  4379. }
  4380. if ( m_TauntEconItemView.IsValid() )
  4381. {
  4382. ParseSharedTauntDataFromEconItemView( &m_TauntEconItemView );
  4383. }
  4384. }
  4385. ConVar tf_halloween_kart_sound_slow_pitch( "tf_halloween_kart_sound_slow_pitch", "30.f", FCVAR_CHEAT | FCVAR_REPLICATED );
  4386. ConVar tf_halloween_kart_sound_fast_pitch( "tf_halloween_kart_sound_fast_pitch", "80.f", FCVAR_CHEAT | FCVAR_REPLICATED );
  4387. extern ConVar tf_halloween_kart_dash_speed;
  4388. //-----------------------------------------------------------------------------
  4389. void C_TFPlayer::UpdateKartEffects()
  4390. {
  4391. if ( m_hKartDamageEffect )
  4392. {
  4393. ParticleProp()->StopEmission( m_hKartDamageEffect );
  4394. m_hKartDamageEffect = NULL;
  4395. }
  4396. if ( !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  4397. return;
  4398. m_iOldKartHealth = m_iKartHealth;
  4399. const char *pszEffect = "";
  4400. if ( m_iKartHealth > 150 )
  4401. {
  4402. pszEffect = "kartdamage_4";
  4403. m_hKartDamageEffect = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN_FOLLOW, INVALID_PARTICLE_ATTACHMENT, Vector(0,0,40) );
  4404. }
  4405. }
  4406. //-----------------------------------------------------------------------------
  4407. void C_TFPlayer::UpdateKartState()
  4408. {
  4409. if ( !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  4410. return;
  4411. m_iOldKartState = m_iKartState;
  4412. // turn brake on and off
  4413. if ( m_iKartState & CTFPlayerShared::kKartState_Braking )
  4414. {
  4415. StartKartBrakeEffect();
  4416. }
  4417. else
  4418. {
  4419. StopKartBrakeEffect();
  4420. }
  4421. }
  4422. //-----------------------------------------------------------------------------
  4423. void C_TFPlayer::UpdateKartSounds()
  4424. {
  4425. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  4426. // Create our engine sound if we dont have one and need one
  4427. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  4428. {
  4429. if ( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] == NULL )
  4430. {
  4431. CBroadcastRecipientFilter filter;
  4432. m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] = controller.SoundCreate( filter, entindex(), "BumperCar.GoLoop" );
  4433. controller.Play( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ], 1.f, 1.f );
  4434. }
  4435. }
  4436. else if ( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] )
  4437. {
  4438. controller.SoundDestroy( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] );
  4439. m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] = NULL;
  4440. }
  4441. if ( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ] )
  4442. {
  4443. float flTargetPitch = RemapValClamped( GetCurrentTauntMoveSpeed(), 0.f, tf_halloween_kart_dash_speed.GetFloat(), tf_halloween_kart_sound_slow_pitch.GetFloat(), tf_halloween_kart_sound_fast_pitch.GetFloat() );
  4444. controller.SoundChangePitch( m_pKartSounds[ KART_SOUND_ENGINE_LOOP ], flTargetPitch, 0.1f );
  4445. }
  4446. // Uncomment this (if) when we have a tire screech sound
  4447. //if ( ( m_iKartState & CTFPlayerShared::kKartState_Driving ) && GetCurrentTauntMoveSpeed() < 300.f )
  4448. //{
  4449. // if ( m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ] == NULL )
  4450. // {
  4451. // CBroadcastRecipientFilter filter;
  4452. // m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ] = controller.SoundCreate( filter, entindex(), "BumperCar.GoLoop" );
  4453. // controller.Play( m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ], 1.f, 80.f );
  4454. // }
  4455. //}
  4456. //else if ( m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ] )
  4457. //{
  4458. // controller.SoundDestroy( m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ] );
  4459. // m_pKartSounds[ KART_SOUND_BURNOUT_LOOP ] = NULL;
  4460. //}
  4461. }
  4462. //-----------------------------------------------------------------------------
  4463. // Purpose:
  4464. //-----------------------------------------------------------------------------
  4465. void C_TFPlayer::InitInvulnerableMaterial( void )
  4466. {
  4467. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  4468. if ( !pLocalPlayer )
  4469. return;
  4470. const char *pszMaterial = NULL;
  4471. int iVisibleTeam = GetTeamNumber();
  4472. // if this player is disguised and on the other team, use disguise team
  4473. if ( m_Shared.InCond( TF_COND_DISGUISED ) && !InSameTeam( pLocalPlayer ) )
  4474. {
  4475. iVisibleTeam = m_Shared.GetDisguiseTeam();
  4476. }
  4477. switch ( iVisibleTeam )
  4478. {
  4479. case TF_TEAM_BLUE:
  4480. pszMaterial = "models/effects/invulnfx_blue.vmt";
  4481. break;
  4482. case TF_TEAM_RED:
  4483. pszMaterial = "models/effects/invulnfx_red.vmt";
  4484. break;
  4485. default:
  4486. break;
  4487. }
  4488. if ( pszMaterial )
  4489. {
  4490. m_InvulnerableMaterial.Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS );
  4491. }
  4492. else
  4493. {
  4494. m_InvulnerableMaterial.Shutdown();
  4495. }
  4496. }
  4497. //-----------------------------------------------------------------------------
  4498. // Purpose:
  4499. //-----------------------------------------------------------------------------
  4500. void C_TFPlayer::StartBurningSound( void )
  4501. {
  4502. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  4503. if ( !m_pBurningSound )
  4504. {
  4505. CLocalPlayerFilter filter;
  4506. m_pBurningSound = controller.SoundCreate( filter, entindex(), "Player.OnFire" );
  4507. }
  4508. controller.Play( m_pBurningSound, 0.0, 100 );
  4509. controller.SoundChangeVolume( m_pBurningSound, 1.0, 0.1 );
  4510. }
  4511. //-----------------------------------------------------------------------------
  4512. // Purpose:
  4513. //-----------------------------------------------------------------------------
  4514. void C_TFPlayer::StopBurningSound( void )
  4515. {
  4516. if ( m_pBurningSound )
  4517. {
  4518. CSoundEnvelopeController::GetController().SoundDestroy( m_pBurningSound );
  4519. m_pBurningSound = NULL;
  4520. }
  4521. }
  4522. //-----------------------------------------------------------------------------
  4523. // Purpose:
  4524. //-----------------------------------------------------------------------------
  4525. void C_TFPlayer::StopBlastJumpLoopSound( int iUserID )
  4526. {
  4527. if ( m_pBlastJumpLoop )
  4528. {
  4529. if ( GetUserID() == iUserID )
  4530. {
  4531. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  4532. controller.SoundDestroy( m_pBlastJumpLoop );
  4533. m_pBlastJumpLoop = NULL;
  4534. }
  4535. }
  4536. }
  4537. //-----------------------------------------------------------------------------
  4538. // Purpose:
  4539. //-----------------------------------------------------------------------------
  4540. void C_TFPlayer::UpdateRecentlyTeleportedEffect( void )
  4541. {
  4542. bool bShow = m_Shared.ShouldShowRecentlyTeleported();
  4543. if ( bShow )
  4544. {
  4545. // NVNT if this is the local player notify haptics system of this teleporter
  4546. // teleporting.
  4547. if(IsLocalPlayer()&&!tfHaptics.wasBeingTeleported) {
  4548. if ( haptics )
  4549. haptics->ProcessHapticEvent(2,"Game","player_teleport");
  4550. tfHaptics.wasBeingHealed = true;
  4551. }
  4552. if ( !m_pTeleporterEffect )
  4553. {
  4554. const char *pszEffectName = NULL;
  4555. int iTeam = GetTeamNumber();
  4556. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  4557. {
  4558. iTeam = m_Shared.GetDisguiseTeam();
  4559. }
  4560. switch( iTeam )
  4561. {
  4562. case TF_TEAM_BLUE:
  4563. pszEffectName = "player_recent_teleport_blue";
  4564. break;
  4565. case TF_TEAM_RED:
  4566. pszEffectName = "player_recent_teleport_red";
  4567. break;
  4568. default:
  4569. break;
  4570. }
  4571. if ( TFGameRules()->IsMannVsMachineMode() && IsABot() )
  4572. {
  4573. #if 0 // Nice idea, but it's chewing into our particle budget, and because bots currently spawn in ubered it's nearly invisible.
  4574. pszEffectName = "bot_recent_teleport_blue";
  4575. #else
  4576. pszEffectName = NULL;
  4577. #endif
  4578. }
  4579. if ( pszEffectName )
  4580. {
  4581. m_pTeleporterEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW );
  4582. }
  4583. }
  4584. }
  4585. else
  4586. {
  4587. // NVNT if this is the local player and we were being teleported
  4588. // flag that we are no longer teleporting.
  4589. if(IsLocalPlayer()&&tfHaptics.wasBeingTeleported) {
  4590. tfHaptics.wasBeingHealed = false;
  4591. }
  4592. if ( m_pTeleporterEffect )
  4593. {
  4594. ParticleProp()->StopEmission( m_pTeleporterEffect );
  4595. m_pTeleporterEffect = NULL;
  4596. }
  4597. }
  4598. }
  4599. //-----------------------------------------------------------------------------
  4600. // Purpose:
  4601. //-----------------------------------------------------------------------------
  4602. void C_TFPlayer::UpdatedMarkedForDeathEffect( bool bForceStop )
  4603. {
  4604. // Dont show the particle over the local player's head. They have the icon that shows
  4605. // up over their health in the HUD which serves this purpose.
  4606. if ( IsLocalPlayer() )
  4607. return;
  4608. bool bShow = m_Shared.InCond( TF_COND_MARKEDFORDEATH ) || m_Shared.InCond( TF_COND_MARKEDFORDEATH_SILENT ) || m_Shared.InCond( TF_COND_PASSTIME_PENALTY_DEBUFF );
  4609. // force stop
  4610. if ( bForceStop || m_Shared.IsStealthed() || m_Shared.InCond( TF_COND_DISGUISED ) )
  4611. {
  4612. bShow = false;
  4613. }
  4614. if ( !bShow )
  4615. {
  4616. // Stop and then go
  4617. RemoveOverheadEffect( "mark_for_death", true );
  4618. }
  4619. if ( bShow )
  4620. {
  4621. AddOverheadEffect( "mark_for_death" );
  4622. }
  4623. }
  4624. //-----------------------------------------------------------------------------
  4625. // Purpose: Show an icon above the player's head to let other players know which Powerup Rune they are carrying
  4626. //-----------------------------------------------------------------------------
  4627. void C_TFPlayer::UpdateRuneIcon( bool bForceStop /*= false */ )
  4628. {
  4629. // Don't show the particle over the local player's head. They have the icon that shows
  4630. // up over their health in the HUD which serves this purpose.
  4631. if ( IsLocalPlayer() )
  4632. return;
  4633. const RuneTypes_t carryingRuneType = m_Shared.GetCarryingRuneType();
  4634. const bool bAllowedToShow = ( m_Shared.IsCarryingRune() && !m_Shared.IsStealthed() );
  4635. int iTeam = IsEnemyPlayer() && m_Shared.InCond( TF_COND_DISGUISED ) ? m_Shared.GetDisguiseTeam() : GetTeamNumber();
  4636. if ( !bAllowedToShow || bForceStop || ( carryingRuneType != m_eDisplayingRuneIcon ) )
  4637. {
  4638. // remove all particle for both team just in case
  4639. for ( int i=0; i<RUNE_TYPES_MAX; ++i )
  4640. {
  4641. RuneTypes_t type = RuneTypes_t(i);
  4642. RemoveOverheadEffect( GetPowerupIconName( type, TF_TEAM_RED ), true );
  4643. RemoveOverheadEffect( GetPowerupIconName( type, TF_TEAM_BLUE ), true );
  4644. }
  4645. m_eDisplayingRuneIcon = RUNE_NONE;
  4646. }
  4647. if ( bAllowedToShow && ( carryingRuneType != m_eDisplayingRuneIcon ) )
  4648. {
  4649. const char* pszEffect = NULL;
  4650. if ( carryingRuneType > RUNE_NONE && carryingRuneType < RUNE_TYPES_MAX )
  4651. {
  4652. pszEffect = GetPowerupIconName( carryingRuneType, iTeam );
  4653. }
  4654. else
  4655. {
  4656. #ifdef STAGING_ONLY
  4657. Assert( !"unexepected value in GetCarryingRuneType, probably a bug" );
  4658. pszEffect = "(null)"; // so we don't crash in the messages below.
  4659. Msg( "GetCarryingRuneType had surprising value: %d for player %s\n", carryingRuneType, GetPlayerName() );
  4660. #endif
  4661. }
  4662. if ( AddOverheadEffect( pszEffect ) )
  4663. {
  4664. m_eDisplayingRuneIcon = carryingRuneType;
  4665. }
  4666. #ifdef STAGING_ONLY
  4667. else
  4668. {
  4669. Msg( "ParticleProp()->Create failed for some reason for powerup %s, for player %s.\n", pszEffect, GetPlayerName() );
  4670. }
  4671. #endif
  4672. }
  4673. }
  4674. //-----------------------------------------------------------------------------
  4675. // Purpose:
  4676. //-----------------------------------------------------------------------------
  4677. void C_TFPlayer::OnPlayerClassChange( void )
  4678. {
  4679. // Init the anim movement vars
  4680. m_PlayerAnimState->SetRunSpeed( GetPlayerClass()->GetMaxSpeed() );
  4681. m_PlayerAnimState->SetWalkSpeed( GetPlayerClass()->GetMaxSpeed() * 0.5 );
  4682. if ( IsLocalPlayer() )
  4683. {
  4684. g_ItemEffectMeterManager.SetPlayer( this );
  4685. }
  4686. ShowNemesisIcon( false );
  4687. ShowDuelingIcon( false );
  4688. SetAppropriateCamera( this );
  4689. }
  4690. //-----------------------------------------------------------------------------
  4691. // Purpose:
  4692. //-----------------------------------------------------------------------------
  4693. void C_TFPlayer::InitPhonemeMappings()
  4694. {
  4695. CStudioHdr *pStudio = GetModelPtr();
  4696. if ( pStudio )
  4697. {
  4698. char szBasename[MAX_PATH];
  4699. Q_StripExtension( pStudio->pszName(), szBasename, sizeof( szBasename ) );
  4700. char szExpressionName[MAX_PATH];
  4701. Q_snprintf( szExpressionName, sizeof( szExpressionName ), "%s/phonemes/phonemes", szBasename );
  4702. if ( FindSceneFile( szExpressionName ) )
  4703. {
  4704. SetupMappings( szExpressionName );
  4705. }
  4706. else
  4707. {
  4708. BaseClass::InitPhonemeMappings();
  4709. }
  4710. }
  4711. }
  4712. //-----------------------------------------------------------------------------
  4713. // Purpose:
  4714. //-----------------------------------------------------------------------------
  4715. void C_TFPlayer::ResetFlexWeights( CStudioHdr *pStudioHdr )
  4716. {
  4717. if ( !pStudioHdr || pStudioHdr->numflexdesc() == 0 )
  4718. return;
  4719. // Reset the flex weights to their starting position.
  4720. LocalFlexController_t iController;
  4721. for ( iController = LocalFlexController_t(0); iController < pStudioHdr->numflexcontrollers(); ++iController )
  4722. {
  4723. SetFlexWeight( iController, 0.0f );
  4724. }
  4725. // Reset the prediction interpolation values.
  4726. m_iv_flexWeight.Reset();
  4727. }
  4728. //-----------------------------------------------------------------------------
  4729. // Purpose:
  4730. //-----------------------------------------------------------------------------
  4731. void C_TFPlayer::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  4732. {
  4733. QAngle myEyeAngles;
  4734. VectorCopy( EyeAngles(), myEyeAngles );
  4735. BaseClass::CalcInEyeCamView( eyeOrigin, eyeAngles, fov );
  4736. /*
  4737. // if we are coaching, we override the eye angles with our original ones
  4738. // @note Tom Bui: we don't try to capture the "up" button event, because that doesn't seem so reliable
  4739. if ( m_bIsCoaching )
  4740. {
  4741. const float kLerpTime = 1.0f;
  4742. if ( ( m_nButtons & IN_JUMP ) != 0 )
  4743. {
  4744. VectorCopy( myEyeAngles, eyeAngles );
  4745. engine->SetViewAngles( eyeAngles );
  4746. m_flCoachLookAroundLerpTime = kLerpTime;
  4747. m_angCoachLookAroundEyeAngles = myEyeAngles;
  4748. }
  4749. else if ( m_flCoachLookAroundLerpTime > 0 )
  4750. {
  4751. m_flCoachLookAroundLerpTime -= gpGlobals->frametime;
  4752. float flPercent = ( kLerpTime - m_flCoachLookAroundLerpTime / kLerpTime );
  4753. eyeAngles = Lerp( flPercent, m_angCoachLookAroundEyeAngles, eyeAngles );
  4754. engine->SetViewAngles( eyeAngles );
  4755. }
  4756. }
  4757. */
  4758. }
  4759. //-----------------------------------------------------------------------------
  4760. // Purpose:
  4761. //-----------------------------------------------------------------------------
  4762. CStudioHdr *C_TFPlayer::OnNewModel( void )
  4763. {
  4764. CStudioHdr *hdr = BaseClass::OnNewModel();
  4765. // Initialize the gibs.
  4766. InitPlayerGibs();
  4767. InitializePoseParams();
  4768. // Init flexes, cancel any scenes we're playing
  4769. ClearSceneEvents( NULL, false );
  4770. // Reset the flex weights.
  4771. ResetFlexWeights( hdr );
  4772. // Reset the players animation states, gestures
  4773. if ( m_PlayerAnimState )
  4774. {
  4775. m_PlayerAnimState->OnNewModel();
  4776. }
  4777. if ( hdr )
  4778. {
  4779. InitPhonemeMappings();
  4780. }
  4781. if ( IsPlayerClass( TF_CLASS_SPY ) )
  4782. {
  4783. m_iSpyMaskBodygroup = FindBodygroupByName( "spyMask" );
  4784. }
  4785. else
  4786. {
  4787. m_iSpyMaskBodygroup = -1;
  4788. }
  4789. return hdr;
  4790. }
  4791. //-----------------------------------------------------------------------------
  4792. // Purpose: Is this player an enemy to the local player
  4793. //-----------------------------------------------------------------------------
  4794. bool C_TFPlayer::IsEnemyPlayer( void )
  4795. {
  4796. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  4797. if ( !pLocalPlayer )
  4798. return false;
  4799. int iTeam = pLocalPlayer->GetTeamNumber();
  4800. // if we are coaching, use the team of the student
  4801. if ( pLocalPlayer->m_hStudent && pLocalPlayer->m_bIsCoaching )
  4802. {
  4803. iTeam = pLocalPlayer->m_hStudent->GetTeamNumber();
  4804. }
  4805. switch( iTeam )
  4806. {
  4807. case TF_TEAM_RED:
  4808. return ( GetTeamNumber() == TF_TEAM_BLUE );
  4809. case TF_TEAM_BLUE:
  4810. return ( GetTeamNumber() == TF_TEAM_RED );
  4811. default:
  4812. break;
  4813. }
  4814. return false;
  4815. }
  4816. //-----------------------------------------------------------------------------
  4817. // Purpose: Displays a nemesis icon on this player to the local player
  4818. //-----------------------------------------------------------------------------
  4819. void C_TFPlayer::ShowNemesisIcon( bool bShow )
  4820. {
  4821. if ( bShow )
  4822. {
  4823. const char *pszEffect = NULL;
  4824. switch ( GetTeamNumber() )
  4825. {
  4826. case TF_TEAM_RED:
  4827. pszEffect = "particle_nemesis_red";
  4828. break;
  4829. case TF_TEAM_BLUE:
  4830. pszEffect = "particle_nemesis_blue";
  4831. break;
  4832. default:
  4833. return; // shouldn't get called if we're not on a team; bail out if it does
  4834. }
  4835. AddOverheadEffect( pszEffect );
  4836. }
  4837. else
  4838. {
  4839. // stop effects for both team colors (to make sure we remove effects in event of team change)
  4840. RemoveOverheadEffect( "particle_nemesis_red", true );
  4841. RemoveOverheadEffect( "particle_nemesis_blue", true );
  4842. }
  4843. m_bIsDisplayingNemesisIcon = bShow;
  4844. }
  4845. //-----------------------------------------------------------------------------
  4846. // Purpose: Displays a dueling icon on this player to the local player
  4847. //-----------------------------------------------------------------------------
  4848. void C_TFPlayer::ShowDuelingIcon( bool bShow )
  4849. {
  4850. if ( bShow )
  4851. {
  4852. const char *pszEffect = NULL;
  4853. switch ( GetTeamNumber() )
  4854. {
  4855. case TF_TEAM_RED:
  4856. pszEffect = "duel_red";
  4857. break;
  4858. case TF_TEAM_BLUE:
  4859. pszEffect = "duel_blue";
  4860. break;
  4861. default:
  4862. return; // shouldn't get called if we're not on a team; bail out if it does
  4863. }
  4864. AddOverheadEffect( pszEffect );
  4865. }
  4866. else
  4867. {
  4868. // stop effects for both team colors (to make sure we remove effects in event of team change)
  4869. RemoveOverheadEffect( "duel_red", true );
  4870. RemoveOverheadEffect( "duel_blue", true );
  4871. }
  4872. m_bIsDisplayingDuelingIcon = bShow;
  4873. }
  4874. //-----------------------------------------------------------------------------
  4875. // Purpose: Displays an icon denoting this player as "IT" to the local player
  4876. //-----------------------------------------------------------------------------
  4877. void C_TFPlayer::ShowIconForIT( bool bShow )
  4878. {
  4879. if ( bShow )
  4880. {
  4881. AddOverheadEffect( "halloween_boss_victim" );
  4882. }
  4883. else
  4884. {
  4885. RemoveOverheadEffect( "halloween_boss_victim", true );
  4886. }
  4887. m_bIsDisplayingIconForIT = bShow;
  4888. }
  4889. //-----------------------------------------------------------------------------
  4890. // Purpose: Displays an icon denoting this player as the Birthday Player to the local player
  4891. //-----------------------------------------------------------------------------
  4892. void C_TFPlayer::ShowBirthdayEffect( bool bShow )
  4893. {
  4894. /*
  4895. if ( bShow )
  4896. {
  4897. ParticleProp()->Create( "birthday_player_circling", PATTACH_POINT_FOLLOW, "head" );
  4898. DispatchParticleEffect( "bday_confetti", GetAbsOrigin() + Vector(0,0,32), vec3_angle );
  4899. }
  4900. else
  4901. {
  4902. ParticleProp()->StopParticlesNamed( "birthday_player_circling", true );
  4903. }
  4904. */
  4905. m_bShouldShowBirthdayEffect = bShow;
  4906. }
  4907. bool C_TFPlayer::HasBombinomiconEffectOnDeath( void )
  4908. {
  4909. int iBombinomicomEffectOnDeath = 0;
  4910. CALL_ATTRIB_HOOK_INT( iBombinomicomEffectOnDeath, bombinomicon_effect_on_death );
  4911. return ( iBombinomicomEffectOnDeath != 0 );
  4912. }
  4913. #ifdef STAGING_ONLY
  4914. //-----------------------------------------------------------------------------
  4915. void C_TFPlayer::UpdateTranqMark( bool bShow, bool bForceStop /*= false */ )
  4916. {
  4917. // Dont show the particle over the local player's head. They have the icon that shows
  4918. // up over their health in the HUD which serves this purpose.
  4919. // only show this mark to spies
  4920. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  4921. if ( IsLocalPlayer() || !pLocalPlayer || !pLocalPlayer->IsPlayerClass( TF_CLASS_SPY ) )
  4922. {
  4923. bShow = false;
  4924. }
  4925. int iTranq = 0;
  4926. CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iTranq, override_projectile_type );
  4927. if ( iTranq != TF_PROJECTILE_TRANQ )
  4928. {
  4929. bShow = false;
  4930. }
  4931. if ( bShow && !m_bIsDisplayingTranqMark && m_Shared.InCond( TF_COND_TRANQ_MARKED ) )
  4932. {
  4933. AddOverheadEffect( "marked_for_tranq" );
  4934. m_bIsDisplayingTranqMark = true;
  4935. }
  4936. else
  4937. {
  4938. if ( m_bIsDisplayingTranqMark || bForceStop || ( !m_Shared.InCond( TF_COND_TRANQ_MARKED ) ) )
  4939. {
  4940. RemoveOverheadEffect( "marked_for_tranq", true );
  4941. m_bIsDisplayingTranqMark = false;
  4942. }
  4943. }
  4944. }
  4945. //-----------------------------------------------------------------------------
  4946. void C_TFPlayer::UpdateSpyClassStealParticle( bool bShow )
  4947. {
  4948. if ( bShow )
  4949. {
  4950. const char * pParticleName = NULL;
  4951. if ( GetTeamNumber() == TF_TEAM_RED )
  4952. {
  4953. pParticleName = "spy_stolen_smoke_red";
  4954. }
  4955. else
  4956. {
  4957. pParticleName = "spy_stolen_smoke_blue";
  4958. }
  4959. AddOverheadEffect( pParticleName );
  4960. }
  4961. else
  4962. {
  4963. // Turn off both in the case of team change
  4964. RemoveOverheadEffect( "spy_stolen_smoke_red", true );
  4965. RemoveOverheadEffect( "spy_stolen_smoke_blue", true );
  4966. }
  4967. }
  4968. #endif // STAGING_ONLY
  4969. #define TF_TAUNT_PITCH 0
  4970. #define TF_TAUNT_YAW 1
  4971. #define TF_TAUNT_DIST 2
  4972. #define TF_TAUNT_MAXYAW 135
  4973. #define TF_TAUNT_MINYAW -135
  4974. #define TF_TAUNT_MAXPITCH 90
  4975. #define TF_TAUNT_MINPITCH 0
  4976. #define TF_TAUNT_IDEALLAG 4.0f
  4977. static Vector TF_TAUNTCAM_HULL_MIN( -9.0f, -9.0f, -9.0f );
  4978. static Vector TF_TAUNTCAM_HULL_MAX( 9.0f, 9.0f, 9.0f );
  4979. static ConVar tf_tauntcam_yaw( "tf_tauntcam_yaw", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  4980. static ConVar tf_tauntcam_pitch( "tf_tauntcam_pitch", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  4981. static ConVar tf_tauntcam_dist( "tf_tauntcam_dist", "150", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  4982. static ConVar tf_tauntcam_speed( "tf_tauntcam_speed", "300", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  4983. ConVar tf_halloween_kart_cam_dist( "tf_halloween_kart_cam_dist", "225", FCVAR_CHEAT );
  4984. //-----------------------------------------------------------------------------
  4985. // Purpose:
  4986. //-----------------------------------------------------------------------------
  4987. void C_TFPlayer::TurnOnTauntCam( void )
  4988. {
  4989. if ( !IsLocalPlayer() )
  4990. return;
  4991. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  4992. return;
  4993. m_flTauntCamTargetDist = ( m_flTauntCamTargetDist != 0.0f ) ? m_flTauntCamTargetDist : tf_tauntcam_dist.GetFloat();
  4994. m_flTauntCamTargetDistUp = ( m_flTauntCamTargetDistUp != 0.0f ) ? m_flTauntCamTargetDistUp : 0.f;
  4995. m_flTauntCamCurrentDist = 0.f;
  4996. m_flTauntCamCurrentDistUp = 0.f;
  4997. // Save the old view angles.
  4998. engine->GetViewAngles( m_angTauntEngViewAngles );
  4999. prediction->GetViewAngles( m_angTauntPredViewAngles );
  5000. m_TauntCameraData.m_flPitch = 0;
  5001. m_TauntCameraData.m_flYaw = 0;
  5002. m_TauntCameraData.m_flDist = m_flTauntCamTargetDist;
  5003. m_TauntCameraData.m_flLag = 1.f;
  5004. m_TauntCameraData.m_vecHullMin.Init( -9.0f, -9.0f, -9.0f );
  5005. m_TauntCameraData.m_vecHullMax.Init( 9.0f, 9.0f, 9.0f );
  5006. if ( tf_taunt_first_person.GetBool() )
  5007. {
  5008. // Remain in first-person.
  5009. }
  5010. else
  5011. {
  5012. g_ThirdPersonManager.SetDesiredCameraOffset( Vector( 0, 0, 0 ) );
  5013. g_ThirdPersonManager.SetOverridingThirdPerson( true );
  5014. ::input->CAM_ToThirdPerson();
  5015. ThirdPersonSwitch( true );
  5016. UpdateKillStreakEffects( m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills ) );
  5017. }
  5018. m_bTauntInterpolating = true;
  5019. if ( m_hItem )
  5020. {
  5021. m_hItem->UpdateVisibility();
  5022. }
  5023. }
  5024. //-----------------------------------------------------------------------------
  5025. // Purpose:
  5026. //-----------------------------------------------------------------------------
  5027. void C_TFPlayer::TurnOnTauntCam_Finish( void )
  5028. {
  5029. }
  5030. //-----------------------------------------------------------------------------
  5031. // Purpose:
  5032. //-----------------------------------------------------------------------------
  5033. void C_TFPlayer::TurnOffTauntCam( void )
  5034. {
  5035. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  5036. return;
  5037. // We want to interpolate back into the guy's head.
  5038. if ( g_ThirdPersonManager.GetForcedThirdPerson() == false )
  5039. {
  5040. m_flTauntCamTargetDist = 0.f;
  5041. m_TauntCameraData.m_flDist = m_flTauntCamTargetDist;
  5042. }
  5043. g_ThirdPersonManager.SetOverridingThirdPerson( false );
  5044. if ( g_ThirdPersonManager.GetForcedThirdPerson() )
  5045. {
  5046. TurnOffTauntCam_Finish();
  5047. }
  5048. }
  5049. //-----------------------------------------------------------------------------
  5050. // Purpose:
  5051. //-----------------------------------------------------------------------------
  5052. void C_TFPlayer::TurnOffTauntCam_Finish( void )
  5053. {
  5054. if ( !IsLocalPlayer() )
  5055. return;
  5056. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  5057. return;
  5058. const Vector& vecOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
  5059. tf_tauntcam_pitch.SetValue( vecOffset[PITCH] - m_angTauntPredViewAngles[PITCH] );
  5060. tf_tauntcam_yaw.SetValue( vecOffset[YAW] - m_angTauntPredViewAngles[YAW] );
  5061. QAngle angles;
  5062. angles[PITCH] = vecOffset[PITCH];
  5063. angles[YAW] = vecOffset[YAW];
  5064. angles[DIST] = vecOffset[DIST];
  5065. if( g_ThirdPersonManager.WantToUseGameThirdPerson() == false )
  5066. {
  5067. ::input->CAM_ToFirstPerson();
  5068. ThirdPersonSwitch( false );
  5069. UpdateKillStreakEffects( m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills ) );
  5070. angles = vec3_angle;
  5071. }
  5072. ::input->CAM_SetCameraThirdData( NULL, angles );
  5073. // Reset the old view angles.
  5074. // engine->SetViewAngles( m_angTauntEngViewAngles );
  5075. // prediction->SetViewAngles( m_angTauntPredViewAngles );
  5076. // Force the feet to line up with the view direction post taunt.
  5077. // If you are forcing aim yaw, your code is almost definitely broken if you don't include a delay between
  5078. // teleporting and forcing yaw. This is due to an unfortunate interaction between the command lookback window,
  5079. // and the fact that m_flEyeYaw is never propogated from the server to the client.
  5080. // TODO: Fix this after Halloween 2014.
  5081. m_PlayerAnimState->m_bForceAimYaw = true;
  5082. m_bTauntInterpolating = false;
  5083. if ( GetViewModel() )
  5084. {
  5085. GetViewModel()->UpdateVisibility();
  5086. }
  5087. if ( m_hItem )
  5088. {
  5089. m_hItem->UpdateVisibility();
  5090. }
  5091. SetAppropriateCamera( this );
  5092. }
  5093. //-----------------------------------------------------------------------------
  5094. // Purpose:
  5095. //-----------------------------------------------------------------------------
  5096. void C_TFPlayer::HandleTaunting( void )
  5097. {
  5098. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  5099. // This code is only for the local player.
  5100. Assert( pLocalPlayer == NULL || pLocalPlayer == this );
  5101. // Clear the taunt slot.
  5102. if ( !m_bWasTaunting &&
  5103. (
  5104. m_Shared.InCond( TF_COND_TAUNTING ) ||
  5105. m_Shared.IsControlStunned() ||
  5106. m_Shared.IsLoser() ||
  5107. m_bIsReadyToHighFive ||
  5108. m_nForceTauntCam ||
  5109. m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) ||
  5110. m_Shared.InCond( TF_COND_HALLOWEEN_GIANT ) ||
  5111. m_Shared.InCond( TF_COND_HALLOWEEN_TINY ) ||
  5112. m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ||
  5113. m_Shared.InCond( TF_COND_HALLOWEEN_KART ) ||
  5114. m_Shared.InCond( TF_COND_MELEE_ONLY ) ||
  5115. m_Shared.InCond( TF_COND_SWIMMING_CURSE )
  5116. )
  5117. )
  5118. {
  5119. m_bWasTaunting = true;
  5120. // Handle the camera for the local player.
  5121. if ( pLocalPlayer )
  5122. {
  5123. TurnOnTauntCam();
  5124. }
  5125. }
  5126. if ( ( !IsAlive() && m_nForceTauntCam < 2 ) ||
  5127. (
  5128. m_bWasTaunting && !m_Shared.InCond( TF_COND_TAUNTING ) && !m_Shared.IsControlStunned() &&
  5129. !m_Shared.InCond( TF_COND_PHASE ) && !m_Shared.IsLoser() && !m_bIsReadyToHighFive &&
  5130. !m_nForceTauntCam && !m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) &&
  5131. !m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) &&
  5132. !m_Shared.InCond( TF_COND_HALLOWEEN_GIANT ) &&
  5133. !m_Shared.InCond( TF_COND_HALLOWEEN_TINY ) &&
  5134. !m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) &&
  5135. !m_Shared.InCond( TF_COND_HALLOWEEN_KART ) &&
  5136. !m_Shared.InCond( TF_COND_MELEE_ONLY ) &&
  5137. !m_Shared.InCond( TF_COND_SWIMMING_CURSE )
  5138. )
  5139. )
  5140. {
  5141. m_bWasTaunting = false;
  5142. // Clear the vcd slot.
  5143. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  5144. // Handle the camera for the local player.
  5145. if ( pLocalPlayer )
  5146. {
  5147. TurnOffTauntCam();
  5148. }
  5149. }
  5150. TauntCamInterpolation();
  5151. }
  5152. //-----------------------------------------------------------------------------
  5153. // Purpose: Handles third person camera interpolation directly
  5154. // so we can manage enter & exit behavior without hacking the camera.
  5155. //-----------------------------------------------------------------------------
  5156. void C_TFPlayer::TauntCamInterpolation()
  5157. {
  5158. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  5159. if ( pLocalPlayer && m_bTauntInterpolating )
  5160. {
  5161. if ( m_flTauntCamCurrentDist != m_flTauntCamTargetDist )
  5162. {
  5163. m_flTauntCamCurrentDist += Sign( m_flTauntCamTargetDist - m_flTauntCamCurrentDist ) * gpGlobals->frametime * tf_tauntcam_speed.GetFloat();
  5164. m_flTauntCamCurrentDist = clamp( m_flTauntCamCurrentDist, m_flTauntCamCurrentDist, m_flTauntCamTargetDist );
  5165. }
  5166. if ( m_flTauntCamCurrentDistUp != m_flTauntCamTargetDistUp )
  5167. {
  5168. m_flTauntCamCurrentDistUp += Sign( m_flTauntCamTargetDistUp - m_flTauntCamCurrentDistUp ) * gpGlobals->frametime * tf_tauntcam_speed.GetFloat();
  5169. m_flTauntCamCurrentDistUp = clamp( m_flTauntCamCurrentDistUp, m_flTauntCamCurrentDistUp, m_flTauntCamTargetDistUp );
  5170. }
  5171. const Vector& vecCamOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
  5172. Vector vecOrigin = pLocalPlayer->GetLocalOrigin();
  5173. vecOrigin += pLocalPlayer->GetViewOffset();
  5174. Vector vecForward, vecUp;
  5175. AngleVectors( QAngle( vecCamOffset[PITCH], vecCamOffset[YAW], 0 ), &vecForward, NULL, &vecUp );
  5176. trace_t trace;
  5177. UTIL_TraceHull( vecOrigin, vecOrigin - ( vecForward * m_flTauntCamCurrentDist ) + ( vecUp * m_flTauntCamCurrentDistUp ), Vector( -9.f, -9.f, -9.f ),
  5178. Vector( 9.f, 9.f, 9.f ), MASK_SOLID_BRUSHONLY, pLocalPlayer, COLLISION_GROUP_DEBRIS, &trace );
  5179. if ( trace.fraction < 1.0 )
  5180. m_flTauntCamCurrentDist *= trace.fraction;
  5181. QAngle angCameraOffset = QAngle( vecCamOffset[PITCH], vecCamOffset[YAW], m_flTauntCamCurrentDist );
  5182. ::input->CAM_SetCameraThirdData( &m_TauntCameraData, angCameraOffset ); // Override camera distance interpolation.
  5183. g_ThirdPersonManager.SetDesiredCameraOffset( Vector( m_flTauntCamCurrentDist, 0, m_flTauntCamCurrentDistUp ) );
  5184. if ( m_flTauntCamCurrentDist == m_flTauntCamTargetDist && m_flTauntCamCurrentDistUp == m_flTauntCamTargetDistUp )
  5185. {
  5186. if ( m_flTauntCamTargetDist == 0.f )
  5187. TurnOffTauntCam_Finish();
  5188. else
  5189. TurnOnTauntCam_Finish();
  5190. }
  5191. }
  5192. }
  5193. //-----------------------------------------------------------------------------
  5194. // Purpose:
  5195. //-----------------------------------------------------------------------------
  5196. void C_TFPlayer::PlayTauntSoundLoop( const char *pszSoundLoopName )
  5197. {
  5198. if ( pszSoundLoopName && *pszSoundLoopName )
  5199. {
  5200. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  5201. CPASAttenuationFilter filter( this );
  5202. m_pTauntSoundLoop = controller.SoundCreate( filter, entindex(), pszSoundLoopName );
  5203. controller.Play( m_pTauntSoundLoop, 1.0, 100 );
  5204. }
  5205. }
  5206. //-----------------------------------------------------------------------------
  5207. // Purpose:
  5208. //-----------------------------------------------------------------------------
  5209. void C_TFPlayer::StopTauntSoundLoop()
  5210. {
  5211. if ( m_pTauntSoundLoop )
  5212. {
  5213. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  5214. controller.SoundDestroy( m_pTauntSoundLoop );
  5215. m_pTauntSoundLoop = NULL;
  5216. }
  5217. }
  5218. //-----------------------------------------------------------------------------
  5219. // Purpose: Indicates whether the spy's cigarette should be burning or not.
  5220. //-----------------------------------------------------------------------------
  5221. bool C_TFPlayer::CanLightCigarette( void )
  5222. {
  5223. // Used to be a massive if-conditional.
  5224. // Expanded for readability.
  5225. if ( !IsPlayerClass( TF_CLASS_SPY ) )
  5226. return false;
  5227. if ( !IsAlive() )
  5228. return false;
  5229. // Don't light if we are disguised and an enemy (not the spy model).
  5230. if ( m_Shared.InCond( TF_COND_DISGUISED ) && IsEnemyPlayer() )
  5231. {
  5232. if ( m_Shared.GetDisguiseClass() != TF_CLASS_SPY )
  5233. {
  5234. return false;
  5235. }
  5236. }
  5237. // don't light for MvM Spy robots
  5238. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  5239. return false;
  5240. // Don't light if we are invis.
  5241. if ( GetPercentInvisible() > 0 )
  5242. return false;
  5243. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  5244. // Don't light for the local player.
  5245. if ( ( pLocalPlayer == this ) || !pLocalPlayer )
  5246. return false;
  5247. // Don't light if we're spectating in first person mode.
  5248. if ( (pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE) && (pLocalPlayer->GetObserverTarget() == this) )
  5249. return false;
  5250. // Don't light if we're covered in urine.
  5251. if ( m_Shared.InCond( TF_COND_URINE ) )
  5252. return false;
  5253. return true;
  5254. }
  5255. //-----------------------------------------------------------------------------
  5256. // Purpose: Flash the bomb hat at ever increasing frequency
  5257. //-----------------------------------------------------------------------------
  5258. void C_TFPlayer::HalloweenBombHeadUpdate( void )
  5259. {
  5260. if ( m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  5261. {
  5262. if ( !m_hHalloweenBombHat && gpGlobals->curtime > m_flBombDelay )
  5263. {
  5264. m_hHalloweenBombHat = C_PlayerAttachedModel::Create( BOMB_HAT_MODEL, this, LookupAttachment("head"), vec3_origin, PAM_PERMANENT, 0 );
  5265. m_hHalloweenBombHat->FollowEntity( this, true );
  5266. }
  5267. if ( m_hHalloweenBombHat )
  5268. {
  5269. m_hHalloweenBombHat->m_nSkin = m_Shared.m_nHalloweenBombHeadStage;
  5270. }
  5271. }
  5272. else
  5273. {
  5274. if ( m_hHalloweenBombHat )
  5275. {
  5276. m_hHalloweenBombHat->StopFollowingEntity();
  5277. m_hHalloweenBombHat->Release();
  5278. }
  5279. }
  5280. }
  5281. //-----------------------------------------------------------------------------
  5282. // Purpose:
  5283. //-----------------------------------------------------------------------------
  5284. bool C_TFPlayer::ShouldPlayerDrawParticles( void )
  5285. {
  5286. #ifdef STAGING_ONLY
  5287. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  5288. if ( pLocalTFPlayer && pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) )
  5289. return false;
  5290. #endif // TF_CLIENT_DLL
  5291. return true;
  5292. }
  5293. //-----------------------------------------------------------------------------
  5294. // Purpose: Check if passed in player is on the local player's friends list
  5295. //-----------------------------------------------------------------------------
  5296. bool C_TFPlayer::IsPlayerOnSteamFriendsList( C_BasePlayer *pPlayer )
  5297. {
  5298. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  5299. if ( !pLocalPlayer )
  5300. return false;
  5301. if ( !pPlayer )
  5302. return false;
  5303. if ( !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() )
  5304. return false;
  5305. player_info_t pi;
  5306. if ( engine->GetPlayerInfo( pPlayer->entindex(), &pi ) && pi.friendsID )
  5307. {
  5308. CSteamID steamID( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
  5309. if ( steamapicontext->SteamFriends()->HasFriend( steamID, k_EFriendFlagImmediate ) )
  5310. return true;
  5311. }
  5312. return false;
  5313. }
  5314. #ifdef STAGING_ONLY
  5315. ConVar tf_debug_moving_taunt( "tf_debug_moving_taunt", "1" );
  5316. #endif // STAGING_ONLY
  5317. //-----------------------------------------------------------------------------
  5318. // Purpose:
  5319. //-----------------------------------------------------------------------------
  5320. void C_TFPlayer::ClientThink()
  5321. {
  5322. // Pass on through to the base class.
  5323. BaseClass::ClientThink();
  5324. UpdateIDTarget();
  5325. UpdateLookAt();
  5326. UpdateOverheadEffects();
  5327. #ifdef STAGING_ONLY
  5328. if ( tf_debug_moving_taunt.GetBool() )
  5329. #endif // STAGING_ONLY
  5330. {
  5331. if ( !IsTaunting() && m_PlayerAnimState->IsGestureSlotActive( GESTURE_SLOT_VCD ) )
  5332. {
  5333. #ifdef STAGING_ONLY
  5334. CAnimationLayer *pLayer = m_PlayerAnimState->GetGestureSlotLayer( GESTURE_SLOT_VCD );
  5335. if ( pLayer )
  5336. {
  5337. const char *pszSequenceName = GetSequenceName( pLayer->m_nSequence );
  5338. Warning( "'%s' is playing '%s' sequence when not taunting.\n", GetPlayerName(), pszSequenceName );
  5339. }
  5340. #endif // STAGING_ONLY
  5341. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  5342. }
  5343. }
  5344. // NVNT update state based effects (prior to healer clear)
  5345. if ( haptics &&haptics->HasDevice() && IsLocalPlayer())
  5346. {
  5347. tfHaptics.HapticsThink(this);
  5348. }
  5349. // Clear our healer, it'll be reset by the medigun client think if we're being healed
  5350. m_hHealer = NULL;
  5351. // Start smoke if we're not invisible or disguised.
  5352. if ( CanLightCigarette() )
  5353. {
  5354. if ( !m_bCigaretteSmokeActive )
  5355. {
  5356. int iSmokeAttachment = LookupAttachment( "cig_smoke" );
  5357. ParticleProp()->Create( "cig_smoke", PATTACH_POINT_FOLLOW, iSmokeAttachment );
  5358. m_bCigaretteSmokeActive = true;
  5359. }
  5360. }
  5361. else // stop the smoke otherwise if its active
  5362. {
  5363. if ( m_bCigaretteSmokeActive )
  5364. {
  5365. ParticleProp()->StopParticlesNamed( "cig_smoke", false );
  5366. m_bCigaretteSmokeActive = false;
  5367. }
  5368. }
  5369. if ( m_bWaterExitEffectActive && !IsAlive() )
  5370. {
  5371. ParticleProp()->StopParticlesNamed( "water_playeremerge", false );
  5372. m_bWaterExitEffectActive = false;
  5373. }
  5374. // Kill the effect if either
  5375. // a) the player is dead
  5376. // b) the enemy disguised spy is now invisible
  5377. if ( !IsAlive() ||
  5378. ( m_Shared.InCond( TF_COND_DISGUISED ) && IsEnemyPlayer() && ( GetPercentInvisible() > 0 ) ) )
  5379. {
  5380. StopSaveMeEffect( true );
  5381. }
  5382. if ( ShouldTauntHintIconBeVisible() )
  5383. {
  5384. CreateTauntWithMeEffect();
  5385. }
  5386. else
  5387. {
  5388. StopTauntWithMeEffect();
  5389. }
  5390. if ( IsLocalPlayer() )
  5391. {
  5392. g_ItemEffectMeterManager.Update( this );
  5393. }
  5394. if ( m_Shared.InCond( TF_COND_DEMO_BUFF ) )
  5395. {
  5396. m_Shared.ClientDemoBuffThink();
  5397. }
  5398. if ( m_pBlastJumpLoop )
  5399. {
  5400. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  5401. if ( !IsAlive() )
  5402. {
  5403. controller.SoundDestroy( m_pBlastJumpLoop );
  5404. m_pBlastJumpLoop = NULL;
  5405. }
  5406. else
  5407. {
  5408. float flTimeAloft = gpGlobals->curtime - m_flBlastJumpLaunchTime;
  5409. float flPitch = RemapValClamped( flTimeAloft, 0.1f, 3.f, 200.f, 100.f );
  5410. float flVolume = RemapValClamped( flTimeAloft, 0.1f, 2.f, 0.25f, 0.95f );
  5411. controller.SoundChangePitch( m_pBlastJumpLoop, flPitch, 0.1f );
  5412. controller.SoundChangeVolume( m_pBlastJumpLoop, flVolume, 0.1f );
  5413. }
  5414. }
  5415. if ( HasTheFlag() && GetGlowObject() )
  5416. {
  5417. C_TFItem *pFlag = GetItem();
  5418. if ( pFlag->ShouldHideGlowEffect() )
  5419. {
  5420. GetGlowObject()->SetEntity( NULL );
  5421. }
  5422. else
  5423. {
  5424. GetGlowObject()->SetEntity( this );
  5425. }
  5426. }
  5427. #ifdef STAGING_ONLY
  5428. /*
  5429. // this code was added for a new sniper rifle that was being looked at, but it breaks glows for existing players by always
  5430. // turning off the glow if the weapon doesn't think it needs to be on. this needs to be fixed before we can use it.
  5431. // some mods use the glows and the SourceTV clients see player glows when they're watching a match.
  5432. else
  5433. {
  5434. bool bGlow = false;
  5435. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  5436. if ( pLocalTFPlayer && pLocalTFPlayer->m_Shared.InCond( TF_COND_ZOOMED ) && !( pLocalTFPlayer->InSameTeam( this ) || m_Shared.GetDisguiseTeam() == pLocalTFPlayer->GetTeamNumber() ) )
  5437. {
  5438. int iGlowEnemiesInScope = 0;
  5439. CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalTFPlayer, iGlowEnemiesInScope, add_sniper_glow_enemies_in_scope );
  5440. if ( iGlowEnemiesInScope != 0 )
  5441. {
  5442. trace_t tr;
  5443. UTIL_TraceLine( pLocalTFPlayer->EyePosition(), EyePosition(), MASK_BLOCKLOS, pLocalTFPlayer, COLLISION_GROUP_NONE, &tr );
  5444. if ( !tr.DidHit() )
  5445. {
  5446. bGlow = true;
  5447. }
  5448. }
  5449. }
  5450. if ( bGlow )
  5451. {
  5452. float r, g, b;
  5453. GetGlowEffectColor( &r, &g, &b );
  5454. EnableGlowEffect( r, g, b );
  5455. }
  5456. else
  5457. {
  5458. DestroyGlowEffect();
  5459. }
  5460. }
  5461. */
  5462. #endif // STAGING_ONLY
  5463. m_Shared.ClientKillStreakBuffThink();
  5464. /*
  5465. if ( m_LeaveServerTimer.HasStarted() && m_LeaveServerTimer.IsElapsed() )
  5466. {
  5467. engine->ExecuteClientCmd( "disconnect" );
  5468. }
  5469. */
  5470. if ( m_Shared.IsEnteringOrExitingFullyInvisible() )
  5471. {
  5472. UpdateSpyStateChange();
  5473. }
  5474. // Predict Halloween Kart
  5475. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) && !m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  5476. {
  5477. if ( IsLocalPlayer() && cl_predict->GetBool() )
  5478. {
  5479. cl_predict->SetValue( false );
  5480. }
  5481. //if ( m_nButtons & IN_ATTACK )
  5482. //{
  5483. // // Check if this is the spellbook so we can save off info to preserve weapon switching
  5484. // CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  5485. // if ( pSpellBook )
  5486. // {
  5487. // if ( pSpellBook )
  5488. // {
  5489. // pSpellBook->PrimaryAttack();
  5490. // }
  5491. // }
  5492. //}
  5493. //// Speed Boost
  5494. //if ( m_nButtons & IN_ATTACK2 )
  5495. //{
  5496. // if ( GetKartSpeedBoost() >= 1.0f )
  5497. // {
  5498. // m_flKartNextAvailableBoost = gpGlobals->curtime + tf_halloween_kart_boost_recharge.GetFloat();
  5499. // m_Shared.AddCond( TF_COND_HALLOWEEN_KART_DASH, tf_halloween_kart_boost_duration.GetFloat() );
  5500. // }
  5501. //}
  5502. }
  5503. else
  5504. {
  5505. // Make Sure its on otherwise
  5506. if ( IsLocalPlayer() && !cl_predict->GetBool() )
  5507. {
  5508. cl_predict->SetValue( true );
  5509. }
  5510. }
  5511. // update rune charge particle
  5512. if ( m_Shared.IsRuneCharged() && !m_pRuneChargeReadyEffect && !m_Shared.IsStealthed() )
  5513. {
  5514. m_pRuneChargeReadyEffect = ParticleProp()->Create( "powerup_supernova_ready", PATTACH_ABSORIGIN_FOLLOW );
  5515. }
  5516. else if ( m_pRuneChargeReadyEffect && ( m_Shared.IsStealthed() || !m_Shared.IsRuneCharged() ) )
  5517. {
  5518. ParticleProp()->StopEmission( m_pRuneChargeReadyEffect );
  5519. m_pRuneChargeReadyEffect = NULL;
  5520. }
  5521. UpdateRuneIcon();
  5522. UpdatedMarkedForDeathEffect();
  5523. if ( TFGameRules() && TFGameRules()->IsPasstimeMode() )
  5524. {
  5525. //
  5526. // Passtime player reticle
  5527. //
  5528. if ( !IsLocalPlayer() && !m_pPasstimePlayerReticle )
  5529. {
  5530. m_pPasstimePlayerReticle = new C_PasstimePlayerReticle( this );
  5531. }
  5532. if ( m_pPasstimePlayerReticle )
  5533. {
  5534. m_pPasstimePlayerReticle->OnClientThink();
  5535. }
  5536. //
  5537. // Passtime ask for ball reticle
  5538. //
  5539. if ( !IsLocalPlayer() && !m_pPasstimeAskForBallReticle )
  5540. {
  5541. m_pPasstimeAskForBallReticle = new C_PasstimeAskForBallReticle( this );
  5542. }
  5543. if ( m_pPasstimeAskForBallReticle )
  5544. {
  5545. m_pPasstimeAskForBallReticle->OnClientThink();
  5546. }
  5547. //
  5548. // Passtime ask for ball button
  5549. //
  5550. if ( m_nButtons & IN_ATTACK3 )
  5551. {
  5552. engine->ClientCmd("voicemenu 1 8");
  5553. }
  5554. }
  5555. }
  5556. void C_TFPlayer::Touch( CBaseEntity *pOther )
  5557. {
  5558. BaseClass::Touch( pOther );
  5559. C_TFPlayer *pVictim = ToTFPlayer( pOther );
  5560. if ( pVictim )
  5561. {
  5562. // ****************************************************************************************************************
  5563. // Halloween Karts
  5564. // Predict Dash Crash
  5565. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH ) )
  5566. {
  5567. Vector vAim = GetLocalVelocity();
  5568. vAim.NormalizeInPlace();
  5569. vAim.z += 0.50f;
  5570. vAim.NormalizeInPlace();
  5571. Vector vOrigin = GetAbsOrigin();
  5572. // Force direction is velocity of the player in the case that this is a head on collison.
  5573. // Trace
  5574. trace_t pTrace;
  5575. Ray_t ray;
  5576. CTraceFilterOnlyNPCsAndPlayer pFilter( this, COLLISION_GROUP_NONE );
  5577. ray.Init( vOrigin, vOrigin + vAim * 16, GetPlayerMins(), GetPlayerMaxs() );
  5578. enginetrace->TraceRay( ray, MASK_SOLID, &pFilter, &pTrace );
  5579. Vector vecForceDirection;
  5580. vecForceDirection = vAim;
  5581. if ( pTrace.m_pEnt == pVictim )
  5582. {
  5583. // Stop moving
  5584. SetAbsVelocity( vec3_origin );
  5585. SetCurrentTauntMoveSpeed( 0 );
  5586. m_Shared.RemoveCond( TF_COND_HALLOWEEN_KART_DASH );
  5587. }
  5588. }
  5589. }
  5590. }
  5591. //
  5592. //-----------------------------------------------------------------------------
  5593. bool C_TFPlayer::GetPredictable( void ) const
  5594. {
  5595. // Halloween Kart Hackery to Get prediction system to behave the way we want we though prediction is actually off
  5596. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  5597. {
  5598. return true;
  5599. }
  5600. return BaseClass::GetPredictable();
  5601. }
  5602. //-----------------------------------------------------------------------------
  5603. // Purpose:
  5604. //-----------------------------------------------------------------------------
  5605. void C_TFPlayer::UpdateLookAt( void )
  5606. {
  5607. bool bFoundViewTarget = false;
  5608. Vector vForward;
  5609. AngleVectors( GetLocalAngles(), &vForward );
  5610. Vector vMyOrigin = GetAbsOrigin();
  5611. Vector vecLookAtTarget = vec3_origin;
  5612. if ( tf_clientsideeye_lookats.GetBool() )
  5613. {
  5614. for( int iClient = 1; iClient <= gpGlobals->maxClients; ++iClient )
  5615. {
  5616. CBaseEntity *pEnt = UTIL_PlayerByIndex( iClient );
  5617. if ( !pEnt || !pEnt->IsPlayer() )
  5618. continue;
  5619. if ( !pEnt->IsAlive() )
  5620. continue;
  5621. if ( pEnt == this )
  5622. continue;
  5623. Vector vDir = pEnt->GetAbsOrigin() - vMyOrigin;
  5624. if ( vDir.Length() > 300 )
  5625. continue;
  5626. VectorNormalize( vDir );
  5627. if ( DotProduct( vForward, vDir ) < 0.0f )
  5628. continue;
  5629. vecLookAtTarget = pEnt->EyePosition();
  5630. bFoundViewTarget = true;
  5631. break;
  5632. }
  5633. }
  5634. if ( bFoundViewTarget == false )
  5635. {
  5636. // no target, look forward
  5637. vecLookAtTarget = GetAbsOrigin() + vForward * 512;
  5638. }
  5639. // orient eyes
  5640. m_viewtarget = vecLookAtTarget;
  5641. /*
  5642. // blinking
  5643. if (m_blinkTimer.IsElapsed())
  5644. {
  5645. m_blinktoggle = !m_blinktoggle;
  5646. m_blinkTimer.Start( RandomFloat( 1.5f, 4.0f ) );
  5647. }
  5648. */
  5649. /*
  5650. // Figure out where we want to look in world space.
  5651. QAngle desiredAngles;
  5652. Vector to = vecLookAtTarget - EyePosition();
  5653. VectorAngles( to, desiredAngles );
  5654. // Figure out where our body is facing in world space.
  5655. QAngle bodyAngles( 0, 0, 0 );
  5656. bodyAngles[YAW] = GetLocalAngles()[YAW];
  5657. float flBodyYawDiff = bodyAngles[YAW] - m_flLastBodyYaw;
  5658. m_flLastBodyYaw = bodyAngles[YAW];
  5659. // Set the head's yaw.
  5660. float desired = AngleNormalize( desiredAngles[YAW] - bodyAngles[YAW] );
  5661. desired = clamp( -desired, m_headYawMin, m_headYawMax );
  5662. m_flCurrentHeadYaw = ApproachAngle( desired, m_flCurrentHeadYaw, 130 * gpGlobals->frametime );
  5663. // Counterrotate the head from the body rotation so it doesn't rotate past its target.
  5664. m_flCurrentHeadYaw = AngleNormalize( m_flCurrentHeadYaw - flBodyYawDiff );
  5665. SetPoseParameter( m_headYawPoseParam, m_flCurrentHeadYaw );
  5666. // Set the head's yaw.
  5667. desired = AngleNormalize( desiredAngles[PITCH] );
  5668. desired = clamp( desired, m_headPitchMin, m_headPitchMax );
  5669. m_flCurrentHeadPitch = ApproachAngle( -desired, m_flCurrentHeadPitch, 130 * gpGlobals->frametime );
  5670. m_flCurrentHeadPitch = AngleNormalize( m_flCurrentHeadPitch );
  5671. SetPoseParameter( m_headPitchPoseParam, m_flCurrentHeadPitch );
  5672. */
  5673. }
  5674. //-----------------------------------------------------------------------------
  5675. // Purpose: Try to steer away from any players and objects we might interpenetrate
  5676. //-----------------------------------------------------------------------------
  5677. #define TF_AVOID_MAX_RADIUS_SQR 5184.0f // Based on player extents and max buildable extents.
  5678. #define TF_OO_AVOID_MAX_RADIUS_SQR 0.00019f
  5679. ConVar tf_max_separation_force ( "tf_max_separation_force", "256", FCVAR_DEVELOPMENTONLY );
  5680. extern ConVar cl_forwardspeed;
  5681. extern ConVar cl_backspeed;
  5682. extern ConVar cl_sidespeed;
  5683. void C_TFPlayer::AvoidPlayers( CUserCmd *pCmd )
  5684. {
  5685. // Turn off the avoid player code.
  5686. if ( !tf_avoidteammates.GetBool() || !tf_avoidteammates_pushaway.GetBool() )
  5687. return;
  5688. // Don't test if the player doesn't exist or is dead.
  5689. if ( IsAlive() == false )
  5690. return;
  5691. C_TFTeam *pTeam = (C_TFTeam*)GetTeam();
  5692. if ( !pTeam )
  5693. return;
  5694. CHudUpgradePanel *pHudVote = GET_HUDELEMENT( CHudUpgradePanel );
  5695. if ( pHudVote && pHudVote->IsActive() )
  5696. {
  5697. return;
  5698. }
  5699. // Up vector.
  5700. static Vector vecUp( 0.0f, 0.0f, 1.0f );
  5701. Vector vecTFPlayerCenter = GetAbsOrigin();
  5702. Vector vecTFPlayerMin = GetPlayerMins();
  5703. Vector vecTFPlayerMax = GetPlayerMaxs();
  5704. float flZHeight = vecTFPlayerMax.z - vecTFPlayerMin.z;
  5705. vecTFPlayerCenter.z += 0.5f * flZHeight;
  5706. VectorAdd( vecTFPlayerMin, vecTFPlayerCenter, vecTFPlayerMin );
  5707. VectorAdd( vecTFPlayerMax, vecTFPlayerCenter, vecTFPlayerMax );
  5708. // Find an intersecting player or object.
  5709. int nAvoidPlayerCount = 0;
  5710. C_TFPlayer *pAvoidPlayerList[MAX_PLAYERS];
  5711. C_TFPlayer *pIntersectPlayer = NULL;
  5712. CBaseObject *pIntersectObject = NULL;
  5713. float flAvoidRadius = 0.0f;
  5714. Vector vecAvoidCenter, vecAvoidMin, vecAvoidMax;
  5715. for ( int i = 0; i < pTeam->GetNumPlayers(); ++i )
  5716. {
  5717. C_TFPlayer *pAvoidPlayer = static_cast< C_TFPlayer * >( pTeam->GetPlayer( i ) );
  5718. if ( pAvoidPlayer == NULL )
  5719. continue;
  5720. // Is the avoid player me?
  5721. if ( pAvoidPlayer == this )
  5722. continue;
  5723. // Save as list to check against for objects.
  5724. pAvoidPlayerList[nAvoidPlayerCount] = pAvoidPlayer;
  5725. ++nAvoidPlayerCount;
  5726. // Check to see if the avoid player is dormant.
  5727. if ( pAvoidPlayer->IsDormant() )
  5728. continue;
  5729. // Is the avoid player solid?
  5730. if ( pAvoidPlayer->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  5731. continue;
  5732. Vector t1, t2;
  5733. vecAvoidCenter = pAvoidPlayer->GetAbsOrigin();
  5734. vecAvoidMin = pAvoidPlayer->GetPlayerMins();
  5735. vecAvoidMax = pAvoidPlayer->GetPlayerMaxs();
  5736. flZHeight = vecAvoidMax.z - vecAvoidMin.z;
  5737. vecAvoidCenter.z += 0.5f * flZHeight;
  5738. VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
  5739. VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );
  5740. if ( IsBoxIntersectingBox( vecTFPlayerMin, vecTFPlayerMax, vecAvoidMin, vecAvoidMax ) )
  5741. {
  5742. // Need to avoid this player.
  5743. if ( !pIntersectPlayer )
  5744. {
  5745. pIntersectPlayer = pAvoidPlayer;
  5746. break;
  5747. }
  5748. }
  5749. }
  5750. // We didn't find a player - look for objects to avoid.
  5751. if ( !pIntersectPlayer )
  5752. {
  5753. for ( int iPlayer = 0; iPlayer < nAvoidPlayerCount; ++iPlayer )
  5754. {
  5755. // Stop when we found an intersecting object.
  5756. if ( pIntersectObject )
  5757. break;
  5758. for ( int iObject = 0; iObject < pTeam->GetNumObjects(); ++iObject )
  5759. {
  5760. CBaseObject *pAvoidObject = pTeam->GetObject( iObject );
  5761. if ( !pAvoidObject )
  5762. continue;
  5763. // Check to see if the object is dormant.
  5764. if ( pAvoidObject->IsDormant() )
  5765. continue;
  5766. // Is the object solid.
  5767. if ( pAvoidObject->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  5768. continue;
  5769. // If we shouldn't avoid it, see if we intersect it.
  5770. if ( pAvoidObject->ShouldPlayersAvoid() )
  5771. {
  5772. vecAvoidCenter = pAvoidObject->WorldSpaceCenter();
  5773. vecAvoidMin = pAvoidObject->WorldAlignMins();
  5774. vecAvoidMax = pAvoidObject->WorldAlignMaxs();
  5775. VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
  5776. VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );
  5777. if ( IsBoxIntersectingBox( vecTFPlayerMin, vecTFPlayerMax, vecAvoidMin, vecAvoidMax ) )
  5778. {
  5779. // Need to avoid this object.
  5780. pIntersectObject = pAvoidObject;
  5781. break;
  5782. }
  5783. }
  5784. }
  5785. }
  5786. }
  5787. // Anything to avoid?
  5788. if ( !pIntersectPlayer && !pIntersectObject )
  5789. {
  5790. m_Shared.SetSeparation( false );
  5791. m_Shared.SetSeparationVelocity( vec3_origin );
  5792. return;
  5793. }
  5794. // Calculate the push strength and direction.
  5795. Vector vecDelta;
  5796. // Avoid a player - they have precedence.
  5797. if ( pIntersectPlayer )
  5798. {
  5799. VectorSubtract( pIntersectPlayer->WorldSpaceCenter(), vecTFPlayerCenter, vecDelta );
  5800. Vector vRad = pIntersectPlayer->WorldAlignMaxs() - pIntersectPlayer->WorldAlignMins();
  5801. vRad.z = 0;
  5802. flAvoidRadius = vRad.Length();
  5803. }
  5804. // Avoid a object.
  5805. else
  5806. {
  5807. VectorSubtract( pIntersectObject->WorldSpaceCenter(), vecTFPlayerCenter, vecDelta );
  5808. Vector vRad = pIntersectObject->WorldAlignMaxs() - pIntersectObject->WorldAlignMins();
  5809. vRad.z = 0;
  5810. flAvoidRadius = vRad.Length();
  5811. }
  5812. float flPushStrength = RemapValClamped( vecDelta.Length(), flAvoidRadius, 0, 0, tf_max_separation_force.GetInt() ); //flPushScale;
  5813. //Msg( "PushScale = %f\n", flPushStrength );
  5814. // Check to see if we have enough push strength to make a difference.
  5815. if ( flPushStrength < 0.01f )
  5816. return;
  5817. Vector vecPush;
  5818. if ( GetAbsVelocity().Length2DSqr() > 0.1f )
  5819. {
  5820. Vector vecVelocity = GetAbsVelocity();
  5821. vecVelocity.z = 0.0f;
  5822. CrossProduct( vecUp, vecVelocity, vecPush );
  5823. VectorNormalize( vecPush );
  5824. }
  5825. else
  5826. {
  5827. // We are not moving, but we're still intersecting.
  5828. QAngle angView = pCmd->viewangles;
  5829. angView.x = 0.0f;
  5830. AngleVectors( angView, NULL, &vecPush, NULL );
  5831. }
  5832. // Move away from the other player/object.
  5833. Vector vecSeparationVelocity;
  5834. if ( vecDelta.Dot( vecPush ) < 0 )
  5835. {
  5836. vecSeparationVelocity = vecPush * flPushStrength;
  5837. }
  5838. else
  5839. {
  5840. vecSeparationVelocity = vecPush * -flPushStrength;
  5841. }
  5842. // Don't allow the max push speed to be greater than the max player speed.
  5843. float flMaxPlayerSpeed = MaxSpeed();
  5844. float flCropFraction = 1.33333333f;
  5845. if ( ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
  5846. {
  5847. flMaxPlayerSpeed *= flCropFraction;
  5848. }
  5849. float flMaxPlayerSpeedSqr = flMaxPlayerSpeed * flMaxPlayerSpeed;
  5850. if ( vecSeparationVelocity.LengthSqr() > flMaxPlayerSpeedSqr )
  5851. {
  5852. vecSeparationVelocity.NormalizeInPlace();
  5853. VectorScale( vecSeparationVelocity, flMaxPlayerSpeed, vecSeparationVelocity );
  5854. }
  5855. QAngle vAngles = pCmd->viewangles;
  5856. vAngles.x = 0;
  5857. Vector currentdir;
  5858. Vector rightdir;
  5859. AngleVectors( vAngles, &currentdir, &rightdir, NULL );
  5860. Vector vDirection = vecSeparationVelocity;
  5861. VectorNormalize( vDirection );
  5862. float fwd = currentdir.Dot( vDirection );
  5863. float rt = rightdir.Dot( vDirection );
  5864. float forward = fwd * flPushStrength;
  5865. float side = rt * flPushStrength;
  5866. //Msg( "fwd: %f - rt: %f - forward: %f - side: %f\n", fwd, rt, forward, side );
  5867. m_Shared.SetSeparation( true );
  5868. m_Shared.SetSeparationVelocity( vecSeparationVelocity );
  5869. pCmd->forwardmove += forward;
  5870. pCmd->sidemove += side;
  5871. // Clamp the move to within legal limits, preserving direction. This is a little
  5872. // complicated because we have different limits for forward, back, and side
  5873. //Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
  5874. float flForwardScale = 1.0f;
  5875. if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
  5876. {
  5877. flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
  5878. }
  5879. else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
  5880. {
  5881. flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
  5882. }
  5883. float flSideScale = 1.0f;
  5884. if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
  5885. {
  5886. flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
  5887. }
  5888. float flScale = MIN( flForwardScale, flSideScale );
  5889. pCmd->forwardmove *= flScale;
  5890. pCmd->sidemove *= flScale;
  5891. //Msg( "Pforwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
  5892. }
  5893. //-----------------------------------------------------------------------------
  5894. // Purpose:
  5895. // Input : flInputSampleTime -
  5896. // *pCmd -
  5897. //-----------------------------------------------------------------------------
  5898. bool C_TFPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
  5899. {
  5900. static QAngle angMoveAngle( 0.0f, 0.0f, 0.0f );
  5901. static float flTauntTurnSpeed = 0.f;
  5902. bool bNoTaunt = true;
  5903. bool bInTaunt = m_Shared.InCond( TF_COND_TAUNTING ) || m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER );
  5904. if ( m_Shared.InCond( TF_COND_FREEZE_INPUT ) )
  5905. {
  5906. pCmd->viewangles = angMoveAngle; // use the last save angles
  5907. pCmd->forwardmove = 0.0f;
  5908. pCmd->sidemove = 0.0f;
  5909. pCmd->upmove = 0.0f;
  5910. pCmd->buttons = 0;
  5911. pCmd->weaponselect = 0;
  5912. pCmd->weaponsubtype = 0;
  5913. pCmd->mousedx = 0;
  5914. pCmd->mousedy = 0;
  5915. }
  5916. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  5917. {
  5918. m_Shared.CreateVehicleMove( flInputSampleTime, pCmd );
  5919. }
  5920. else if ( bInTaunt )
  5921. {
  5922. if ( tf_allow_taunt_switch.GetInt() <= 1 )
  5923. {
  5924. pCmd->weaponselect = 0;
  5925. }
  5926. int nCurrentButtons = pCmd->buttons;
  5927. pCmd->buttons = 0;
  5928. if ( !CanMoveDuringTaunt() )
  5929. {
  5930. pCmd->forwardmove = 0.0f;
  5931. pCmd->sidemove = 0.0f;
  5932. pCmd->upmove = 0.0f;
  5933. VectorCopy( angMoveAngle, pCmd->viewangles );
  5934. }
  5935. else
  5936. {
  5937. float flSign = pCmd->sidemove != 0.f ? 1.f : -1.f;
  5938. float flMaxTurnSpeed = m_flTauntTurnSpeed;
  5939. #ifdef STAGING_ONLY
  5940. flMaxTurnSpeed = cl_taunt_max_turn_speed.GetFloat() > 0.f ? cl_taunt_max_turn_speed.GetFloat() : flMaxTurnSpeed;
  5941. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  5942. {
  5943. flMaxTurnSpeed = cl_halloween_kart_turn_speed.GetFloat();
  5944. }
  5945. #endif // STAGING_ONLY
  5946. if ( m_flTauntTurnAccelerationTime > 0.f )
  5947. {
  5948. flTauntTurnSpeed = clamp( flTauntTurnSpeed + flSign * ( flInputSampleTime / m_flTauntTurnAccelerationTime ) * flMaxTurnSpeed, 0.f, flMaxTurnSpeed );
  5949. }
  5950. else
  5951. {
  5952. flTauntTurnSpeed = flMaxTurnSpeed;
  5953. }
  5954. float flSmoothTurnSpeed = 0.f;
  5955. if ( flMaxTurnSpeed > 0.f )
  5956. {
  5957. flSmoothTurnSpeed = SimpleSpline( flTauntTurnSpeed / flMaxTurnSpeed ) * flMaxTurnSpeed;
  5958. }
  5959. // only let these button through
  5960. if ( pCmd->sidemove < 0 )
  5961. {
  5962. angMoveAngle += QAngle( 0.f, flSmoothTurnSpeed * flInputSampleTime, 0.f );
  5963. }
  5964. else if( pCmd->sidemove > 0 )
  5965. {
  5966. angMoveAngle += QAngle( 0.f, -flSmoothTurnSpeed * flInputSampleTime, 0.f );
  5967. }
  5968. pCmd->buttons = nCurrentButtons & ( IN_MOVELEFT | IN_MOVERIGHT | IN_FORWARD | IN_BACK );
  5969. pCmd->sidemove = 0.0f;
  5970. VectorCopy( angMoveAngle, pCmd->viewangles );
  5971. }
  5972. // allow remap taunt keys to go through
  5973. CTFTauntInfo *pTaunt = m_TauntEconItemView.GetStaticData()->GetTauntData();
  5974. if ( pTaunt )
  5975. {
  5976. for ( int i=0; i<pTaunt->GetTauntInputRemapCount(); ++i )
  5977. {
  5978. const CTFTauntInfo::TauntInputRemap_t& tauntRemap = pTaunt->GetTauntInputRemapScene( i );
  5979. if ( nCurrentButtons & tauntRemap.m_iButton )
  5980. {
  5981. pCmd->buttons |= tauntRemap.m_iButton;
  5982. }
  5983. }
  5984. }
  5985. bNoTaunt = false;
  5986. }
  5987. else
  5988. {
  5989. flTauntTurnSpeed = 0.f;
  5990. VectorCopy( pCmd->viewangles, angMoveAngle );
  5991. }
  5992. BaseClass::CreateMove( flInputSampleTime, pCmd );
  5993. // Don't avoid players if in the middle of a high five. This prevents high-fivers from becoming separated.
  5994. if ( !bInTaunt || ( !m_bIsReadyToHighFive && !CTFPlayerSharedUtils::ConceptIsPartnerTaunt( m_Shared.m_iTauntConcept ) ) )
  5995. {
  5996. AvoidPlayers( pCmd );
  5997. }
  5998. return bNoTaunt;
  5999. }
  6000. //-----------------------------------------------------------------------------
  6001. // Purpose: This prevents some anims from being thrown out when the client is in prediction simulation.
  6002. // Stun anims, for example, (additive gestures) are synchronized by time and can keep playing on the client
  6003. // to prevent pauses during the stun loop.
  6004. //-----------------------------------------------------------------------------
  6005. bool C_TFPlayer::PlayAnimEventInPrediction( PlayerAnimEvent_t event )
  6006. {
  6007. if ( !cl_predict->GetBool() )
  6008. return true;
  6009. switch ( event )
  6010. {
  6011. case PLAYERANIMEVENT_STUN_BEGIN:
  6012. case PLAYERANIMEVENT_STUN_MIDDLE:
  6013. case PLAYERANIMEVENT_STUN_END:
  6014. case PLAYERANIMEVENT_PASSTIME_THROW_BEGIN:
  6015. case PLAYERANIMEVENT_PASSTIME_THROW_MIDDLE:
  6016. case PLAYERANIMEVENT_PASSTIME_THROW_END:
  6017. case PLAYERANIMEVENT_PASSTIME_THROW_CANCEL:
  6018. return true;
  6019. default:
  6020. return false;
  6021. }
  6022. }
  6023. //-----------------------------------------------------------------------------
  6024. // Purpose:
  6025. //-----------------------------------------------------------------------------
  6026. void C_TFPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  6027. {
  6028. if ( IsLocalPlayer() )
  6029. {
  6030. if ( !prediction->IsFirstTimePredicted() && !PlayAnimEventInPrediction( event ) )
  6031. return;
  6032. }
  6033. MDLCACHE_CRITICAL_SECTION();
  6034. m_PlayerAnimState->DoAnimationEvent( event, nData );
  6035. }
  6036. //-----------------------------------------------------------------------------
  6037. // Purpose:
  6038. //-----------------------------------------------------------------------------
  6039. void C_TFPlayer::CreateBombonomiconHint()
  6040. {
  6041. if ( IsLocalPlayer() && IsAlive() )
  6042. {
  6043. m_hBombonomiconHint = C_MerasmusBombEffect::Create( BOMBONOMICON_MODEL, this, Vector(-40, 0, 120), QAngle(30, 0, 0), 100.0, 4.0, PRM_SPIN_Z );
  6044. m_flBombDelay = gpGlobals->curtime + 2.0f;
  6045. m_hBombonomiconHint->SetModelScale( 0.5f );
  6046. CSingleUserRecipientFilter filter(this);
  6047. CSoundParameters params;
  6048. if ( CBaseEntity::GetParametersForSound( "Halloween.BombinomiconSpin", params, NULL ) )
  6049. {
  6050. EmitSound_t es( params );
  6051. EmitSound( filter, m_hBombonomiconHint->entindex(), es );
  6052. }
  6053. }
  6054. }
  6055. //-----------------------------------------------------------------------------
  6056. // Purpose:
  6057. //-----------------------------------------------------------------------------
  6058. void C_TFPlayer::DestroyBombonomiconHint()
  6059. {
  6060. if ( IsLocalPlayer() )
  6061. {
  6062. if ( m_hBombonomiconHint )
  6063. {
  6064. m_hBombonomiconHint->Release();
  6065. }
  6066. }
  6067. }
  6068. //-----------------------------------------------------------------------------
  6069. // Purpose: Similar to OnNewModel. only reset animation related data
  6070. //-----------------------------------------------------------------------------
  6071. void C_TFPlayer::CleanUpAnimationOnSpawn()
  6072. {
  6073. CStudioHdr *hdr = GetModelPtr();
  6074. if ( !hdr )
  6075. return;
  6076. InitializePoseParams();
  6077. // Init flexes, cancel any scenes we're playing
  6078. ClearSceneEvents( NULL, false );
  6079. // Reset the flex weights.
  6080. ResetFlexWeights( GetModelPtr() );
  6081. // Reset the players animation states, gestures
  6082. if ( m_PlayerAnimState )
  6083. {
  6084. m_PlayerAnimState->ClearAnimationState();
  6085. }
  6086. }
  6087. //-----------------------------------------------------------------------------
  6088. // Purpose:
  6089. //-----------------------------------------------------------------------------
  6090. bool C_TFPlayer::IsABot( void )
  6091. {
  6092. if ( m_bIsABot )
  6093. return true;
  6094. if ( g_PR && g_PR->IsFakePlayer( entindex() ) )
  6095. return true;
  6096. return false;
  6097. }
  6098. //-----------------------------------------------------------------------------
  6099. // Purpose:
  6100. //-----------------------------------------------------------------------------
  6101. Vector C_TFPlayer::GetObserverCamOrigin( void )
  6102. {
  6103. if ( !IsAlive() )
  6104. {
  6105. if ( m_hFirstGib )
  6106. {
  6107. IPhysicsObject *pPhysicsObject = m_hFirstGib->VPhysicsGetObject();
  6108. if( pPhysicsObject )
  6109. {
  6110. Vector vecMassCenter = pPhysicsObject->GetMassCenterLocalSpace();
  6111. Vector vecWorld;
  6112. m_hFirstGib->CollisionProp()->CollisionToWorldSpace( vecMassCenter, &vecWorld );
  6113. return (vecWorld);
  6114. }
  6115. return m_hFirstGib->GetRenderOrigin();
  6116. }
  6117. return GetDeathViewPosition();
  6118. }
  6119. return BaseClass::GetObserverCamOrigin();
  6120. }
  6121. //-----------------------------------------------------------------------------
  6122. // Purpose: Consider the viewer and other factors when determining resulting
  6123. // invisibility
  6124. //-----------------------------------------------------------------------------
  6125. float C_TFPlayer::GetEffectiveInvisibilityLevel( void )
  6126. {
  6127. float flPercentInvisible = GetPercentInvisible();
  6128. // Crude way to limit Halloween spell
  6129. bool bHalloweenSpellStealth = TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) && m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF );
  6130. bool bLimitedInvis = !IsEnemyPlayer() || bHalloweenSpellStealth;
  6131. #ifdef STAGING_ONLY
  6132. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  6133. if ( pLocalTFPlayer && pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) && pLocalTFPlayer != this )
  6134. {
  6135. bLimitedInvis = false;
  6136. }
  6137. #endif // STAGING_ONLY
  6138. // If this is a teammate of the local player or viewer is observer,
  6139. // dont go above a certain max invis
  6140. if ( bLimitedInvis )
  6141. {
  6142. float flMax = tf_teammate_max_invis.GetFloat();
  6143. if ( flPercentInvisible > flMax )
  6144. {
  6145. flPercentInvisible = flMax;
  6146. }
  6147. }
  6148. else
  6149. {
  6150. // If this player just killed me, show them slightly
  6151. // less than full invis in the deathcam and freezecam
  6152. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  6153. if ( pLocalPlayer )
  6154. {
  6155. int iObserverMode = pLocalPlayer->GetObserverMode();
  6156. if ( ( iObserverMode == OBS_MODE_FREEZECAM || iObserverMode == OBS_MODE_DEATHCAM ) &&
  6157. pLocalPlayer->GetObserverTarget() == this )
  6158. {
  6159. float flMax = tf_teammate_max_invis.GetFloat();
  6160. if ( flPercentInvisible > flMax )
  6161. {
  6162. flPercentInvisible = flMax;
  6163. }
  6164. }
  6165. }
  6166. }
  6167. return flPercentInvisible;
  6168. }
  6169. //-----------------------------------------------------------------------------
  6170. // Purpose:
  6171. //-----------------------------------------------------------------------------
  6172. void C_TFPlayer::SetBodygroupsDirty( void )
  6173. {
  6174. m_bBodygroupsDirty = true;
  6175. CTFViewModel *pVM = dynamic_cast<CTFViewModel *>( GetViewModel() );
  6176. if ( pVM )
  6177. {
  6178. pVM->m_bBodygroupsDirty = true;
  6179. }
  6180. }
  6181. //-----------------------------------------------------------------------------
  6182. // Purpose:
  6183. //-----------------------------------------------------------------------------
  6184. void C_TFPlayer::RecalcBodygroupsIfDirty( void )
  6185. {
  6186. if ( m_bBodygroupsDirty )
  6187. {
  6188. m_Shared.RecalculatePlayerBodygroups();
  6189. m_bBodygroupsDirty = false;
  6190. }
  6191. }
  6192. //-----------------------------------------------------------------------------
  6193. // Purpose:
  6194. //-----------------------------------------------------------------------------
  6195. int C_TFPlayer::DrawModel( int flags )
  6196. {
  6197. // If we're a dead player with a fresh ragdoll, don't draw
  6198. if ( m_nRenderFX == kRenderFxRagdoll )
  6199. return 0;
  6200. RecalcBodygroupsIfDirty();
  6201. // Don't draw the model at all if we're fully invisible
  6202. if ( GetEffectiveInvisibilityLevel() >= 1.0f )
  6203. {
  6204. if ( m_hHalloweenBombHat && ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 ) && !m_hHalloweenBombHat->IsEffectActive( EF_NODRAW ) )
  6205. {
  6206. m_hHalloweenBombHat->SetEffects( EF_NODRAW );
  6207. }
  6208. return 0;
  6209. }
  6210. else
  6211. {
  6212. if ( m_hHalloweenBombHat && ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 ) && m_hHalloweenBombHat->IsEffectActive( EF_NODRAW ) )
  6213. {
  6214. m_hHalloweenBombHat->RemoveEffects( EF_NODRAW );
  6215. }
  6216. }
  6217. CMatRenderContextPtr pRenderContext( materials );
  6218. bool bDoEffect = false;
  6219. float flAmountToChop = 0.0;
  6220. if ( m_Shared.InCond( TF_COND_DISGUISING ) )
  6221. {
  6222. flAmountToChop = ( gpGlobals->curtime - m_flDisguiseEffectStartTime ) *
  6223. ( 1.0 / TF_TIME_TO_DISGUISE );
  6224. }
  6225. else
  6226. {
  6227. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  6228. {
  6229. float flETime = gpGlobals->curtime - m_flDisguiseEffectStartTime;
  6230. if ( ( flETime > 0.0 ) && ( flETime < TF_TIME_TO_SHOW_DISGUISED_FINISHED_EFFECT ) )
  6231. {
  6232. flAmountToChop = 1.0 - ( flETime * ( 1.0/TF_TIME_TO_SHOW_DISGUISED_FINISHED_EFFECT ) );
  6233. }
  6234. }
  6235. }
  6236. bDoEffect = ( flAmountToChop > 0.0 ) && ( ! IsLocalPlayer() );
  6237. #if ( SHOW_DISGUISE_EFFECT == 0 )
  6238. bDoEffect = false;
  6239. #endif
  6240. bDoEffect = false;
  6241. if ( bDoEffect )
  6242. {
  6243. Vector vMyOrigin = GetAbsOrigin();
  6244. BoxDeformation_t mybox;
  6245. mybox.m_ClampMins = vMyOrigin - Vector(100,100,100);
  6246. mybox.m_ClampMaxes = vMyOrigin + Vector(500,500,72 * ( 1 - flAmountToChop ) );
  6247. pRenderContext->PushDeformation( &mybox );
  6248. }
  6249. int ret = BaseClass::DrawModel( flags );
  6250. if ( bDoEffect )
  6251. pRenderContext->PopDeformation();
  6252. return ret;
  6253. }
  6254. //-----------------------------------------------------------------------------
  6255. // Purpose:
  6256. //-----------------------------------------------------------------------------
  6257. void C_TFPlayer::ProcessMuzzleFlashEvent()
  6258. {
  6259. CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  6260. // Reenable when the weapons have muzzle flash attachments in the right spot.
  6261. bool bInToolRecordingMode = ToolsEnabled() && clienttools->IsInRecordingMode();
  6262. if ( this == pLocalPlayer && !bInToolRecordingMode )
  6263. return; // don't show own world muzzle flash for localplayer
  6264. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  6265. {
  6266. // also don't show in 1st person spec mode
  6267. if ( pLocalPlayer->GetObserverTarget() == this )
  6268. return;
  6269. }
  6270. C_TFWeaponBase *pWeapon = m_Shared.GetActiveTFWeapon();
  6271. if ( !pWeapon )
  6272. return;
  6273. pWeapon->ProcessMuzzleFlashEvent();
  6274. }
  6275. //-----------------------------------------------------------------------------
  6276. // Purpose:
  6277. //-----------------------------------------------------------------------------
  6278. int C_TFPlayer::GetIDTarget() const
  6279. {
  6280. return m_iIDEntIndex;
  6281. }
  6282. //-----------------------------------------------------------------------------
  6283. // Purpose:
  6284. //-----------------------------------------------------------------------------
  6285. void C_TFPlayer::SetForcedIDTarget( int iTarget )
  6286. {
  6287. m_iForcedIDTarget = iTarget;
  6288. }
  6289. //-----------------------------------------------------------------------------
  6290. // Purpose: Update this client's targetid entity
  6291. //-----------------------------------------------------------------------------
  6292. void C_TFPlayer::UpdateIDTarget()
  6293. {
  6294. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  6295. if ( !pLocalPlayer || !IsLocalPlayer() )
  6296. return;
  6297. // don't show IDs if mp_fadetoblack is on
  6298. if ( GetTeamNumber() > TEAM_SPECTATOR && mp_fadetoblack.GetBool() && !IsAlive() )
  6299. {
  6300. m_iIDEntIndex = 0;
  6301. return;
  6302. }
  6303. if ( m_iForcedIDTarget )
  6304. {
  6305. m_iIDEntIndex = m_iForcedIDTarget;
  6306. return;
  6307. }
  6308. // If we're in deathcam, ID our killer
  6309. if ( (GetObserverMode() == OBS_MODE_DEATHCAM || GetObserverMode() == OBS_MODE_CHASE) && GetObserverTarget() && GetObserverTarget() != GetLocalTFPlayer() )
  6310. {
  6311. m_iIDEntIndex = GetObserverTarget()->entindex();
  6312. return;
  6313. }
  6314. // Clear old target and find a new one
  6315. m_iIDEntIndex = 0;
  6316. trace_t tr;
  6317. Vector vecStart, vecEnd;
  6318. VectorMA( MainViewOrigin(), MAX_TRACE_LENGTH, MainViewForward(), vecEnd );
  6319. VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
  6320. // If we're in observer mode, ignore our observer target. Otherwise, ignore ourselves.
  6321. if ( IsObserver() )
  6322. {
  6323. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, GetObserverTarget(), COLLISION_GROUP_NONE, &tr );
  6324. }
  6325. else
  6326. {
  6327. // Add DEBRIS when a medic has revive (for tracing against revive markers)
  6328. int iReviveMedic = 0;
  6329. CALL_ATTRIB_HOOK_INT( iReviveMedic, revive );
  6330. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() && pLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) )
  6331. {
  6332. iReviveMedic = 1;
  6333. }
  6334. int nMask = MASK_SOLID | CONTENTS_DEBRIS;
  6335. UTIL_TraceLine( vecStart, vecEnd, nMask, this, COLLISION_GROUP_NONE, &tr );
  6336. }
  6337. bool bIsEnemyPlayer = false;
  6338. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  6339. {
  6340. // It's okay to start solid against enemies because we sometimes press right against them
  6341. bIsEnemyPlayer = GetTeamNumber() != tr.m_pEnt->GetTeamNumber();
  6342. }
  6343. if ( ( !tr.startsolid || bIsEnemyPlayer ) && tr.DidHitNonWorldEntity() )
  6344. {
  6345. C_BaseEntity *pEntity = tr.m_pEnt;
  6346. if ( pEntity && ( pEntity != this ) )
  6347. {
  6348. m_iIDEntIndex = pEntity->entindex();
  6349. }
  6350. }
  6351. }
  6352. //-----------------------------------------------------------------------------
  6353. // Purpose: Display appropriate hints for the target we're looking at
  6354. //-----------------------------------------------------------------------------
  6355. void C_TFPlayer::DisplaysHintsForTarget( C_BaseEntity *pTarget )
  6356. {
  6357. // If the entity provides hints, ask them if they have one for this player
  6358. ITargetIDProvidesHint *pHintInterface = dynamic_cast<ITargetIDProvidesHint*>(pTarget);
  6359. if ( pHintInterface )
  6360. {
  6361. pHintInterface->DisplayHintTo( this );
  6362. }
  6363. }
  6364. //-----------------------------------------------------------------------------
  6365. // Purpose:
  6366. //-----------------------------------------------------------------------------
  6367. int C_TFPlayer::GetRenderTeamNumber( void )
  6368. {
  6369. return m_nSkin;
  6370. }
  6371. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  6372. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  6373. //-----------------------------------------------------------------------------
  6374. // Purpose:
  6375. //-----------------------------------------------------------------------------
  6376. Vector C_TFPlayer::GetDeathViewPosition()
  6377. {
  6378. Vector origin = EyePosition();
  6379. C_TFRagdoll *pRagdoll = static_cast<C_TFRagdoll*>( m_hRagdoll.Get() );
  6380. if ( pRagdoll )
  6381. {
  6382. if ( pRagdoll->IsDeathAnim() )
  6383. {
  6384. origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z*4;
  6385. }
  6386. else
  6387. {
  6388. IRagdoll *pIRagdoll = GetRepresentativeRagdoll();
  6389. if ( pIRagdoll )
  6390. {
  6391. origin = pIRagdoll->GetRagdollOrigin();
  6392. origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
  6393. }
  6394. }
  6395. }
  6396. return origin;
  6397. }
  6398. //-----------------------------------------------------------------------------
  6399. // Purpose:
  6400. //-----------------------------------------------------------------------------
  6401. void C_TFPlayer::CalcDeathCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  6402. {
  6403. CBaseEntity * killer = GetObserverTarget();
  6404. C_BaseAnimating *pKillerAnimating = killer ? killer->GetBaseAnimating() : NULL;
  6405. // Swing to face our killer within half the death anim time
  6406. float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / (TF_DEATH_ANIMATION_TIME * 0.5);
  6407. interpolation = clamp( interpolation, 0.0f, 1.0f );
  6408. interpolation = SimpleSpline( interpolation );
  6409. float flMinChaseDistance = CHASE_CAM_DISTANCE_MIN;
  6410. float flMaxChaseDistance = CHASE_CAM_DISTANCE_MAX;
  6411. if ( pKillerAnimating )
  6412. {
  6413. float flScaleSquared = pKillerAnimating->GetModelScale() * pKillerAnimating->GetModelScale();
  6414. flMinChaseDistance *= flScaleSquared;
  6415. flMaxChaseDistance *= flScaleSquared;
  6416. }
  6417. m_flObserverChaseDistance += gpGlobals->frametime * 48.0f;
  6418. m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, flMinChaseDistance, flMaxChaseDistance );
  6419. QAngle aForward = eyeAngles = EyeAngles();
  6420. Vector origin = GetDeathViewPosition();
  6421. if ( m_hHeadGib )
  6422. {
  6423. // View from our decapitated head.
  6424. IPhysicsObject *pPhysicsObject = m_hHeadGib->VPhysicsGetObject();
  6425. if( pPhysicsObject )
  6426. {
  6427. Vector vecMassCenter = pPhysicsObject->GetMassCenterLocalSpace();
  6428. Vector vecWorld;
  6429. m_hHeadGib->CollisionProp()->CollisionToWorldSpace( vecMassCenter, &vecWorld );
  6430. m_hHeadGib->AddEffects( EF_NODRAW );
  6431. eyeOrigin = vecWorld + Vector(0,0,6);
  6432. QAngle aHead = m_hHeadGib->GetAbsAngles();
  6433. Vector vBody;
  6434. if ( m_hRagdoll )
  6435. {
  6436. // Turn to face our ragdoll.
  6437. vBody = m_hRagdoll->GetAbsOrigin() - eyeOrigin;
  6438. }
  6439. else
  6440. {
  6441. vBody = m_hHeadGib->GetAbsOrigin();
  6442. }
  6443. QAngle aBody; VectorAngles( vBody, aBody );
  6444. InterpolateAngles( aHead, aBody, eyeAngles, interpolation );
  6445. return;
  6446. }
  6447. }
  6448. if ( killer && (killer != this) )
  6449. {
  6450. Vector vKiller = killer->EyePosition() - origin;
  6451. QAngle aKiller;
  6452. VectorAngles( vKiller, aKiller );
  6453. InterpolateAngles( aForward, aKiller, eyeAngles, interpolation );
  6454. }
  6455. Vector vForward;
  6456. AngleVectors( eyeAngles, &vForward );
  6457. VectorNormalize( vForward );
  6458. VectorMA( origin, -m_flObserverChaseDistance, vForward, eyeOrigin );
  6459. trace_t trace; // clip against world
  6460. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  6461. UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
  6462. C_BaseEntity::PopEnableAbsRecomputations();
  6463. if (trace.fraction < 1.0)
  6464. {
  6465. eyeOrigin = trace.endpos;
  6466. m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
  6467. }
  6468. fov = GetFOV();
  6469. }
  6470. //-----------------------------------------------------------------------------
  6471. // Purpose: Do nothing multiplayer_animstate takes care of animation.
  6472. // Input : playerAnim -
  6473. //-----------------------------------------------------------------------------
  6474. void C_TFPlayer::SetAnimation( PLAYER_ANIM playerAnim )
  6475. {
  6476. return;
  6477. }
  6478. float C_TFPlayer::GetMinFOV() const
  6479. {
  6480. // Min FOV for Sniper Rifle
  6481. return 20;
  6482. }
  6483. //-----------------------------------------------------------------------------
  6484. // Purpose:
  6485. //-----------------------------------------------------------------------------
  6486. const QAngle& C_TFPlayer::EyeAngles()
  6487. {
  6488. if ( IsLocalPlayer() && g_nKillCamMode == OBS_MODE_NONE )
  6489. {
  6490. return BaseClass::EyeAngles();
  6491. }
  6492. else
  6493. {
  6494. return m_angEyeAngles;
  6495. }
  6496. }
  6497. //-----------------------------------------------------------------------------
  6498. // Purpose:
  6499. // Input : &color -
  6500. //-----------------------------------------------------------------------------
  6501. void C_TFPlayer::GetTeamColor( Color &color )
  6502. {
  6503. color[3] = 255;
  6504. if ( GetTeamNumber() == TF_TEAM_RED )
  6505. {
  6506. color[0] = 159;
  6507. color[1] = 55;
  6508. color[2] = 34;
  6509. }
  6510. else if ( GetTeamNumber() == TF_TEAM_BLUE )
  6511. {
  6512. color[0] = 76;
  6513. color[1] = 109;
  6514. color[2] = 129;
  6515. }
  6516. else
  6517. {
  6518. color[0] = 255;
  6519. color[1] = 255;
  6520. color[2] = 255;
  6521. }
  6522. }
  6523. //-----------------------------------------------------------------------------
  6524. // Purpose:
  6525. // Input : bCopyEntity -
  6526. // Output : C_BaseAnimating *
  6527. //-----------------------------------------------------------------------------
  6528. C_BaseAnimating *C_TFPlayer::BecomeRagdollOnClient()
  6529. {
  6530. // Let the C_TFRagdoll take care of this.
  6531. return NULL;
  6532. }
  6533. //-----------------------------------------------------------------------------
  6534. // Purpose:
  6535. // Input : -
  6536. // Output : IRagdoll*
  6537. //-----------------------------------------------------------------------------
  6538. IRagdoll* C_TFPlayer::GetRepresentativeRagdoll() const
  6539. {
  6540. if ( m_hRagdoll.Get() )
  6541. {
  6542. C_TFRagdoll *pRagdoll = static_cast<C_TFRagdoll*>( m_hRagdoll.Get() );
  6543. if ( !pRagdoll )
  6544. return NULL;
  6545. return pRagdoll->GetIRagdoll();
  6546. }
  6547. else
  6548. {
  6549. return NULL;
  6550. }
  6551. }
  6552. //-----------------------------------------------------------------------------
  6553. // Purpose:
  6554. //-----------------------------------------------------------------------------
  6555. void C_TFPlayer::InitPlayerGibs( void )
  6556. {
  6557. // Clear out the gib list and create a new one.
  6558. m_aGibs.Purge();
  6559. m_aNormalGibs.PurgeAndDeleteElements();
  6560. m_aSillyGibs.Purge();
  6561. int nModelIndex = GetPlayerClass()->HasCustomModel() ? modelinfo->GetModelIndex( GetPlayerClass()->GetModelName() ) : GetModelIndex();
  6562. BuildGibList( m_aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
  6563. if ( TFGameRules() && TFGameRules()->IsBirthday() )
  6564. {
  6565. for ( int i = 0; i < m_aGibs.Count(); i++ )
  6566. {
  6567. if ( RandomFloat(0,1) < 0.75 )
  6568. {
  6569. V_strcpy_safe( m_aGibs[i].modelName, g_pszBDayGibs[ RandomInt(0,ARRAYSIZE(g_pszBDayGibs)-1) ] );
  6570. }
  6571. }
  6572. }
  6573. // Copy the normal gibs list to be saved for later when swapping with Pyro Vision
  6574. FOR_EACH_VEC ( m_aGibs, i )
  6575. {
  6576. char *cloneStr = new char [ 512 ];
  6577. Q_strncpy( cloneStr, m_aGibs[i].modelName, 512 );
  6578. m_aNormalGibs.AddToTail( cloneStr );
  6579. // Create a list of silly gibs
  6580. int iRandIndex = RandomInt(4,ARRAYSIZE(g_pszBDayGibs)-1);
  6581. m_aSillyGibs.AddToTail( iRandIndex );
  6582. }
  6583. }
  6584. //-----------------------------------------------------------------------------
  6585. // Purpose : Checks vision flags and ensures the proper gib models are loaded for vision mode
  6586. //-----------------------------------------------------------------------------
  6587. void C_TFPlayer::CheckAndUpdateGibType( void )
  6588. {
  6589. // check the first gib, if it's different copy them all over
  6590. if ( IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO ) || ( TFGameRules() && TFGameRules()->UseSillyGibs() ) )
  6591. {
  6592. if ( Q_strcmp( m_aGibs[0].modelName, g_pszBDayGibs[ m_aSillyGibs[0] ]) != 0 )
  6593. {
  6594. FOR_EACH_VEC( m_aGibs, i )
  6595. {
  6596. V_strcpy_safe( m_aGibs[i].modelName, g_pszBDayGibs[ m_aSillyGibs[i] ] );
  6597. }
  6598. }
  6599. }
  6600. else
  6601. {
  6602. if ( Q_strcmp( m_aGibs[0].modelName, m_aNormalGibs[0]) != 0 )
  6603. {
  6604. FOR_EACH_VEC( m_aGibs, i )
  6605. {
  6606. V_strcpy_safe( m_aGibs[i].modelName, m_aNormalGibs[i] );
  6607. }
  6608. }
  6609. }
  6610. }
  6611. //-----------------------------------------------------------------------------
  6612. // Purpose:
  6613. // Input : &vecOrigin -
  6614. // &vecVelocity -
  6615. // &vecImpactVelocity -
  6616. //-----------------------------------------------------------------------------
  6617. void C_TFPlayer::CreatePlayerGibs( const Vector &vecOrigin, const Vector &vecVelocity, float flImpactScale, bool bBurning, bool bWearableGibs, bool bOnlyHead, bool bDisguiseGibs )
  6618. {
  6619. // Make sure we have Gibs to create.
  6620. if ( m_aGibs.Count() == 0 )
  6621. return;
  6622. AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
  6623. Vector vecBreakVelocity = vecVelocity;
  6624. vecBreakVelocity.z += tf_playergib_forceup.GetFloat();
  6625. VectorNormalize( vecBreakVelocity );
  6626. vecBreakVelocity *= tf_playergib_force.GetFloat();
  6627. // Cap the impulse.
  6628. float flSpeed = vecBreakVelocity.Length();
  6629. if ( flSpeed > tf_playergib_maxspeed.GetFloat() )
  6630. {
  6631. VectorScale( vecBreakVelocity, tf_playergib_maxspeed.GetFloat() / flSpeed, vecBreakVelocity );
  6632. }
  6633. breakablepropparams_t breakParams( vecOrigin, GetRenderAngles(), vecBreakVelocity, angularImpulse );
  6634. breakParams.impactEnergyScale = 1.0f;//
  6635. // Break up the player.
  6636. if ( !bWearableGibs )
  6637. {
  6638. // Gib the player's body.
  6639. m_hHeadGib = NULL;
  6640. m_hSpawnedGibs.Purge();
  6641. bool bHasCustomModel = GetPlayerClass()->HasCustomModel();
  6642. int nModelIndex = bHasCustomModel ? modelinfo->GetModelIndex( GetPlayerClass()->GetModelName() ) : GetModelIndex();
  6643. if ( bOnlyHead )
  6644. {
  6645. if ( UTIL_IsLowViolence() )
  6646. {
  6647. // No bloody gibs with pyro-vision goggles
  6648. return;
  6649. }
  6650. // Create only a head gib.
  6651. CUtlVector<breakmodel_t> headGib;
  6652. int nClassIndex = GetPlayerClass()->GetClassIndex();
  6653. if ( bHasCustomModel )
  6654. {
  6655. for ( int i=0; i<m_aGibs.Count(); ++i )
  6656. {
  6657. if ( Q_strcmp( m_aGibs[i].modelName, g_pszBotHeadGibs[nClassIndex] ) == 0 )
  6658. {
  6659. headGib.AddToHead( m_aGibs[i] );
  6660. }
  6661. }
  6662. }
  6663. else
  6664. {
  6665. for ( int i=0; i<m_aGibs.Count(); ++i )
  6666. {
  6667. if ( Q_strcmp( m_aGibs[i].modelName, g_pszHeadGibs[nClassIndex] ) == 0 )
  6668. {
  6669. headGib.AddToHead( m_aGibs[i] );
  6670. }
  6671. }
  6672. }
  6673. m_hFirstGib = CreateGibsFromList( headGib, nModelIndex, NULL, breakParams, this, -1 , false, true, &m_hSpawnedGibs, bBurning );
  6674. m_hHeadGib = m_hFirstGib;
  6675. if ( m_hFirstGib )
  6676. {
  6677. IPhysicsObject *pPhysicsObject = m_hFirstGib->VPhysicsGetObject();
  6678. if( pPhysicsObject )
  6679. {
  6680. // Give the head some rotational damping so it doesn't roll so much (for the player's view).
  6681. float damping, rotdamping;
  6682. pPhysicsObject->GetDamping( &damping, &rotdamping );
  6683. rotdamping *= 6.f;
  6684. pPhysicsObject->SetDamping( &damping, &rotdamping );
  6685. }
  6686. }
  6687. }
  6688. else
  6689. {
  6690. CheckAndUpdateGibType();
  6691. m_hFirstGib = CreateGibsFromList( m_aGibs, nModelIndex, NULL, breakParams, this, -1 , false, true, &m_hSpawnedGibs, bBurning );
  6692. }
  6693. DropPartyHat( breakParams, vecBreakVelocity );
  6694. }
  6695. else
  6696. {
  6697. // Gib up the player's clothing.
  6698. for ( int i=0; i<GetNumWearables(); ++i )
  6699. {
  6700. C_TFWearable *pItem = dynamic_cast<C_TFWearable*> (GetWearable(i));
  6701. if ( !pItem )
  6702. continue;
  6703. // Don't try to drop items which haven't loaded yet
  6704. if ( !pItem->GetModel() || !pItem->GetModelPtr() )
  6705. continue;
  6706. // Only drop wearable gibs for wearables that are flagged as droppable.
  6707. if ( pItem->GetDropType() != ITEM_DROP_TYPE_DROP )
  6708. continue;
  6709. if ( pItem->IsDisguiseWearable() && !bDisguiseGibs )
  6710. continue;
  6711. if ( !pItem->IsDisguiseWearable() && bDisguiseGibs )
  6712. continue;
  6713. DropWearable( pItem, breakParams );
  6714. }
  6715. }
  6716. }
  6717. //-----------------------------------------------------------------------------
  6718. // Purpose:
  6719. //-----------------------------------------------------------------------------
  6720. void C_TFPlayer::DropWearable( C_TFWearable *pItem, const breakablepropparams_t &params )
  6721. {
  6722. // Get the position from the rootbone of the wearable entity itself
  6723. Vector position;
  6724. matrix3x4_t rootBone;
  6725. if ( !pItem->IsDynamicModelLoading() && pItem->GetRootBone( rootBone ) )
  6726. {
  6727. MatrixPosition( rootBone, position );
  6728. }
  6729. else
  6730. {
  6731. position = pItem->GetAbsOrigin();
  6732. }
  6733. // Don't spawn wearables out of bounds
  6734. if ( !IsEntityPositionReasonable( position ) )
  6735. {
  6736. return;
  6737. }
  6738. const model_t *pModel = modelinfo->GetModel( pItem->GetModelIndex() );
  6739. // Check that the entity wouldn't be spawned in a wall
  6740. Vector mins, maxs;
  6741. modelinfo->GetModelRenderBounds( pModel, mins, maxs );
  6742. trace_t trace;
  6743. CTraceFilterNoNPCsOrPlayer filter( this, COLLISION_GROUP_NONE );
  6744. UTIL_TraceHull( position, position, mins, maxs, MASK_SOLID, &filter, &trace );
  6745. if ( trace.startsolid )
  6746. {
  6747. return;
  6748. }
  6749. // Velocity
  6750. Vector objectVelocity = params.velocity;
  6751. float flScale = VectorNormalize( objectVelocity );
  6752. objectVelocity.x += RandomFloat( -1.f, 1.0f );
  6753. objectVelocity.y += RandomFloat( -1.0f, 1.0f );
  6754. objectVelocity.z += RandomFloat( 0.0f, 1.0f );
  6755. VectorNormalize( objectVelocity );
  6756. objectVelocity *= flScale;
  6757. // Now create the TF2 wearable gib
  6758. C_EconWearableGib *pEntity = new C_EconWearableGib();
  6759. if ( !pEntity )
  6760. return;
  6761. const char *pszModelName = modelinfo->GetModelName( pModel );
  6762. pEntity->SetModelName( AllocPooledString( pszModelName ) );
  6763. pEntity->SetAbsOrigin( position );
  6764. pEntity->SetAbsAngles( pItem->GetAbsAngles() );
  6765. pEntity->SetOwnerEntity( this );
  6766. pEntity->ChangeTeam( GetTeamNumber() ); // our gibs will match our team; this will probably not be used for anything besides team coloring
  6767. // Copy the script created item data over
  6768. pEntity->GetAttributeContainer()->SetItem( pItem->GetAttributeContainer()->GetItem() );
  6769. if ( !pEntity->Initialize( false ) )
  6770. {
  6771. pEntity->Release();
  6772. return;
  6773. }
  6774. pEntity->m_nSkin = m_nSkin;
  6775. pEntity->StartFadeOut( 15.0f );
  6776. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  6777. if ( !pPhysicsObject )
  6778. {
  6779. pEntity->Release();
  6780. return;
  6781. }
  6782. // randomize velocity by 5%
  6783. float rndf = RandomFloat( -0.025, 0.025 );
  6784. Vector rndVel = objectVelocity + rndf*objectVelocity;
  6785. pPhysicsObject->AddVelocity( &rndVel, &params.angularVelocity );
  6786. }
  6787. //-----------------------------------------------------------------------------
  6788. // Purpose:
  6789. //-----------------------------------------------------------------------------
  6790. void C_TFPlayer::DropPartyHat( breakablepropparams_t &breakParams, Vector &vecBreakVelocity )
  6791. {
  6792. // Turning off party hats because we've moving to real hats
  6793. return;
  6794. /*
  6795. if ( m_hPartyHat )
  6796. {
  6797. breakmodel_t breakModel;
  6798. Q_strncpy( breakModel.modelName, BDAY_HAT_MODEL, sizeof(breakModel.modelName) );
  6799. breakModel.health = 1;
  6800. breakModel.fadeTime = RandomFloat(5,10);
  6801. breakModel.fadeMinDist = 0.0f;
  6802. breakModel.fadeMaxDist = 0.0f;
  6803. breakModel.burstScale = breakParams.defBurstScale;
  6804. breakModel.collisionGroup = COLLISION_GROUP_DEBRIS;
  6805. breakModel.isRagdoll = false;
  6806. breakModel.isMotionDisabled = false;
  6807. breakModel.placementName[0] = 0;
  6808. breakModel.placementIsBone = false;
  6809. breakModel.offset = GetAbsOrigin() - m_hPartyHat->GetAbsOrigin();
  6810. BreakModelCreateSingle( this, &breakModel, m_hPartyHat->GetAbsOrigin(), m_hPartyHat->GetAbsAngles(), vecBreakVelocity, breakParams.angularVelocity, m_hPartyHat->m_nSkin, breakParams );
  6811. m_hPartyHat->Release();
  6812. }
  6813. */
  6814. }
  6815. //-----------------------------------------------------------------------------
  6816. // Purpose: How many buildables does this player own
  6817. //-----------------------------------------------------------------------------
  6818. int C_TFPlayer::GetObjectCount( void )
  6819. {
  6820. return m_aObjects.Count();
  6821. }
  6822. //-----------------------------------------------------------------------------
  6823. // Purpose: Get a specific buildable that this player owns
  6824. //-----------------------------------------------------------------------------
  6825. C_BaseObject *C_TFPlayer::GetObject( int index_ )
  6826. {
  6827. return m_aObjects[index_].Get();
  6828. }
  6829. //-----------------------------------------------------------------------------
  6830. // Purpose: Get a specific buildable that this player owns
  6831. //-----------------------------------------------------------------------------
  6832. C_BaseObject *C_TFPlayer::GetObjectOfType( int iObjectType, int iObjectMode ) const
  6833. {
  6834. int iCount = m_aObjects.Count();
  6835. for ( int i=0;i<iCount;i++ )
  6836. {
  6837. C_BaseObject *pObj = m_aObjects[i].Get();
  6838. if ( !pObj )
  6839. continue;
  6840. if ( pObj->IsDormant() || pObj->IsMarkedForDeletion() )
  6841. continue;
  6842. if ( pObj->GetType() != iObjectType )
  6843. continue;
  6844. if ( pObj->GetObjectMode() != iObjectMode )
  6845. continue;
  6846. return pObj;
  6847. }
  6848. return NULL;
  6849. }
  6850. //-----------------------------------------------------------------------------
  6851. // Purpose:
  6852. // Input : collisionGroup -
  6853. // Output : Returns true on success, false on failure.
  6854. //-----------------------------------------------------------------------------
  6855. bool C_TFPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const
  6856. {
  6857. if ( ( ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) && tf_avoidteammates.GetBool() ) ||
  6858. collisionGroup == TFCOLLISION_GROUP_ROCKETS )
  6859. {
  6860. switch( GetTeamNumber() )
  6861. {
  6862. case TF_TEAM_RED:
  6863. if ( !( contentsMask & CONTENTS_REDTEAM ) )
  6864. return false;
  6865. break;
  6866. case TF_TEAM_BLUE:
  6867. if ( !( contentsMask & CONTENTS_BLUETEAM ) )
  6868. return false;
  6869. break;
  6870. }
  6871. }
  6872. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  6873. }
  6874. float C_TFPlayer::GetPercentInvisible( void )
  6875. {
  6876. return m_Shared.GetPercentInvisible();
  6877. }
  6878. void C_TFPlayer::AdjustSkinIndexForZombie( int iClassIndex, int &iSkinIndex )
  6879. {
  6880. // We only know how to adjust the 4 standard base skins (red/blue * normal/invuln)
  6881. Assert( iSkinIndex >= 0 && iSkinIndex < 4 );
  6882. if ( iClassIndex == TF_CLASS_SPY )
  6883. {
  6884. // Spy has a bunch of extra skins used to adjust the mask
  6885. iSkinIndex += 22;
  6886. }
  6887. else
  6888. {
  6889. // 4: red zombie
  6890. // 5: blue zombie
  6891. // 6: red zombie invuln
  6892. // 7: blue zombie invuln
  6893. iSkinIndex += 4;
  6894. }
  6895. }
  6896. bool C_TFPlayer::BRenderAsZombie( bool bWeaponsCheck /*= false */ )
  6897. {
  6898. // Only if the local player is optining in.
  6899. if ( !IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_HALLOWEEN, bWeaponsCheck ) )
  6900. return false;
  6901. // Should we render as somebody else?
  6902. bool bRenderDisguised = false;
  6903. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  6904. {
  6905. // When disguised, our teammates will see us with a mask.
  6906. // Don't show us as a zombie in that state, because the zombie parts
  6907. // (like every other cosmetic) disappear.
  6908. if ( !IsEnemyPlayer() )
  6909. return false;
  6910. // Ditto when we are disguised as an enemy spy. We always use the mask
  6911. // in that case and hide cosmetics
  6912. if ( m_Shared.GetDisguiseClass() == TF_CLASS_SPY )
  6913. return false;
  6914. bRenderDisguised = true;
  6915. }
  6916. int iPlayerSkinOverride = bRenderDisguised ? m_Shared.GetDisguisedSkinOverride() : GetSkinOverride();
  6917. return iPlayerSkinOverride == 1;
  6918. }
  6919. //-----------------------------------------------------------------------------
  6920. // Purpose:
  6921. //-----------------------------------------------------------------------------
  6922. int C_TFPlayer::GetSkin()
  6923. {
  6924. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  6925. if ( !pLocalPlayer )
  6926. return 0;
  6927. // Allow server plugins to override
  6928. if ( m_bForcedSkin )
  6929. return m_nForcedSkin;
  6930. int iVisibleTeam = GetTeamNumber();
  6931. // if this player is disguised and on the other team, use disguise team
  6932. if ( m_Shared.InCond( TF_COND_DISGUISED_AS_DISPENSER ) && IsEnemyPlayer() && ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
  6933. {
  6934. iVisibleTeam = ( iVisibleTeam == TF_TEAM_RED ? TF_TEAM_BLUE : TF_TEAM_RED );
  6935. }
  6936. else if ( m_Shared.InCond( TF_COND_DISGUISED ) && IsEnemyPlayer() )
  6937. {
  6938. iVisibleTeam = m_Shared.GetDisguiseTeam();
  6939. }
  6940. int nSkin;
  6941. switch( iVisibleTeam )
  6942. {
  6943. case TF_TEAM_RED:
  6944. nSkin = 0;
  6945. break;
  6946. case TF_TEAM_BLUE:
  6947. nSkin = 1;
  6948. break;
  6949. default:
  6950. nSkin = 0;
  6951. break;
  6952. }
  6953. // Assume we'll switch skins to show the spy mask
  6954. bool bCheckSpyMask = true;
  6955. // 3 and 4 are invulnerable
  6956. if ( m_Shared.IsInvulnerable() &&
  6957. ( !m_Shared.InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) || gpGlobals->curtime < GetLastDamageTime() + 2.0f ) )
  6958. {
  6959. nSkin += 2;
  6960. bCheckSpyMask = false;
  6961. }
  6962. // Check for any special player skin override behaviour.
  6963. if ( BRenderAsZombie() )
  6964. {
  6965. int iClass = GetPlayerClass()->GetClassIndex();
  6966. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  6967. {
  6968. Assert( IsEnemyPlayer() );
  6969. iClass = m_Shared.GetDisguiseClass();
  6970. }
  6971. AdjustSkinIndexForZombie( iClass, nSkin );
  6972. bCheckSpyMask = false; // no spy masks for zombies
  6973. }
  6974. if ( bCheckSpyMask && m_Shared.InCond( TF_COND_DISGUISED ) )
  6975. {
  6976. if ( !IsEnemyPlayer() )
  6977. {
  6978. nSkin += 4 + ( ( m_Shared.GetDisguiseClass() - TF_FIRST_NORMAL_CLASS ) * 2 );
  6979. }
  6980. else if ( m_Shared.GetDisguiseClass() == TF_CLASS_SPY )
  6981. {
  6982. nSkin += 4 + ( ( m_Shared.GetDisguiseMask() - TF_FIRST_NORMAL_CLASS ) * 2 );
  6983. }
  6984. }
  6985. // Check for testing override
  6986. #ifdef STAGING_ONLY
  6987. if ( ( this == pLocalPlayer ) && ( test_local_player_skin_override.GetInt() >= 0 ) )
  6988. nSkin = test_local_player_skin_override.GetInt();
  6989. #endif
  6990. return nSkin;
  6991. }
  6992. //-----------------------------------------------------------------------------
  6993. // Purpose:
  6994. // Input : iClass -
  6995. // Output : Returns true on success, false on failure.
  6996. //-----------------------------------------------------------------------------
  6997. bool C_TFPlayer::IsPlayerClass( int iClass ) const
  6998. {
  6999. const C_TFPlayerClass *pClass = GetPlayerClass();
  7000. if ( !pClass )
  7001. return false;
  7002. return ( pClass->GetClassIndex() == iClass );
  7003. }
  7004. //-----------------------------------------------------------------------------
  7005. // Purpose: Don't take damage decals while stealthed
  7006. //-----------------------------------------------------------------------------
  7007. void C_TFPlayer::AddDecal( const Vector& rayStart, const Vector& rayEnd,
  7008. const Vector& decalCenter, int hitbox, int decalIndex, bool doTrace, trace_t& tr, int maxLODToDecal )
  7009. {
  7010. if ( m_Shared.IsStealthed() )
  7011. {
  7012. return;
  7013. }
  7014. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  7015. {
  7016. return;
  7017. }
  7018. if ( m_Shared.IsInvulnerable() )
  7019. {
  7020. Vector vecDir = rayEnd - rayStart;
  7021. VectorNormalize(vecDir);
  7022. g_pEffects->Ricochet( rayEnd - (vecDir * 8), -vecDir );
  7023. return;
  7024. }
  7025. #ifdef TF_RAID_MODE
  7026. // no decals for the BLUE team in Raid mode
  7027. // (temp workaround for decals causing the glows to not draw correctly)
  7028. if ( TFGameRules() && TFGameRules()->IsRaidMode() )
  7029. {
  7030. if ( GetTeamNumber() == TF_TEAM_BLUE )
  7031. {
  7032. return;
  7033. }
  7034. }
  7035. #endif // TF_RAID_MODE
  7036. // don't decal from inside the player
  7037. if ( tr.startsolid )
  7038. {
  7039. return;
  7040. }
  7041. BaseClass::AddDecal( rayStart, rayEnd, decalCenter, hitbox, decalIndex, doTrace, tr, maxLODToDecal );
  7042. }
  7043. //-----------------------------------------------------------------------------
  7044. // Called every time the player respawns
  7045. //-----------------------------------------------------------------------------
  7046. void C_TFPlayer::ClientPlayerRespawn( void )
  7047. {
  7048. if ( IsLocalPlayer() )
  7049. {
  7050. // MCJOHN - For testing, dump out all of the textures whenever we respawn
  7051. STAGING_ONLY_EXEC( engine->ClientCmd_Unrestricted( "mat_evict_all" ) );
  7052. // Dod called these, not sure why
  7053. //MoveToLastReceivedPosition( true );
  7054. //ResetLatched();
  7055. // Reset the camera.
  7056. HandleTaunting();
  7057. ResetToneMapping(1.0);
  7058. // Release the duck toggle key
  7059. KeyUp( &in_ducktoggle, NULL );
  7060. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_respawn" );
  7061. if ( event )
  7062. {
  7063. gameeventmanager->FireEventClientSide( event );
  7064. }
  7065. // NVNT revert tf haptics
  7066. tfHaptics.Revert();
  7067. #if defined( REPLAY_ENABLED )
  7068. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED )
  7069. {
  7070. // Notify replay history manager that the local player has spawned.
  7071. // NOTE: This won't do anything if replay isn't enabled, isn't recording, etc.
  7072. g_pClientReplayContext->OnPlayerSpawn();
  7073. }
  7074. #endif
  7075. SetAppropriateCamera( this );
  7076. if ( m_hRevivePrompt )
  7077. {
  7078. m_hRevivePrompt->MarkForDeletion();
  7079. m_hRevivePrompt = NULL;
  7080. }
  7081. m_bNotifiedWeaponInspectThisLife = false;
  7082. }
  7083. UpdateVisibility();
  7084. DestroyBoneAttachments();
  7085. UpdateDemomanEyeEffect( 0 );
  7086. UpdateKillStreakEffects( 0 );
  7087. UpdateMVMEyeGlowEffect( true );
  7088. m_hHeadGib = NULL;
  7089. m_hFirstGib = NULL;
  7090. m_hSpawnedGibs.Purge();
  7091. m_fMetersRan = 0;
  7092. SetShowHudMenuTauntSelection( false );
  7093. CleanUpAnimationOnSpawn();
  7094. }
  7095. //-----------------------------------------------------------------------------
  7096. // Purpose:
  7097. //-----------------------------------------------------------------------------
  7098. bool C_TFPlayer::ShouldDraw()
  7099. {
  7100. if ( IsLocalPlayer() )
  7101. {
  7102. if ( m_PlayerClass.HasCustomModel() && !m_PlayerClass.CustomModelIsVisibleToSelf() )
  7103. return false;
  7104. }
  7105. if ( this == C_TFPlayer::GetLocalTFPlayer() )
  7106. {
  7107. if ( this->m_Shared.InCond( TF_COND_ZOOMED ) )
  7108. {
  7109. return false;
  7110. }
  7111. }
  7112. return BaseClass::ShouldDraw();
  7113. }
  7114. //-----------------------------------------------------------------------------
  7115. // Purpose:
  7116. //-----------------------------------------------------------------------------
  7117. int C_TFPlayer::GetVisionFilterFlags( bool bWeaponsCheck /*= false */ )
  7118. {
  7119. #if defined( REPLAY_ENABLED )
  7120. extern IEngineClientReplay *g_pEngineClientReplay;
  7121. if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
  7122. {
  7123. if ( tf_replay_pyrovision.GetBool() )
  7124. {
  7125. return TF_VISION_FILTER_PYRO;
  7126. }
  7127. return 0x00;
  7128. }
  7129. #endif
  7130. int nVisionOptInFlags = 0;
  7131. CALL_ATTRIB_HOOK_INT( nVisionOptInFlags, vision_opt_in_flags );
  7132. if ( IsLocalPlayer() )
  7133. {
  7134. // Force our vision filter to a specific setting
  7135. if ( bWeaponsCheck && m_nForceVisionFilterFlags != 0 )
  7136. {
  7137. return m_nForceVisionFilterFlags;
  7138. }
  7139. #ifdef _DEBUG
  7140. int nFlags = 0;
  7141. if ( test_pyrovision.GetBool() )
  7142. {
  7143. nFlags |= TF_VISION_FILTER_PYRO;
  7144. }
  7145. if ( test_halloweenvision.GetBool() )
  7146. {
  7147. nFlags |= TF_VISION_FILTER_HALLOWEEN;
  7148. }
  7149. if ( test_romevision.GetBool() )
  7150. {
  7151. nFlags |= TF_VISION_FILTER_ROME;
  7152. }
  7153. if ( nFlags != 0 )
  7154. {
  7155. return nFlags;
  7156. }
  7157. if ( test_vision_off.GetBool() )
  7158. {
  7159. return 0x00;
  7160. }
  7161. #endif
  7162. // Check the PyroVision Convar
  7163. if ( GetTeamNumber() == TEAM_SPECTATOR )
  7164. {
  7165. if ( tf_spectate_pyrovision.GetBool() )
  7166. {
  7167. nVisionOptInFlags |= TF_VISION_FILTER_PYRO;
  7168. }
  7169. }
  7170. }
  7171. // check for holidays and add them in to the mix
  7172. // Halloween / Fullmoon vision
  7173. if ( TFGameRules()->IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  7174. {
  7175. nVisionOptInFlags |= TF_VISION_FILTER_HALLOWEEN;
  7176. }
  7177. // opt-in for romevision?
  7178. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() &&
  7179. TFSharedContentManager() && TFSharedContentManager()->IsSharedVisionAvailable( TF_VISION_FILTER_ROME ) &&
  7180. tf_romevision_opt_in.GetBool() )
  7181. {
  7182. nVisionOptInFlags |= TF_VISION_FILTER_ROME;
  7183. }
  7184. return nVisionOptInFlags;
  7185. }
  7186. //-----------------------------------------------------------------------------
  7187. // Purpose:
  7188. // Input : updateType -
  7189. //-----------------------------------------------------------------------------
  7190. void C_TFPlayer::CalculateVisionUsingCurrentFlags( void )
  7191. {
  7192. if ( IsLocalPlayer() )
  7193. {
  7194. for( int iClient = 1; iClient <= gpGlobals->maxClients; ++iClient )
  7195. {
  7196. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iClient ) );
  7197. if ( !pPlayer || !pPlayer->IsPlayer() )
  7198. continue;
  7199. if ( !pPlayer->IsAlive() )
  7200. continue;
  7201. pPlayer->UpdateWearables();
  7202. pPlayer->SetBodygroupsDirty();
  7203. if ( pPlayer->GetActiveWeapon() )
  7204. {
  7205. pPlayer->GetActiveWeapon()->RestartParticleEffect();
  7206. }
  7207. }
  7208. }
  7209. }
  7210. //-----------------------------------------------------------------------------
  7211. // Purpose:
  7212. //-----------------------------------------------------------------------------
  7213. void C_TFPlayer::CreateSaveMeEffect( MedicCallerType nType /*= CALLER_TYPE_NORMAL*/ )
  7214. {
  7215. // Don't create them for the local player in first-person view.
  7216. if ( IsLocalPlayer() && InFirstPersonView() )
  7217. return;
  7218. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  7219. return;
  7220. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  7221. // If I'm disguised as the enemy, play to all players
  7222. if ( m_Shared.InCond( TF_COND_DISGUISED ) && m_Shared.GetDisguiseTeam() != GetTeamNumber() && !m_Shared.IsStealthed() )
  7223. {
  7224. // play to all players
  7225. }
  7226. else
  7227. {
  7228. // only play to teammates
  7229. if ( pLocalPlayer && pLocalPlayer->GetTeamNumber() != GetTeamNumber() )
  7230. return;
  7231. }
  7232. StopSaveMeEffect();
  7233. float flHealth = float( GetHealth() ) / float( GetMaxHealth() );
  7234. Vector vHealth;
  7235. vHealth.x = flHealth;
  7236. vHealth.y = flHealth;
  7237. vHealth.z = flHealth;
  7238. if ( nType == CALLER_TYPE_AUTO )
  7239. {
  7240. m_pSaveMeEffect = ParticleProp()->Create( "speech_mediccall_auto", PATTACH_POINT_FOLLOW, "head" );
  7241. EmitSound( "Medic.AutoCallerAnnounce" );
  7242. }
  7243. else
  7244. {
  7245. m_pSaveMeEffect = ParticleProp()->Create( "speech_mediccall", PATTACH_POINT_FOLLOW, "head" );
  7246. }
  7247. if ( m_pSaveMeEffect )
  7248. {
  7249. m_pSaveMeEffect->SetControlPoint( 1, vHealth );
  7250. }
  7251. // If the local player is a medic, add this player to our list of medic callers
  7252. if ( pLocalPlayer && pLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && pLocalPlayer->IsAlive() == true )
  7253. {
  7254. Vector vecPos;
  7255. if ( GetAttachmentLocal( LookupAttachment( "head" ), vecPos ) )
  7256. {
  7257. vecPos += Vector(0,0,18); // Particle effect is 18 units above the attachment
  7258. CTFMedicCallerPanel::AddMedicCaller( this, 5.0, vecPos, nType );
  7259. }
  7260. }
  7261. IGameEvent *event = gameeventmanager->CreateEvent( "player_calledformedic" );
  7262. if ( event )
  7263. {
  7264. event->SetInt( "userid", GetUserID() );
  7265. gameeventmanager->FireEventClientSide( event );
  7266. }
  7267. m_flSaveMeExpireTime = gpGlobals->curtime + 5.0f;
  7268. }
  7269. //-----------------------------------------------------------------------------
  7270. // Purpose:
  7271. //-----------------------------------------------------------------------------
  7272. void C_TFPlayer::StopSaveMeEffect( bool bForceRemoveInstantly /*= false*/ )
  7273. {
  7274. if ( m_pSaveMeEffect )
  7275. {
  7276. if ( bForceRemoveInstantly )
  7277. {
  7278. ParticleProp()->StopEmissionAndDestroyImmediately( m_pSaveMeEffect );
  7279. }
  7280. else
  7281. {
  7282. ParticleProp()->StopEmission( m_pSaveMeEffect );
  7283. }
  7284. m_pSaveMeEffect = NULL;
  7285. }
  7286. }
  7287. //-----------------------------------------------------------------------------
  7288. // Purpose:
  7289. //-----------------------------------------------------------------------------
  7290. void C_TFPlayer::CreateTauntWithMeEffect()
  7291. {
  7292. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  7293. if ( !pLocalPlayer || this == pLocalPlayer )
  7294. return;
  7295. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  7296. return;
  7297. if ( !m_pTauntWithMeEffect )
  7298. {
  7299. const char *pszImageName;
  7300. const char *pszParticleName;
  7301. if ( GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
  7302. {
  7303. pszImageName = "../Effects/speech_taunt";
  7304. pszParticleName = "speech_taunt_all";
  7305. }
  7306. else if ( GetTeamNumber() == TF_TEAM_RED )
  7307. {
  7308. pszImageName = "../Effects/speech_taunt_red";
  7309. pszParticleName = "speech_taunt_red";
  7310. }
  7311. else
  7312. {
  7313. pszImageName = "../Effects/speech_taunt_blue";
  7314. pszParticleName = "speech_taunt_blue";
  7315. }
  7316. m_pTauntWithMeEffect = ParticleProp()->Create( pszParticleName, PATTACH_POINT_FOLLOW, "head" );
  7317. }
  7318. }
  7319. //-----------------------------------------------------------------------------
  7320. // Purpose:
  7321. //-----------------------------------------------------------------------------
  7322. void C_TFPlayer::StopTauntWithMeEffect()
  7323. {
  7324. if ( m_pTauntWithMeEffect )
  7325. {
  7326. ParticleProp()->StopEmissionAndDestroyImmediately( m_pTauntWithMeEffect );
  7327. m_pTauntWithMeEffect = NULL;
  7328. }
  7329. }
  7330. //-----------------------------------------------------------------------------
  7331. // Purpose:
  7332. //-----------------------------------------------------------------------------
  7333. void C_TFPlayer::CreateKart()
  7334. {
  7335. Assert( !m_pKart );
  7336. m_pKart = new C_BaseAnimating();
  7337. if ( m_pKart )
  7338. {
  7339. m_pKart->SetModel( "models/player/items/taunts/bumpercar/parts/bumpercar.mdl" );
  7340. m_pKart->m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1;
  7341. // Add to the client entity list. This has to be done before we attach to the parent or
  7342. // else we won't wind up on their "also shadow these children" list.
  7343. ClientEntityList().AddNonNetworkableEntity(m_pKart);
  7344. m_pKart->FollowEntity(this, true);
  7345. m_pKart->CreateShadow();
  7346. TrackAngRotation( false );
  7347. }
  7348. }
  7349. //-----------------------------------------------------------------------------
  7350. // Purpose:
  7351. //-----------------------------------------------------------------------------
  7352. void C_TFPlayer::RemoveKart()
  7353. {
  7354. StopKartEffect();
  7355. if ( m_pKart )
  7356. {
  7357. // Cleanup our shadows.
  7358. m_pKart->DestroyShadow();
  7359. // Remove from the client entity list.
  7360. ClientEntityList().RemoveEntity( m_pKart->GetClientHandle() );
  7361. m_pKart->Release();
  7362. m_pKart = NULL;
  7363. TrackAngRotation( true );
  7364. }
  7365. }
  7366. //-----------------------------------------------------------------------------
  7367. // Purpose:
  7368. //-----------------------------------------------------------------------------
  7369. void C_TFPlayer::CreateKartEffect( const char *pszEffectName )
  7370. {
  7371. if ( !m_pKart )
  7372. return;
  7373. #ifdef DEBUG
  7374. for ( int i=0; i<ARRAYSIZE( m_pKartParticles ); ++i )
  7375. {
  7376. Assert( !m_pKartParticles[i] );
  7377. }
  7378. #endif // DEBUG
  7379. m_pKartParticles[ KART_PARTICLE_LEFT_LIGHT ] = m_pKart->ParticleProp()->Create( pszEffectName , PATTACH_POINT_FOLLOW, "bumpercar_wheel_left" );
  7380. m_pKartParticles[ KART_PARTICLE_RIGHT_LIGHT ] = m_pKart->ParticleProp()->Create( pszEffectName , PATTACH_POINT_FOLLOW, "bumpercar_wheel_right" );
  7381. }
  7382. //-----------------------------------------------------------------------------
  7383. // Purpose:
  7384. //-----------------------------------------------------------------------------
  7385. void C_TFPlayer::StopKartEffect()
  7386. {
  7387. if ( !m_pKart )
  7388. return;
  7389. for ( int i=0; i<ARRAYSIZE( m_pKartParticles ); ++i )
  7390. {
  7391. if ( m_pKartParticles[i] )
  7392. {
  7393. m_pKart->ParticleProp()->StopEmission( m_pKartParticles[i] );
  7394. m_pKartParticles[i] = NULL;
  7395. }
  7396. }
  7397. }
  7398. //-----------------------------------------------------------------------------
  7399. void C_TFPlayer::StartKartBrakeEffect()
  7400. {
  7401. if ( !m_pKart )
  7402. return;
  7403. if ( !m_pKartParticles[KART_PARTICLE_LEFT_WHEEL] )
  7404. {
  7405. m_pKartParticles[KART_PARTICLE_LEFT_WHEEL] = m_pKart->ParticleProp()->Create( "kart_braking_sparks" , PATTACH_POINT_FOLLOW, "left_rear_wheel" );
  7406. }
  7407. if ( !m_pKartParticles[KART_PARTICLE_RIGHT_WHEEL] )
  7408. {
  7409. m_pKartParticles[KART_PARTICLE_RIGHT_WHEEL] = m_pKart->ParticleProp()->Create( "kart_braking_sparks", PATTACH_POINT_FOLLOW, "right_rear_wheel" );
  7410. }
  7411. }
  7412. //-----------------------------------------------------------------------------
  7413. void C_TFPlayer::StopKartBrakeEffect()
  7414. {
  7415. if ( !m_pKart )
  7416. return;
  7417. if ( m_pKartParticles[KART_PARTICLE_LEFT_WHEEL] )
  7418. {
  7419. m_pKart->ParticleProp()->StopEmission( m_pKartParticles[KART_PARTICLE_LEFT_WHEEL] );
  7420. }
  7421. if ( m_pKartParticles[KART_PARTICLE_RIGHT_WHEEL] )
  7422. {
  7423. m_pKart->ParticleProp()->StopEmission( m_pKartParticles[KART_PARTICLE_RIGHT_WHEEL] );
  7424. }
  7425. }
  7426. //-----------------------------------------------------------------------------
  7427. // Purpose:
  7428. //-----------------------------------------------------------------------------
  7429. bool C_TFPlayer::IsOverridingViewmodel( void )
  7430. {
  7431. C_TFPlayer *pPlayer = this;
  7432. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  7433. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE &&
  7434. pLocalPlayer->GetObserverTarget() && pLocalPlayer->GetObserverTarget()->IsPlayer() )
  7435. {
  7436. pPlayer = assert_cast<C_TFPlayer*>(pLocalPlayer->GetObserverTarget());
  7437. }
  7438. if ( pPlayer->m_Shared.IsInvulnerable() )
  7439. return true;
  7440. return BaseClass::IsOverridingViewmodel();
  7441. }
  7442. void C_TFPlayer::OverrideView( CViewSetup *pSetup )
  7443. {
  7444. BaseClass::OverrideView( pSetup );
  7445. TFPlayerClassData_t *pData = GetPlayerClass()->GetData();
  7446. if ( pData && g_ThirdPersonManager.WantToUseGameThirdPerson() )
  7447. {
  7448. Vector vecOffset = pData->m_vecThirdPersonOffset;
  7449. g_ThirdPersonManager.SetDesiredCameraOffset( vecOffset );
  7450. }
  7451. }
  7452. //-----------------------------------------------------------------------------
  7453. // Purpose: Draw my viewmodel in some special way
  7454. //-----------------------------------------------------------------------------
  7455. int C_TFPlayer::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags )
  7456. {
  7457. int ret = 0;
  7458. C_TFPlayer *pPlayer = this;
  7459. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  7460. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE &&
  7461. pLocalPlayer->GetObserverTarget() && pLocalPlayer->GetObserverTarget()->IsPlayer() )
  7462. {
  7463. pPlayer = assert_cast<C_TFPlayer*>(pLocalPlayer->GetObserverTarget());
  7464. }
  7465. if ( pPlayer->m_Shared.IsInvulnerable() )
  7466. {
  7467. if ( flags & STUDIO_RENDER )
  7468. {
  7469. // Force the invulnerable material
  7470. modelrender->ForcedMaterialOverride( *pPlayer->GetInvulnMaterialRef() );
  7471. }
  7472. // We allow our weapon to then override this if it wants to.
  7473. // This allows c_* weapons to draw themselves.
  7474. C_BaseCombatWeapon *pWeapon = pViewmodel->GetOwningWeapon();
  7475. if ( pWeapon && pWeapon->IsOverridingViewmodel() )
  7476. {
  7477. ret = pWeapon->DrawOverriddenViewmodel( pViewmodel, flags );
  7478. }
  7479. else
  7480. {
  7481. ret = pViewmodel->DrawOverriddenViewmodel( flags );
  7482. }
  7483. if ( flags & STUDIO_RENDER )
  7484. {
  7485. modelrender->ForcedMaterialOverride( NULL );
  7486. }
  7487. }
  7488. return ret;
  7489. }
  7490. //-----------------------------------------------------------------------------
  7491. // Purpose:
  7492. //-----------------------------------------------------------------------------
  7493. void C_TFPlayer::ApplyBoneMatrixTransform( matrix3x4_t& transform )
  7494. {
  7495. BaseClass::ApplyBoneMatrixTransform ( transform );
  7496. }
  7497. //-----------------------------------------------------------------------------
  7498. // Purpose:
  7499. //-----------------------------------------------------------------------------
  7500. void BuildBigHeadTransformations( CBaseAnimating *pObject, CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed, float flScale )
  7501. {
  7502. if ( !pObject || flScale == 1.f )
  7503. return;
  7504. // Scale the head.
  7505. int iHeadBone = pObject->LookupBone( "bip_head" );
  7506. if ( iHeadBone == -1 )
  7507. return;
  7508. matrix3x4_t &transform = pObject->GetBoneForWrite( iHeadBone );
  7509. Vector head_position;
  7510. MatrixGetTranslation( transform, head_position );
  7511. // Scale the head.
  7512. MatrixScaleBy ( flScale, transform );
  7513. const int cMaxNumHelms = 2;
  7514. int iHelmIndex[cMaxNumHelms];
  7515. iHelmIndex[0] = pObject->LookupBone( "prp_helmet" );
  7516. iHelmIndex[1] = pObject->LookupBone( "prp_hat" );
  7517. for ( int i = 0; i < cMaxNumHelms; i++ )
  7518. {
  7519. if ( iHelmIndex[i] != -1 )
  7520. {
  7521. matrix3x4_t &transformhelmet = pObject->GetBoneForWrite( iHelmIndex[i] );
  7522. MatrixScaleBy ( flScale, transformhelmet );
  7523. Vector helmet_position;
  7524. MatrixGetTranslation( transformhelmet, helmet_position );
  7525. Vector offset = helmet_position - head_position;
  7526. MatrixSetTranslation ( ( offset * flScale ) + head_position, transformhelmet );
  7527. }
  7528. }
  7529. }
  7530. //-----------------------------------------------------------------------------
  7531. // Purpose:
  7532. //-----------------------------------------------------------------------------
  7533. void BuildDecapitatedTransformations( CBaseAnimating *pObject, CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  7534. {
  7535. if ( !pObject )
  7536. return;
  7537. // Scale the head to nothing.
  7538. int iHeadBone = pObject->LookupBone( "bip_head" );
  7539. if ( iHeadBone != -1 )
  7540. {
  7541. matrix3x4_t &transform = pObject->GetBoneForWrite( iHeadBone );
  7542. MatrixScaleByZero ( transform );
  7543. }
  7544. int iHelm = pObject->LookupBone( "prp_helmet" );
  7545. if ( iHelm != -1 )
  7546. {
  7547. // Scale the helmet.
  7548. matrix3x4_t &transformhelmet = pObject->GetBoneForWrite( iHelm );
  7549. MatrixScaleByZero ( transformhelmet );
  7550. }
  7551. iHelm = pObject->LookupBone( "prp_hat" );
  7552. if ( iHelm != -1 )
  7553. {
  7554. matrix3x4_t &transformhelmet = pObject->GetBoneForWrite( iHelm );
  7555. MatrixScaleByZero ( transformhelmet );
  7556. }
  7557. }
  7558. //-----------------------------------------------------------------------------
  7559. // Purpose:
  7560. //-----------------------------------------------------------------------------
  7561. void BuildNeckScaleTransformations( CBaseAnimating *pObject, CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed, float flScale, int iClass )
  7562. {
  7563. if ( !pObject || flScale == 1.f )
  7564. return;
  7565. int iNeck = pObject->LookupBone( "bip_neck" );
  7566. if ( iNeck == -1 )
  7567. return;
  7568. matrix3x4_t &neck_transform = pObject->GetBoneForWrite( iNeck );
  7569. Vector spine_position, neck_position, head_position, position, offset(0, 0, 0);
  7570. if ( iClass != TF_CLASS_HEAVYWEAPONS )
  7571. {
  7572. // Compress the neck into the spine.
  7573. int iSpine = pObject->LookupBone( "bip_spine_3" );
  7574. if ( iSpine != -1 )
  7575. {
  7576. matrix3x4_t &spine_transform = pObject->GetBoneForWrite( iSpine );
  7577. MatrixPosition( spine_transform, spine_position );
  7578. MatrixPosition( neck_transform, neck_position );
  7579. position = flScale * ( neck_position - spine_position );
  7580. MatrixSetTranslation( spine_position + position, neck_transform );
  7581. }
  7582. }
  7583. if ( iClass == TF_CLASS_SPY )
  7584. {
  7585. int iCig = pObject->LookupBone( "prp_cig" );
  7586. if ( iCig != -1 )
  7587. {
  7588. matrix3x4_t &cig_transform = pObject->GetBoneForWrite( iCig );
  7589. MatrixScaleByZero ( cig_transform );
  7590. }
  7591. }
  7592. // Compress the head into the neck.
  7593. int iHead = pObject->LookupBone( "bip_head" );
  7594. if ( iHead != -1 )
  7595. {
  7596. matrix3x4_t &head_transform = pObject->GetBoneForWrite( iHead );
  7597. MatrixPosition( head_transform, head_position );
  7598. MatrixPosition( neck_transform, neck_position );
  7599. offset = head_position - neck_position;
  7600. MatrixSetTranslation( neck_position, head_transform );
  7601. }
  7602. // Store helmet bone offset.
  7603. int iHelm = pObject->LookupBone( "prp_helmet" );
  7604. if ( iHelm != -1 )
  7605. {
  7606. matrix3x4_t &helmet_transform = pObject->GetBoneForWrite( iHelm );
  7607. MatrixPosition( helmet_transform, position );
  7608. MatrixSetTranslation( position - offset, helmet_transform );
  7609. }
  7610. // Store alternate helmet bone offset.
  7611. iHelm = pObject->LookupBone( "prp_hat" );
  7612. if ( iHelm != -1 )
  7613. {
  7614. matrix3x4_t &hat_transform = pObject->GetBoneForWrite( iHelm );
  7615. MatrixPosition( hat_transform, position );
  7616. MatrixSetTranslation( position - offset, hat_transform );
  7617. }
  7618. }
  7619. //-----------------------------------------------------------------------------
  7620. // Purpose: Get child bones from a specified bone index
  7621. //-----------------------------------------------------------------------------
  7622. void AppendChildren_R( CUtlVector< const mstudiobone_t * > *pChildBones, const studiohdr_t *pHdr, int nBone )
  7623. {
  7624. if ( !pChildBones || !pHdr )
  7625. return;
  7626. // Child bones have to have a larger bone index than their parent, so start searching from nBone + 1
  7627. for ( int i = nBone + 1; i < pHdr->numbones; ++i )
  7628. {
  7629. const mstudiobone_t *pBone = pHdr->pBone( i );
  7630. if ( pBone->parent == nBone )
  7631. {
  7632. pChildBones->AddToTail( pBone );
  7633. // If you just want immediate children don't recurse, this will do a depth first traversal, could do
  7634. // breadth first by adding all children first and then looping through the added bones and recursing
  7635. AppendChildren_R( pChildBones, pHdr, i );
  7636. }
  7637. }
  7638. }
  7639. //-----------------------------------------------------------------------------
  7640. // Purpose:
  7641. //-----------------------------------------------------------------------------
  7642. void BuildTorsoScaleTransformations( CBaseAnimating *pObject, CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed, float flScale, int iClass )
  7643. {
  7644. if ( !pObject || flScale == 1.f )
  7645. return;
  7646. int iPelvis = pObject->LookupBone( "bip_pelvis" );
  7647. if ( iPelvis == -1 )
  7648. return;
  7649. const studiohdr_t *pHdr = modelinfo->GetStudiomodel( pObject->GetModel() );
  7650. int iTargetBone = iPelvis;
  7651. // must be in this order
  7652. static const char *s_torsoBoneNames[] =
  7653. {
  7654. "bip_spine_0",
  7655. "bip_spine_1",
  7656. "bip_spine_2",
  7657. "bip_spine_3",
  7658. "bip_neck"
  7659. };
  7660. // Compress torso bones toward pelvis in order.
  7661. for ( int i=0; i<ARRAYSIZE( s_torsoBoneNames ); ++i )
  7662. {
  7663. int iMoveBone = pObject->LookupBone( s_torsoBoneNames[i] );
  7664. if ( iMoveBone == -1 )
  7665. {
  7666. return;
  7667. }
  7668. const matrix3x4_t &targetBone_transform = pObject->GetBone( iTargetBone );
  7669. Vector vTargetBonePos;
  7670. MatrixPosition( targetBone_transform, vTargetBonePos );
  7671. matrix3x4_t &moveBone_transform = pObject->GetBoneForWrite( iMoveBone );
  7672. Vector vMoveBonePos;
  7673. MatrixPosition( moveBone_transform, vMoveBonePos );
  7674. Vector vNewMovePos = vTargetBonePos + flScale * ( vMoveBonePos - vTargetBonePos );
  7675. MatrixSetTranslation( vNewMovePos, moveBone_transform );
  7676. iTargetBone = iMoveBone;
  7677. Vector vOffset = vNewMovePos - vMoveBonePos;
  7678. // apply to all its child bones
  7679. CUtlVector< const mstudiobone_t * > vecChildBones;
  7680. AppendChildren_R( &vecChildBones, pHdr, iMoveBone );
  7681. for ( int j=0; j<vecChildBones.Count(); ++j )
  7682. {
  7683. int iChildBone = pObject->LookupBone( vecChildBones[j]->pszName() );
  7684. if ( iChildBone == -1 )
  7685. continue;
  7686. matrix3x4_t &childBone_transform = pObject->GetBoneForWrite( iChildBone );
  7687. Vector vChildPos;
  7688. MatrixPosition( childBone_transform, vChildPos );
  7689. MatrixSetTranslation( vChildPos + vOffset, childBone_transform );
  7690. }
  7691. }
  7692. }
  7693. //-----------------------------------------------------------------------------
  7694. // Purpose:
  7695. //-----------------------------------------------------------------------------
  7696. void BuildHandScaleTransformations( CBaseAnimating *pObject, CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed, float flScale )
  7697. {
  7698. if ( !pObject || flScale == 1.f )
  7699. return;
  7700. const studiohdr_t *pHdr = modelinfo->GetStudiomodel( pObject->GetModel() );
  7701. // must be in this order
  7702. static const char *s_handBoneNames[] =
  7703. {
  7704. "bip_hand_L",
  7705. "bip_hand_R"
  7706. };
  7707. // scale hand bones
  7708. for ( int i=0; i<ARRAYSIZE( s_handBoneNames ); ++i )
  7709. {
  7710. int iHand = pObject->LookupBone( s_handBoneNames[i] );
  7711. if ( iHand == -1 )
  7712. {
  7713. continue;
  7714. }
  7715. matrix3x4_t& transform = pObject->GetBoneForWrite( iHand );
  7716. MatrixScaleBy( flScale, transform );
  7717. // apply to all its child bones
  7718. CUtlVector< const mstudiobone_t * > vecChildBones;
  7719. AppendChildren_R( &vecChildBones, pHdr, iHand );
  7720. for ( int j=0; j<vecChildBones.Count(); ++j )
  7721. {
  7722. int iChildBone = pObject->LookupBone( vecChildBones[j]->pszName() );
  7723. if ( iChildBone == -1 )
  7724. continue;
  7725. matrix3x4_t &childBone_transform = pObject->GetBoneForWrite( iChildBone );
  7726. MatrixScaleBy( flScale, childBone_transform );
  7727. }
  7728. }
  7729. }
  7730. //-----------------------------------------------------------------------------
  7731. // Purpose:
  7732. //-----------------------------------------------------------------------------
  7733. void C_TFPlayer::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  7734. {
  7735. BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
  7736. if ( GetGroundEntity() == NULL )
  7737. {
  7738. Vector hullSizeNormal = VEC_HULL_MAX_SCALED( this ) - VEC_HULL_MIN_SCALED( this );
  7739. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( this ) - VEC_DUCK_HULL_MIN_SCALED( this );
  7740. Vector duckOffset = ( hullSizeNormal - hullSizeCrouch );
  7741. // The player is in the air.
  7742. if ( GetFlags() & FL_DUCKING )
  7743. {
  7744. if ( !m_bDuckJumpInterp )
  7745. {
  7746. m_flFirstDuckJumpInterp = gpGlobals->curtime;
  7747. }
  7748. m_bDuckJumpInterp = true;
  7749. m_flLastDuckJumpInterp = gpGlobals->curtime;
  7750. float flRatio = MIN( 0.15f, gpGlobals->curtime - m_flFirstDuckJumpInterp ) / 0.15f;
  7751. m_flDuckJumpInterp = 1.f - flRatio;
  7752. }
  7753. else if ( m_bDuckJumpInterp )
  7754. {
  7755. float flRatio = MIN( 0.15f, gpGlobals->curtime - m_flLastDuckJumpInterp ) / 0.15f;
  7756. m_flDuckJumpInterp = -(1.f - flRatio);
  7757. if ( m_flDuckJumpInterp == 0.f )
  7758. {
  7759. // Turn off interpolation when we return to our normal, unducked location.
  7760. m_bDuckJumpInterp = false;
  7761. }
  7762. }
  7763. if ( m_bDuckJumpInterp && m_flDuckJumpInterp != 0.f )
  7764. {
  7765. duckOffset *= m_flDuckJumpInterp;
  7766. for (int i = 0; i < hdr->numbones(); i++)
  7767. {
  7768. if ( !( hdr->boneFlags( i ) & boneMask ) )
  7769. continue;
  7770. matrix3x4_t &transform = GetBoneForWrite( i );
  7771. Vector bone_pos;
  7772. MatrixGetTranslation( transform, bone_pos );
  7773. MatrixSetTranslation( bone_pos - duckOffset, transform );
  7774. }
  7775. }
  7776. }
  7777. else if ( m_bDuckJumpInterp )
  7778. {
  7779. m_bDuckJumpInterp = false;
  7780. }
  7781. m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
  7782. float flHeadScale = m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ? 1.5 : m_flHeadScale;
  7783. BuildBigHeadTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, flHeadScale );
  7784. BuildTorsoScaleTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, m_flTorsoScale, GetPlayerClass()->GetClassIndex() );
  7785. BuildHandScaleTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, m_flHandScale );
  7786. BuildFirstPersonMeathookTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed, "bip_head" );
  7787. }
  7788. //-----------------------------------------------------------------------------
  7789. // Purpose:
  7790. //-----------------------------------------------------------------------------
  7791. void C_TFRagdoll::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  7792. {
  7793. BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
  7794. m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
  7795. BuildBigHeadTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, m_flHeadScale );
  7796. BuildTorsoScaleTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, m_flTorsoScale, GetClass() );
  7797. BuildHandScaleTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, m_flHandScale );
  7798. if ( IsDecapitation() && !m_bBaseTransform )
  7799. {
  7800. m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
  7801. BuildDecapitatedTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed );
  7802. }
  7803. if ( IsHeadSmash() && !m_bBaseTransform )
  7804. {
  7805. m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
  7806. BuildNeckScaleTransformations( this, hdr, pos, q, cameraTransform, boneMask, boneComputed, 0.5f, GetClass() );
  7807. }
  7808. }
  7809. //-----------------------------------------------------------------------------
  7810. // Purpose:
  7811. //-----------------------------------------------------------------------------
  7812. void C_TFPlayer::SetHealer( C_TFPlayer *pHealer, float flChargeLevel )
  7813. {
  7814. if ( pHealer && IsPlayerClass( TF_CLASS_SPY ) )
  7815. {
  7816. IGameEvent *event = gameeventmanager->CreateEvent( "player_healedbymedic" );
  7817. if ( event )
  7818. {
  7819. event->SetInt( "medic", pHealer->entindex() );
  7820. gameeventmanager->FireEventClientSide( event );
  7821. }
  7822. }
  7823. // We may be getting healed by multiple healers. Show the healer
  7824. // who's got the highest charge level.
  7825. if ( m_hHealer )
  7826. {
  7827. if ( m_flHealerChargeLevel > flChargeLevel )
  7828. return;
  7829. }
  7830. m_hHealer = pHealer;
  7831. m_flHealerChargeLevel = flChargeLevel;
  7832. }
  7833. //-----------------------------------------------------------------------------
  7834. // Purpose:
  7835. //-----------------------------------------------------------------------------
  7836. bool C_TFPlayer::MedicIsReleasingCharge( void )
  7837. {
  7838. if ( IsPlayerClass(TF_CLASS_MEDIC) )
  7839. {
  7840. CTFWeaponBase *pWpn = GetActiveTFWeapon();
  7841. if ( pWpn == NULL )
  7842. return false;
  7843. CWeaponMedigun *pMedigun = dynamic_cast <CWeaponMedigun*>( pWpn );
  7844. if ( pMedigun )
  7845. return pMedigun->IsReleasingCharge();
  7846. }
  7847. return false;
  7848. }
  7849. //-----------------------------------------------------------------------------
  7850. // Purpose:
  7851. //-----------------------------------------------------------------------------
  7852. bool C_TFPlayer::CanShowClassMenu( void )
  7853. {
  7854. if ( IsHLTV() )
  7855. return false;
  7856. if( TFGameRules() )
  7857. {
  7858. if ( TFGameRules()->IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true )
  7859. {
  7860. return !m_bArenaSpectator;
  7861. }
  7862. // Dont allow the change class menu to come up when we're doing the doors and things. There's really weird
  7863. // sorting issues that go on even though the class menu is supposed to draw under the match status panel.
  7864. if ( TFGameRules()->IsCompetitiveMode() )
  7865. {
  7866. float flRestartTime = TFGameRules()->GetRoundRestartTime() - gpGlobals->curtime;
  7867. if ( flRestartTime > 0.f && flRestartTime < 10.f )
  7868. {
  7869. return false;
  7870. }
  7871. }
  7872. }
  7873. return ( GetTeamNumber() > LAST_SHARED_TEAM );
  7874. }
  7875. //-----------------------------------------------------------------------------
  7876. // Purpose:
  7877. //-----------------------------------------------------------------------------
  7878. bool C_TFPlayer::CanShowTeamMenu( void )
  7879. {
  7880. if ( IsHLTV() )
  7881. return false;
  7882. #ifdef STAGING_ONLY
  7883. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  7884. #else
  7885. if ( TFGameRules() && ( TFGameRules()->IsCompetitiveMode() || TFGameRules()->IsPowerupMode() ) )
  7886. #endif // STAGING_ONLY
  7887. return false;
  7888. return ( GetTeamNumber() != TEAM_UNASSIGNED );
  7889. }
  7890. //-----------------------------------------------------------------------------
  7891. // Purpose:
  7892. //-----------------------------------------------------------------------------
  7893. void C_TFPlayer::InitializePoseParams( void )
  7894. {
  7895. /*
  7896. m_headYawPoseParam = LookupPoseParameter( "head_yaw" );
  7897. GetPoseParameterRange( m_headYawPoseParam, m_headYawMin, m_headYawMax );
  7898. m_headPitchPoseParam = LookupPoseParameter( "head_pitch" );
  7899. GetPoseParameterRange( m_headPitchPoseParam, m_headPitchMin, m_headPitchMax );
  7900. */
  7901. CStudioHdr *hdr = GetModelPtr();
  7902. Assert( hdr );
  7903. if ( !hdr )
  7904. return;
  7905. for ( int i = 0; i < hdr->GetNumPoseParameters() ; i++ )
  7906. {
  7907. SetPoseParameter( hdr, i, 0.0 );
  7908. }
  7909. }
  7910. //-----------------------------------------------------------------------------
  7911. // Purpose:
  7912. //-----------------------------------------------------------------------------
  7913. Vector C_TFPlayer::GetChaseCamViewOffset( CBaseEntity *target )
  7914. {
  7915. if ( target->IsBaseObject() )
  7916. {
  7917. CBaseObject* pObj = dynamic_cast<CBaseObject*>( target );
  7918. if ( pObj && pObj->IsMiniBuilding() )
  7919. return Vector(0,0,40);
  7920. else
  7921. return Vector(0,0,64);
  7922. }
  7923. return BaseClass::GetChaseCamViewOffset( target );
  7924. }
  7925. //-----------------------------------------------------------------------------
  7926. // Purpose: Called from PostDataUpdate to update the model index
  7927. //-----------------------------------------------------------------------------
  7928. void C_TFPlayer::ValidateModelIndex( void )
  7929. {
  7930. if ( m_Shared.InCond( TF_COND_DISGUISED_AS_DISPENSER ) && IsEnemyPlayer() && ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
  7931. {
  7932. m_nModelIndex = modelinfo->GetModelIndex( "models/buildables/dispenser_light.mdl" );
  7933. if ( C_BasePlayer::GetLocalPlayer() != this )
  7934. {
  7935. SetAbsAngles( vec3_angle );
  7936. }
  7937. }
  7938. else if ( m_Shared.InCond( TF_COND_DISGUISED ) && IsEnemyPlayer() )
  7939. {
  7940. TFPlayerClassData_t *pData = GetPlayerClassData( m_Shared.GetDisguiseClass() );
  7941. m_nModelIndex = modelinfo->GetModelIndex( pData->GetModelName() );
  7942. }
  7943. else if ( m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  7944. {
  7945. if ( GetTeamNumber() == TF_TEAM_BLUE )
  7946. {
  7947. m_nModelIndex = modelinfo->GetModelIndex( "models/props_halloween/ghost_no_hat.mdl" );
  7948. }
  7949. else
  7950. {
  7951. m_nModelIndex = modelinfo->GetModelIndex( "models/props_halloween/ghost_no_hat_red.mdl" );
  7952. }
  7953. }
  7954. else
  7955. {
  7956. C_TFPlayerClass *pClass = GetPlayerClass();
  7957. if ( pClass )
  7958. {
  7959. m_nModelIndex = modelinfo->GetModelIndex( pClass->GetModelName() );
  7960. }
  7961. }
  7962. if ( m_iSpyMaskBodygroup > -1 && GetModelPtr() != NULL && IsPlayerClass( TF_CLASS_SPY ) )
  7963. {
  7964. if ( m_Shared.InCond( TF_COND_DISGUISED ) || m_Shared.InCond( TF_COND_DISGUISED_AS_DISPENSER ) )
  7965. {
  7966. if ( !IsEnemyPlayer() || (m_Shared.GetDisguiseClass() == TF_CLASS_SPY) )
  7967. {
  7968. SetBodygroup( m_iSpyMaskBodygroup, 1 );
  7969. }
  7970. }
  7971. else
  7972. {
  7973. SetBodygroup( m_iSpyMaskBodygroup, 0 );
  7974. }
  7975. }
  7976. BaseClass::ValidateModelIndex();
  7977. }
  7978. //-----------------------------------------------------------------------------
  7979. // Purpose: Simulate the player for this frame
  7980. //-----------------------------------------------------------------------------
  7981. void C_TFPlayer::Simulate( void )
  7982. {
  7983. //Frame updates
  7984. if ( this == C_BasePlayer::GetLocalPlayer() )
  7985. {
  7986. //Update the flashlight
  7987. Flashlight();
  7988. }
  7989. // TF doesn't do step sounds based on velocity, instead using anim events
  7990. // So we deliberately skip over the base player simulate, which calls them.
  7991. BaseClass::BaseClass::Simulate();
  7992. }
  7993. //-----------------------------------------------------------------------------
  7994. // Purpose:
  7995. //-----------------------------------------------------------------------------
  7996. #define PLAYER_HALFWIDTH 10
  7997. #define SURFACE_SNOW 91
  7998. void C_TFPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
  7999. {
  8000. if ( event == 7001 )
  8001. {
  8002. // Force a footstep sound
  8003. m_flStepSoundTime = 0;
  8004. Vector vel;
  8005. EstimateAbsVelocity( vel );
  8006. surfacedata_t *t_pSurface = GetGroundSurface();
  8007. UpdateStepSound( t_pSurface, GetAbsOrigin(), vel );
  8008. if ( t_pSurface && !this->m_Shared.IsStealthed() && !this->m_Shared.InCond( TF_COND_DISGUISED ) && ( ( vel.x < -150 || vel.x > 150 ) || ( vel.y < -150 || vel.y > 150 ) ) )
  8009. {
  8010. // check for snow underfoot and trigger particle and decal fx
  8011. if ( t_pSurface->game.material == SURFACE_SNOW )
  8012. {
  8013. ParticleProp()->Create("snow_steppuff01", PATTACH_ABSORIGIN, 0 );
  8014. Vector right;
  8015. AngleVectors( angles, 0, &right, 0 );
  8016. // Figure out where the top of the stepping leg is
  8017. trace_t tr;
  8018. Vector hipOrigin;
  8019. VectorMA( origin, m_IsFootprintOnLeft ? -PLAYER_HALFWIDTH : PLAYER_HALFWIDTH, right, hipOrigin );
  8020. // Find where that leg hits the ground
  8021. UTIL_TraceLine( hipOrigin, hipOrigin + Vector(0, 0, -COORD_EXTENT * 1.74),
  8022. MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr);
  8023. // Create the decal
  8024. CPVSFilter filter( tr.endpos );
  8025. UTIL_DecalTrace( &tr, m_IsFootprintOnLeft ? "footprintL_snow" : "footprintR_snow" );
  8026. m_IsFootprintOnLeft = !m_IsFootprintOnLeft;
  8027. }
  8028. // Halloween-specific bonus footsteps
  8029. int iHalloweenFootstepType = 0;
  8030. if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  8031. {
  8032. CALL_ATTRIB_HOOK_INT( iHalloweenFootstepType, halloween_footstep_type );
  8033. }
  8034. if ( m_nFootStamps > 0 )
  8035. {
  8036. // White stamps!
  8037. iHalloweenFootstepType = 0xFFFFFFFF;
  8038. }
  8039. if ( iHalloweenFootstepType != 0 )
  8040. {
  8041. SpawnHalloweenSpellFootsteps( PATTACH_ABSORIGIN, iHalloweenFootstepType );
  8042. }
  8043. if ( m_nFootStamps > 0 )
  8044. {
  8045. m_nFootStamps--;
  8046. }
  8047. }
  8048. }
  8049. else if ( event == AE_WPN_HIDE )
  8050. {
  8051. if ( GetActiveWeapon() )
  8052. {
  8053. int iDisableWeaponHidingForAnimations = 0;
  8054. CALL_ATTRIB_HOOK_INT_ON_OTHER( GetActiveWeapon(), iDisableWeaponHidingForAnimations, disable_weapon_hiding_for_animations );
  8055. if ( iDisableWeaponHidingForAnimations == 0 )
  8056. {
  8057. GetActiveWeapon()->SetWeaponVisible( false );
  8058. }
  8059. }
  8060. }
  8061. else if ( event == AE_WPN_UNHIDE )
  8062. {
  8063. if ( m_Shared.IsControlStunned() )
  8064. return;
  8065. if ( GetActiveWeapon() )
  8066. {
  8067. GetActiveWeapon()->SetWeaponVisible( true );
  8068. }
  8069. }
  8070. else if ( event == AE_WPN_PLAYWPNSOUND )
  8071. {
  8072. if ( GetActiveWeapon() )
  8073. {
  8074. int iSnd = GetWeaponSoundFromString(options);
  8075. if ( iSnd != -1 )
  8076. {
  8077. GetActiveWeapon()->WeaponSound( (WeaponSound_t)iSnd );
  8078. }
  8079. }
  8080. }
  8081. else if ( event == TF_AE_CIGARETTE_THROW )
  8082. {
  8083. CEffectData data;
  8084. int iAttach = LookupAttachment( options );
  8085. GetAttachment( iAttach, data.m_vOrigin, data.m_vAngles );
  8086. data.m_vAngles = GetRenderAngles();
  8087. data.m_hEntity = ClientEntityList().EntIndexToHandle( entindex() );
  8088. DispatchEffect( "TF_ThrowCigarette", data );
  8089. return;
  8090. }
  8091. else if ( event == AE_CL_BODYGROUP_SET_VALUE_CMODEL_WPN )
  8092. {
  8093. CTFWeaponBase *pWpn = GetActiveTFWeapon();
  8094. if ( pWpn )
  8095. {
  8096. pWpn->GetAppropriateWorldOrViewModel()->FireEvent( origin, angles, AE_CL_BODYGROUP_SET_VALUE, options );
  8097. }
  8098. }
  8099. else if ( event == AE_TAUNT_ENABLE_MOVE )
  8100. {
  8101. m_bAllowMoveDuringTaunt = true;
  8102. }
  8103. else if ( event == AE_TAUNT_DISABLE_MOVE )
  8104. {
  8105. m_bAllowMoveDuringTaunt = false;
  8106. }
  8107. else
  8108. BaseClass::FireEvent( origin, angles, event, options );
  8109. }
  8110. void C_TFPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
  8111. {
  8112. // don't play footstep sound while in kart
  8113. if ( m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  8114. {
  8115. return;
  8116. }
  8117. // don't play footstep sound while taunting
  8118. if ( IsTaunting() )
  8119. {
  8120. return;
  8121. }
  8122. BaseClass::UpdateStepSound( psurface, vecOrigin, vecVelocity );
  8123. }
  8124. CNewParticleEffect *C_TFPlayer::SpawnHalloweenSpellFootsteps( ParticleAttachment_t eParticleAttachment, int iHalloweenFootstepType )
  8125. {
  8126. enum EPileOfHalloweenConstantHacks
  8127. {
  8128. kHalloweenSpell_RGBConstant_HHH = 2,
  8129. kHalloweenSpell_RGBConstant_TeamColor = 1,
  8130. kHalloweenSpell_RGB_Red = 12073019,
  8131. kHalloweenSpell_RGB_Blue = 5801378,
  8132. };
  8133. if ( iHalloweenFootstepType == kHalloweenSpell_RGBConstant_HHH )
  8134. return ParticleProp()->Create( "halloween_boss_foot_impact", eParticleAttachment, 0 );
  8135. CNewParticleEffect *pEffect = ParticleProp()->Create( m_nFootStamps ? "foot_stamp" : "halloween_boss_foot_impact_customcolor", eParticleAttachment, 0 );
  8136. if ( pEffect )
  8137. {
  8138. const int iRGB = iHalloweenFootstepType != kHalloweenSpell_RGBConstant_TeamColor // special "use team-color" hack value
  8139. ? iHalloweenFootstepType // use the attribute value as the RGB
  8140. : GetTeamNumber() == TF_TEAM_BLUE // which team are we on?
  8141. ? kHalloweenSpell_RGB_Blue
  8142. : kHalloweenSpell_RGB_Red;
  8143. pEffect->SetControlPoint( 1, Vector( ((iRGB & 0xff0000) >> 16) / 255.0f, ((iRGB & 0xff00) >> 8) / 255.0f, (iRGB & 0xff) / 255.0f ) );
  8144. }
  8145. return pEffect;
  8146. }
  8147. // Shadows
  8148. ConVar cl_blobbyshadows( "cl_blobbyshadows", "0", FCVAR_CLIENTDLL );
  8149. ShadowType_t C_TFPlayer::ShadowCastType( void )
  8150. {
  8151. // Removed the GetPercentInvisible - should be taken care off in BindProxy now.
  8152. if ( !IsVisible() /*|| GetPercentInvisible() > 0.0f*/ )
  8153. return SHADOWS_NONE;
  8154. if ( IsEffectActive(EF_NODRAW | EF_NOSHADOW) )
  8155. return SHADOWS_NONE;
  8156. // If in ragdoll mode.
  8157. if ( m_nRenderFX == kRenderFxRagdoll )
  8158. return SHADOWS_NONE;
  8159. if ( !ShouldDrawThisPlayer() )
  8160. {
  8161. // First-person with viewmodels.
  8162. return SHADOWS_NONE;
  8163. }
  8164. if ( cl_blobbyshadows.GetBool() )
  8165. return SHADOWS_SIMPLE;
  8166. return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
  8167. }
  8168. float g_flFattenAmt = 24.0; // Roughly how far out the Heavy's minigun pokes out.
  8169. void C_TFPlayer::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
  8170. {
  8171. if ( shadowType == SHADOWS_SIMPLE )
  8172. {
  8173. // Don't let the render bounds change when we're using blobby shadows, or else the shadow
  8174. // will pop and stretch.
  8175. mins = CollisionProp()->OBBMins();
  8176. maxs = CollisionProp()->OBBMaxs();
  8177. }
  8178. else
  8179. {
  8180. GetRenderBounds( mins, maxs );
  8181. // We do this because the normal bbox calculations don't take pose params into account, and
  8182. // the rotation of the guy's upper torso can place his gun a ways out of his bbox, and
  8183. // the shadow will get cut off as he rotates.
  8184. //
  8185. // Thus, we give it some padding here.
  8186. g_flFattenAmt = 36.0f;
  8187. mins -= Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  8188. maxs += Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  8189. }
  8190. }
  8191. void C_TFPlayer::GetRenderBounds( Vector& theMins, Vector& theMaxs )
  8192. {
  8193. // TODO POSTSHIP - this hack/fix goes hand-in-hand with a fix in CalcSequenceBoundingBoxes in utils/studiomdl/simplify.cpp.
  8194. // When we enable the fix in CalcSequenceBoundingBoxes, we can get rid of this.
  8195. //
  8196. // What we're doing right here is making sure it only uses the bbox for our lower-body sequences since,
  8197. // with the current animations and the bug in CalcSequenceBoundingBoxes, are WAY bigger than they need to be.
  8198. C_BaseAnimating::GetRenderBounds( theMins, theMaxs );
  8199. }
  8200. bool C_TFPlayer::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
  8201. {
  8202. if ( shadowType == SHADOWS_SIMPLE )
  8203. {
  8204. // Blobby shadows should sit directly underneath us.
  8205. pDirection->Init( 0, 0, -1 );
  8206. return true;
  8207. }
  8208. else
  8209. {
  8210. return BaseClass::GetShadowCastDirection( pDirection, shadowType );
  8211. }
  8212. }
  8213. //-----------------------------------------------------------------------------
  8214. // Purpose: Returns whether this player is the nemesis of the local player
  8215. //-----------------------------------------------------------------------------
  8216. bool C_TFPlayer::IsNemesisOfLocalPlayer()
  8217. {
  8218. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  8219. if ( pLocalPlayer )
  8220. {
  8221. // return whether this player is dominating the local player
  8222. return m_Shared.IsPlayerDominated( pLocalPlayer->entindex() );
  8223. }
  8224. return false;
  8225. }
  8226. extern ConVar tf_tournament_hide_domination_icons;
  8227. //-----------------------------------------------------------------------------
  8228. // Purpose: Returns whether we should show the dueling icon for this player
  8229. //-----------------------------------------------------------------------------
  8230. bool C_TFPlayer::ShouldShowDuelingIcon()
  8231. {
  8232. if ( TFGameRules() && TFGameRules()->IsInTournamentMode() && tf_tournament_hide_domination_icons.GetBool() )
  8233. return false;
  8234. if ( m_PlayerClass.HasCustomModel() )
  8235. return false;
  8236. extern bool DuelMiniGame_IsDuelingLocalPlayer( C_TFPlayer *pPlayer );
  8237. // we should show the dueling effect on this player if he is dueling the local player,
  8238. // and is not dead, cloaked or disguised
  8239. if ( DuelMiniGame_IsDuelingLocalPlayer( this ) && g_PR && g_PR->IsConnected( entindex() ) )
  8240. {
  8241. bool bStealthed = m_Shared.IsStealthed();
  8242. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED );
  8243. if ( IsAlive() && !bStealthed && !bDisguised )
  8244. return true;
  8245. }
  8246. return false;
  8247. }
  8248. //-----------------------------------------------------------------------------
  8249. // Purpose: Returns whether we should show the nemesis icon for this player
  8250. //-----------------------------------------------------------------------------
  8251. bool C_TFPlayer::ShouldShowNemesisIcon()
  8252. {
  8253. if ( TFGameRules() && TFGameRules()->IsInTournamentMode() && tf_tournament_hide_domination_icons.GetBool() )
  8254. return false;
  8255. if ( m_PlayerClass.HasCustomModel() )
  8256. return false;
  8257. // we should show the nemesis effect on this player if he is the nemesis of the local player,
  8258. // and is not dead, cloaked or disguised
  8259. if ( IsNemesisOfLocalPlayer() && g_PR && g_PR->IsConnected( entindex() ) )
  8260. {
  8261. bool bStealthed = m_Shared.IsStealthed();
  8262. bool bDisguised = m_Shared.InCond( TF_COND_DISGUISED );
  8263. if ( IsAlive() && !bStealthed && !bDisguised )
  8264. return true;
  8265. }
  8266. return false;
  8267. }
  8268. bool C_TFPlayer::IsWeaponLowered( void )
  8269. {
  8270. CTFWeaponBase *pWeapon = GetActiveTFWeapon();
  8271. if ( !pWeapon )
  8272. return false;
  8273. CTFGameRules *pRules = TFGameRules();
  8274. // Lower losing team's weapons in bonus round
  8275. if ( ( pRules->State_Get() == GR_STATE_TEAM_WIN ) && ( pRules->GetWinningTeam() != GetTeamNumber() ) )
  8276. return true;
  8277. // Hide all view models after the game is over
  8278. if ( pRules->State_Get() == GR_STATE_GAME_OVER && ( !pRules->IsInTournamentMode() || pRules->IsMannVsMachineMode() ) )
  8279. return true;
  8280. if ( m_Shared.InCond( TF_COND_PHASE ) )
  8281. return true;
  8282. if ( m_Shared.InCond( TF_COND_COMPETITIVE_LOSER ) )
  8283. return true;
  8284. return false;
  8285. }
  8286. //-----------------------------------------------------------------------------
  8287. // Purpose:
  8288. //-----------------------------------------------------------------------------
  8289. bool C_TFPlayer::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  8290. {
  8291. switch ( event->GetType() )
  8292. {
  8293. case CChoreoEvent::SEQUENCE:
  8294. case CChoreoEvent::GESTURE:
  8295. return StartGestureSceneEvent( info, scene, event, actor, pTarget );
  8296. default:
  8297. return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget );
  8298. }
  8299. }
  8300. bool C_TFPlayer::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
  8301. {
  8302. switch ( info->m_pEvent->GetType() )
  8303. {
  8304. case CChoreoEvent::SEQUENCE:
  8305. case CChoreoEvent::GESTURE:
  8306. return StopGestureSceneEvent( info, fastKill, canceled );
  8307. default:
  8308. return BaseClass::ClearSceneEvent( info, fastKill, canceled );
  8309. }
  8310. }
  8311. //-----------------------------------------------------------------------------
  8312. // Purpose:
  8313. //-----------------------------------------------------------------------------
  8314. bool C_TFPlayer::StartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  8315. {
  8316. // Get the (gesture) sequence.
  8317. info->m_nSequence = LookupSequence( event->GetParameters() );
  8318. if ( info->m_nSequence < 0 )
  8319. return false;
  8320. // Player the (gesture) sequence.
  8321. float flCycle = 0.0f;
  8322. bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
  8323. if (!looping)
  8324. {
  8325. float dt = scene->GetTime() - event->GetStartTime();
  8326. m_flTauntDuration = SequenceDuration( info->m_nSequence );
  8327. flCycle = dt / m_flTauntDuration;
  8328. flCycle = clamp( flCycle, 0.f, 1.0f );
  8329. }
  8330. else
  8331. {
  8332. float flStart, flEnd;
  8333. scene->GetSceneTimes( flStart, flEnd );
  8334. m_flTauntDuration = flEnd - flStart;
  8335. }
  8336. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  8337. m_PlayerAnimState->AddVCDSequenceToGestureSlot( GESTURE_SLOT_VCD, info->m_nSequence, flCycle, true );
  8338. m_nTauntSequence = info->m_nSequence;
  8339. m_flTauntStartTime = gpGlobals->curtime;
  8340. return true;
  8341. }
  8342. //-----------------------------------------------------------------------------
  8343. // Purpose:
  8344. //-----------------------------------------------------------------------------
  8345. bool C_TFPlayer::StopGestureSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
  8346. {
  8347. // The ResetGestureSlot call will prevent people from doing running taunts (which they like to do),
  8348. // so let's only reset the gesture slot if the scene contains a loop (such as the high five pose).
  8349. bool bSceneContainsLoop = false;
  8350. for ( int i = 0; i < info->m_pScene->GetNumEvents(); i++ )
  8351. {
  8352. CChoreoEvent *pEvent = info->m_pScene->GetEvent( i );
  8353. if ( pEvent->GetType() == CChoreoEvent::LOOP )
  8354. {
  8355. bSceneContainsLoop = true;
  8356. break;
  8357. }
  8358. }
  8359. if( bSceneContainsLoop )
  8360. m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  8361. return true;
  8362. }
  8363. bool C_TFPlayer::IsAllowedToSwitchWeapons( void )
  8364. {
  8365. if ( IsWeaponLowered() == true )
  8366. return false;
  8367. if ( TFGameRules() )
  8368. {
  8369. if ( TFGameRules()->IsPasstimeMode() && m_Shared.HasPasstimeBall() )
  8370. return false;
  8371. if ( TFGameRules()->ShowMatchSummary() )
  8372. return false;
  8373. }
  8374. // Can't weapon switch during a taunt.
  8375. if( m_Shared.InCond( TF_COND_TAUNTING ) && tf_allow_taunt_switch.GetInt() <= 1 )
  8376. return false;
  8377. return BaseClass::IsAllowedToSwitchWeapons();
  8378. }
  8379. IMaterial *C_TFPlayer::GetHeadLabelMaterial( void )
  8380. {
  8381. if ( g_pHeadLabelMaterial[0] == NULL )
  8382. SetupHeadLabelMaterials();
  8383. if ( GetTeamNumber() == TF_TEAM_RED )
  8384. {
  8385. return g_pHeadLabelMaterial[TF_PLAYER_HEAD_LABEL_RED];
  8386. }
  8387. else
  8388. {
  8389. return g_pHeadLabelMaterial[TF_PLAYER_HEAD_LABEL_BLUE];
  8390. }
  8391. return BaseClass::GetHeadLabelMaterial();
  8392. }
  8393. void SetupHeadLabelMaterials( void )
  8394. {
  8395. for ( int i = 0; i < 2; i++ )
  8396. {
  8397. if ( g_pHeadLabelMaterial[i] )
  8398. {
  8399. g_pHeadLabelMaterial[i]->DecrementReferenceCount();
  8400. g_pHeadLabelMaterial[i] = NULL;
  8401. }
  8402. g_pHeadLabelMaterial[i] = materials->FindMaterial( pszHeadLabelNames[i], TEXTURE_GROUP_VGUI );
  8403. if ( g_pHeadLabelMaterial[i] )
  8404. {
  8405. g_pHeadLabelMaterial[i]->IncrementReferenceCount();
  8406. }
  8407. }
  8408. }
  8409. void C_TFPlayer::ComputeFxBlend( void )
  8410. {
  8411. BaseClass::ComputeFxBlend();
  8412. float flInvisible = GetPercentInvisible();
  8413. if ( flInvisible != 0.0f )
  8414. {
  8415. // Tell our shadow
  8416. ClientShadowHandle_t hShadow = GetShadowHandle();
  8417. if ( hShadow != CLIENTSHADOW_INVALID_HANDLE )
  8418. {
  8419. g_pClientShadowMgr->SetFalloffBias( hShadow, flInvisible * 255 );
  8420. }
  8421. }
  8422. }
  8423. //-----------------------------------------------------------------------------
  8424. // Purpose:
  8425. //-----------------------------------------------------------------------------
  8426. void C_TFPlayer::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov )
  8427. {
  8428. HandleTaunting();
  8429. BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
  8430. }
  8431. void SelectDisguise( int iClass, int iTeam );
  8432. static void cc_tf_player_lastdisguise( const CCommand &args )
  8433. {
  8434. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  8435. if ( pPlayer == NULL )
  8436. return;
  8437. // disguise as our last known disguise. desired disguise will be initted to something sensible
  8438. if ( pPlayer->CanDisguise() || pPlayer->CanDisguise_OnKill() )
  8439. {
  8440. // disguise as the previous class, if one exists
  8441. int nClass = pPlayer->m_Shared.GetDesiredDisguiseClass();
  8442. int iLocalTeam = pPlayer->GetTeamNumber();
  8443. int iEnemyTeam = ( iLocalTeam == TF_TEAM_BLUE ) ? TF_TEAM_RED : TF_TEAM_BLUE;
  8444. int nTeam = pPlayer->m_Shared.WasLastDisguiseAsOwnTeam() ? iLocalTeam : iEnemyTeam;
  8445. //If we pass in "random" or whatever then just make it pick a random class.
  8446. if ( args.ArgC() > 1 )
  8447. {
  8448. nClass = TF_CLASS_UNDEFINED;
  8449. }
  8450. if ( nClass == TF_CLASS_UNDEFINED )
  8451. {
  8452. // they haven't disguised yet, pick a nice one for them.
  8453. // exclude some undesirable classes
  8454. do
  8455. {
  8456. nClass = random->RandomInt( TF_FIRST_NORMAL_CLASS, TF_LAST_NORMAL_CLASS );
  8457. } while( nClass == TF_CLASS_SCOUT || nClass == TF_CLASS_SPY );
  8458. nTeam = iEnemyTeam;
  8459. }
  8460. SelectDisguise( nClass, nTeam );
  8461. }
  8462. }
  8463. static ConCommand lastdisguise( "lastdisguise", cc_tf_player_lastdisguise, "Disguises the spy as the last disguise." );
  8464. static void cc_tf_player_disguise( const CCommand &args )
  8465. {
  8466. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  8467. if ( pPlayer == NULL )
  8468. return;
  8469. if ( args.ArgC() >= 3 )
  8470. {
  8471. if ( pPlayer->CanDisguise() || pPlayer->CanDisguise_OnKill() )
  8472. {
  8473. int nClass = atoi( args[ 1 ] );
  8474. int nTeam = atoi( args[ 2 ] );
  8475. //Disguise as enemy team
  8476. if ( nTeam == -1 )
  8477. {
  8478. if ( pPlayer->GetTeamNumber() == TF_TEAM_BLUE )
  8479. {
  8480. nTeam = TF_TEAM_RED;
  8481. }
  8482. else
  8483. {
  8484. nTeam = TF_TEAM_BLUE;
  8485. }
  8486. }
  8487. else if ( nTeam == -2 ) //disguise as my own team
  8488. {
  8489. nTeam = pPlayer->GetTeamNumber();
  8490. }
  8491. else
  8492. {
  8493. nTeam = ( nTeam == 1 ) ? TF_TEAM_BLUE : TF_TEAM_RED;
  8494. }
  8495. // intercepting the team value and reassigning what gets passed into Disguise()
  8496. // because the team numbers in the client menu don't match the #define values for the teams
  8497. SelectDisguise( nClass, nTeam );
  8498. }
  8499. }
  8500. }
  8501. static ConCommand disguise( "disguise", cc_tf_player_disguise, "Disguises the spy." );
  8502. //-----------------------------------------------------------------------------
  8503. // Purpose:
  8504. //-----------------------------------------------------------------------------
  8505. static void cc_tf_crashclient()
  8506. {
  8507. C_TFPlayer *pPlayer = NULL;
  8508. pPlayer->ComputeFxBlend();
  8509. }
  8510. static ConCommand tf_crashclient( "tf_crashclient", cc_tf_crashclient, "Crashes this client for testing.", FCVAR_DEVELOPMENTONLY );
  8511. //-----------------------------------------------------------------------------
  8512. // Purpose:
  8513. //-----------------------------------------------------------------------------
  8514. void C_TFPlayer::ForceUpdateObjectHudState( void )
  8515. {
  8516. m_bUpdateObjectHudState = true;
  8517. }
  8518. #include "c_obj_sentrygun.h"
  8519. static void cc_tf_debugsentrydmg()
  8520. {
  8521. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  8522. pPlayer->UpdateIDTarget();
  8523. int iTarget = pPlayer->GetIDTarget();
  8524. if ( iTarget > 0 )
  8525. {
  8526. C_BaseEntity *pEnt = cl_entitylist->GetEnt( iTarget );
  8527. C_ObjectSentrygun *pSentry = dynamic_cast< C_ObjectSentrygun * >( pEnt );
  8528. if ( pSentry )
  8529. {
  8530. pSentry->DebugDamageParticles();
  8531. }
  8532. }
  8533. }
  8534. static ConCommand tf_debugsentrydamage( "tf_debugsentrydamage", cc_tf_debugsentrydmg, "", FCVAR_DEVELOPMENTONLY );
  8535. /*
  8536. CON_COMMAND_F( spectate_random_server_extend_time, "extend the timer we're spectating this server before we disconnect", FCVAR_DEVELOPMENTONLY )
  8537. {
  8538. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  8539. if ( pPlayer )
  8540. {
  8541. if ( pPlayer->m_LeaveServerTimer.HasStarted() )
  8542. {
  8543. float flTime = spectate_random_server_basetime.GetFloat();
  8544. if ( args.ArgC() > 1 )
  8545. {
  8546. flTime = MAX( 0, Q_atof( args[ 1 ] ) );
  8547. }
  8548. pPlayer->m_LeaveServerTimer.Start( flTime );
  8549. }
  8550. }
  8551. }*/
  8552. //-----------------------------------------------------------------------------
  8553. // Purpose:
  8554. //-----------------------------------------------------------------------------
  8555. void C_TFPlayer::GetTargetIDDataString( bool bIsDisguised, OUT_Z_BYTECAP(iMaxLenInBytes) wchar_t *sDataString, int iMaxLenInBytes, bool &bIsAmmoData, bool &bIsKillStreakData )
  8556. {
  8557. Assert( iMaxLenInBytes >= sizeof(sDataString[0]) );
  8558. // Make sure the output string is always initialized to a null-terminated string,
  8559. // since the conditions below are tricky.
  8560. sDataString[0] = 0;
  8561. if ( bIsDisguised )
  8562. {
  8563. if ( !IsEnemyPlayer() )
  8564. {
  8565. // The target is a disguised friendly spy. They appear to the player with no disguise. Add the disguise
  8566. // team & class to the target ID element.
  8567. bool bDisguisedAsEnemy = ( m_Shared.GetDisguiseTeam() != GetTeamNumber() );
  8568. const wchar_t *wszAlignment = g_pVGuiLocalize->Find( bDisguisedAsEnemy ? "#TF_enemy" : "#TF_friendly" );
  8569. int classindex = m_Shared.GetDisguiseClass();
  8570. const wchar_t *wszClassName = g_pVGuiLocalize->Find( g_aPlayerClassNames[classindex] );
  8571. // build a string with disguise information
  8572. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_friendlyspy_disguise" ),
  8573. 2, wszAlignment, wszClassName );
  8574. }
  8575. else if ( IsEnemyPlayer() && (m_Shared.GetDisguiseClass() == TF_CLASS_SPY) )
  8576. {
  8577. // The target is an enemy spy disguised as a friendly spy. Show a fake team & class ID element.
  8578. int classindex = m_Shared.GetDisguiseMask();
  8579. const wchar_t *wszClassName = g_pVGuiLocalize->Find( g_aPlayerClassNames[classindex] );
  8580. const wchar_t *wszAlignment = g_pVGuiLocalize->Find( "#TF_enemy" );
  8581. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_friendlyspy_disguise" ),
  8582. 2, wszAlignment, wszClassName );
  8583. }
  8584. }
  8585. if ( IsPlayerClass( TF_CLASS_MEDIC ) )
  8586. {
  8587. CTFWeaponBase *pMedigun = NULL;
  8588. // Medics put their ubercharge & medigun type into the data string
  8589. wchar_t wszChargeLevel[ 10 ];
  8590. _snwprintf( wszChargeLevel, ARRAYSIZE(wszChargeLevel) - 1, L"%.0f", MedicGetChargeLevel( &pMedigun ) * 100 );
  8591. wszChargeLevel[ ARRAYSIZE(wszChargeLevel)-1 ] = '\0';
  8592. if ( pMedigun && pMedigun->GetAttributeContainer()->GetItem() && pMedigun->GetAttributeContainer()->GetItem()->GetItemQuality() != AE_NORMAL )
  8593. {
  8594. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_mediccharge_wpn" ), 2, wszChargeLevel, pMedigun->GetAttributeContainer()->GetItem()->GetItemName() );
  8595. }
  8596. else
  8597. {
  8598. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_mediccharge" ), 1, wszChargeLevel );
  8599. }
  8600. }
  8601. else if ( bIsDisguised && (m_Shared.GetDisguiseClass() == TF_CLASS_MEDIC) && IsEnemyPlayer() )
  8602. {
  8603. // Show a fake charge level for a disguised enemy medic.
  8604. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_mediccharge" ), 1, L"0" );
  8605. }
  8606. else
  8607. {
  8608. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  8609. if ( pLocalPlayer && pLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) )
  8610. {
  8611. CTFWeaponBase *pTFWeapon = GetActiveTFWeapon();
  8612. if ( pTFWeapon )
  8613. {
  8614. // Check for weapon_blocks_healing
  8615. int iBlockHealing = 0;
  8616. CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFWeapon, iBlockHealing, weapon_blocks_healing );
  8617. if ( iBlockHealing )
  8618. {
  8619. if ( pTFWeapon->GetAttributeContainer() && pTFWeapon->GetAttributeContainer()->GetItem() )
  8620. {
  8621. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_noheal" ), 1, pTFWeapon->GetAttributeContainer()->GetItem()->GetItemName() );
  8622. }
  8623. else
  8624. {
  8625. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_noheal_unknown" ), 0 );
  8626. }
  8627. }
  8628. // Show target's clip state to attached medics
  8629. if ( !sDataString[0] && m_nActiveWpnClip >= 0 )
  8630. {
  8631. C_TFPlayer *pTFHealTarget = ToTFPlayer( pLocalPlayer->MedicGetHealTarget() );
  8632. if ( pTFHealTarget && pTFHealTarget == this )
  8633. {
  8634. wchar_t wszClip[10];
  8635. V_snwprintf( wszClip, ARRAYSIZE(wszClip) - 1, L"%d", m_nActiveWpnClip );
  8636. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_ammo" ), 1, wszClip );
  8637. bIsAmmoData = true;
  8638. }
  8639. }
  8640. }
  8641. }
  8642. if ( !bIsAmmoData )
  8643. {
  8644. // Check for kill streak data
  8645. if ( m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills ) > 0 )
  8646. {
  8647. bIsKillStreakData = true;
  8648. wchar_t wszClip[10];
  8649. V_snwprintf( wszClip, ARRAYSIZE(wszClip) - 1, L"%d", m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills ) );
  8650. g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find( "#TF_playerid_ammo" ), 1, wszClip );
  8651. }
  8652. }
  8653. }
  8654. }
  8655. //-----------------------------------------------------------------------------
  8656. // Purpose:
  8657. //-----------------------------------------------------------------------------
  8658. void C_TFPlayer::ThirdPersonSwitch( bool bThirdperson )
  8659. {
  8660. BaseClass::ThirdPersonSwitch( bThirdperson );
  8661. // Update our viewmodel whenever we switch view modes
  8662. C_TFPlayer *pTFObserverTarget = ToTFPlayer( GetObserverTarget() );
  8663. if ( pTFObserverTarget )
  8664. {
  8665. C_TFWeaponBase *pWeapon = pTFObserverTarget->m_Shared.GetActiveTFWeapon();
  8666. if ( pWeapon )
  8667. {
  8668. pWeapon->UpdateAttachmentModels();
  8669. }
  8670. pTFObserverTarget->UpdateWearables();
  8671. pTFObserverTarget->SetBodygroupsDirty();
  8672. }
  8673. // Update our weapon's visibility when we switch
  8674. C_BaseCombatWeapon* pWeapon = GetActiveWeapon();
  8675. if ( pWeapon )
  8676. {
  8677. pWeapon->UpdateVisibility();
  8678. }
  8679. // Update visibility of any worn items.
  8680. UpdateWearables();
  8681. SetBodygroupsDirty();
  8682. }
  8683. //-----------------------------------------------------------------------------
  8684. // Purpose: Update our active weapon's extra wearable's visibility and shadows
  8685. // as well as all our wearables.
  8686. //-----------------------------------------------------------------------------
  8687. void C_TFPlayer::UpdateWearables()
  8688. {
  8689. BaseClass::UpdateWearables();
  8690. CTFWeaponBase* pWeapon = dynamic_cast< CTFWeaponBase* >( GetActiveWeapon() );
  8691. if ( pWeapon )
  8692. {
  8693. pWeapon->UpdateVisibility();
  8694. }
  8695. }
  8696. //-----------------------------------------------------------------------------
  8697. // Purpose:
  8698. //-----------------------------------------------------------------------------
  8699. void C_TFPlayer::OnAchievementAchieved( int iAchievement )
  8700. {
  8701. EmitSound( "Achievement.Earned" );
  8702. BaseClass::OnAchievementAchieved( iAchievement );
  8703. }
  8704. //-----------------------------------------------------------------------------
  8705. // Purpose: Feign Death
  8706. //-----------------------------------------------------------------------------
  8707. void C_TFPlayer::FeignDeath( CTakeDamageInfo& info )
  8708. {
  8709. // Can't feign death if we're actually dead or if we're not a spy.
  8710. if ( !IsAlive() || !IsPlayerClass( TF_CLASS_SPY) )
  8711. return;
  8712. // Can't feign death if we're already stealthed.
  8713. if ( m_Shared.IsStealthed() )
  8714. return;
  8715. // Can't feign death if we aren't at full cloak energy.
  8716. if ( !CanGoInvisible() || ( m_Shared.GetSpyCloakMeter() < 100.0f ) )
  8717. return;
  8718. // Predict feign death condition effects.
  8719. m_Shared.AddCond( TF_COND_FEIGN_DEATH );
  8720. }
  8721. //-----------------------------------------------------------------------------
  8722. // Purpose:
  8723. //-----------------------------------------------------------------------------
  8724. void C_TFPlayer::UpdateSpyStateChange( void )
  8725. {
  8726. UpdateOverhealEffect();
  8727. UpdateRecentlyTeleportedEffect();
  8728. UpdatedMarkedForDeathEffect();
  8729. // Update our resist shield color
  8730. if ( m_pTempShield && m_Shared.GetCarryingRuneType() == RUNE_RESIST )
  8731. {
  8732. m_pTempShield->m_nSkin = ( m_Shared.GetDisplayedTeam() == TF_TEAM_RED ) ? 0 : 1;
  8733. }
  8734. UpdateRuneIcon( true );
  8735. // Remove Speed lines if Stealthed
  8736. if ( m_Shared.IsStealthed() )
  8737. {
  8738. if ( m_pSpeedBoostEffect )
  8739. {
  8740. ParticleProp()->StopEmission( m_pSpeedBoostEffect );
  8741. m_pSpeedBoostEffect = NULL;
  8742. }
  8743. m_Shared.EndRadiusHealEffect();
  8744. }
  8745. // Force Weapon updates
  8746. if ( GetActiveWeapon() )
  8747. {
  8748. GetActiveWeapon()->PreDataUpdate( DATA_UPDATE_DATATABLE_CHANGED );
  8749. }
  8750. // TranqMark
  8751. // test : Let Marked spies be seen via the debuff
  8752. //bool bShow = ( m_Shared.InCond( TF_COND_TRANQ_MARKED )
  8753. // && !m_Shared.IsStealthed();
  8754. }
  8755. //-----------------------------------------------------------------------------
  8756. // Purpose:
  8757. //-----------------------------------------------------------------------------
  8758. void C_TFPlayer::UpdateOverhealEffect( void )
  8759. {
  8760. bool bShow = m_Shared.InCond( TF_COND_HEALTH_OVERHEALED );
  8761. int iTeam = GetTeamNumber();
  8762. if ( IsLocalPlayer() || ( m_Shared.IsStealthed() && !InSameTeam( GetLocalTFPlayer() ) ) )
  8763. {
  8764. bShow = false;
  8765. }
  8766. else if ( IsPlayerClass( TF_CLASS_SPY ) && !InSameTeam( GetLocalTFPlayer() ) && m_Shared.InCond( TF_COND_DISGUISED ))
  8767. {
  8768. iTeam = m_Shared.GetDisguiseTeam();
  8769. }
  8770. if ( bShow )
  8771. {
  8772. if ( !m_pOverHealedEffect )
  8773. {
  8774. CreateOverhealEffect( iTeam );
  8775. }
  8776. else if ( m_pOverHealedEffect )
  8777. {
  8778. ParticleProp()->StopEmission( m_pOverHealedEffect );
  8779. m_pOverHealedEffect = NULL;
  8780. CreateOverhealEffect( iTeam );
  8781. }
  8782. }
  8783. else
  8784. {
  8785. if ( m_pOverHealedEffect )
  8786. {
  8787. ParticleProp()->StopEmission( m_pOverHealedEffect );
  8788. m_pOverHealedEffect = NULL;
  8789. }
  8790. }
  8791. }
  8792. //-----------------------------------------------------------------------------
  8793. // Purpose:
  8794. //-----------------------------------------------------------------------------
  8795. void C_TFPlayer::CreateOverhealEffect( int iTeam )
  8796. {
  8797. const char *pEffect = NULL;
  8798. switch( iTeam )
  8799. {
  8800. case TF_TEAM_BLUE:
  8801. pEffect = "overhealedplayer_blue_pluses";
  8802. break;
  8803. case TF_TEAM_RED:
  8804. pEffect = "overhealedplayer_red_pluses";
  8805. break;
  8806. default:
  8807. break;
  8808. }
  8809. if ( pEffect )
  8810. {
  8811. m_pOverHealedEffect = ParticleProp()->Create( pEffect, PATTACH_ABSORIGIN_FOLLOW );
  8812. }
  8813. }
  8814. //-----------------------------------------------------------------------------
  8815. // Purpose:
  8816. //-----------------------------------------------------------------------------
  8817. void C_TFPlayer::SetMetersRan( float fMeters, int iFrame )
  8818. {
  8819. if ( iFrame != m_iLastRanFrame )
  8820. {
  8821. m_iLastRanFrame = iFrame;
  8822. m_fMetersRan = fMeters;
  8823. }
  8824. }
  8825. bool C_TFPlayer::InSameDisguisedTeam( CBaseEntity *pEnt )
  8826. {
  8827. if ( pEnt == NULL )
  8828. return false;
  8829. int iMyApparentTeam = GetTeamNumber();
  8830. if ( m_bIsCoaching && m_hStudent )
  8831. {
  8832. iMyApparentTeam = m_hStudent->GetTeamNumber();
  8833. }
  8834. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  8835. {
  8836. iMyApparentTeam = m_Shared.GetDisguiseTeam();
  8837. }
  8838. C_TFPlayer *pPlayerEnt = ToTFPlayer( pEnt );
  8839. int iTheirApparentTeam = pEnt->GetTeamNumber();
  8840. if ( pPlayerEnt && pPlayerEnt->m_Shared.InCond( TF_COND_DISGUISED ) )
  8841. {
  8842. iTheirApparentTeam = pPlayerEnt->m_Shared.GetDisguiseTeam();
  8843. }
  8844. return ( iMyApparentTeam == iTheirApparentTeam || GetTeamNumber() == pEnt->GetTeamNumber() || iTheirApparentTeam == GetTeamNumber() );
  8845. }
  8846. //-----------------------------------------------------------------------------
  8847. // Purpose: When a player's ragdoll is created we want the ragdoll to
  8848. // appear to have any attached wearable items the player had.
  8849. // This uses client side entities instead of the actual wearable items.
  8850. //-----------------------------------------------------------------------------
  8851. static bool IsDecapitationCustomDamageType( int iCustomDamageType )
  8852. {
  8853. return iCustomDamageType == TF_DMG_CUSTOM_DECAPITATION
  8854. || iCustomDamageType == TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING
  8855. || iCustomDamageType == TF_DMG_CUSTOM_DECAPITATION_BOSS
  8856. || iCustomDamageType == TF_DMG_CUSTOM_MERASMUS_DECAPITATION;
  8857. }
  8858. void C_TFPlayer::CreateBoneAttachmentsFromWearables( C_TFRagdoll *pRagdoll, bool bDisguised )
  8859. {
  8860. if ( bDisguised && !ShouldDrawSpyAsDisguised() )
  8861. {
  8862. // the team of disguised spy don't see any wearable
  8863. return;
  8864. }
  8865. for ( int wbl = GetNumWearables()-1; wbl >= 0; wbl-- )
  8866. {
  8867. C_TFWearable *pItem = dynamic_cast<C_TFWearable*> (GetWearable(wbl));
  8868. if ( !pItem )
  8869. continue;
  8870. if ( pItem->IsViewModelWearable() )
  8871. continue;
  8872. if ( pItem->IsDisguiseWearable() && !bDisguised )
  8873. continue;
  8874. if ( !pItem->IsDisguiseWearable() && bDisguised )
  8875. continue;
  8876. pItem->OnWearerDeath();
  8877. if ( pItem->GetDropType() >= ITEM_DROP_TYPE_DROP )
  8878. continue;
  8879. CAttributeContainer *pCont = pItem->GetAttributeContainer();
  8880. CEconItemView *pEconItemView = pCont ? pCont->GetItem() : NULL;
  8881. // If this is a decapitated ragdoll, don't attach anything in our head/misc item slots.
  8882. if ( IsDecapitationCustomDamageType( pRagdoll->GetDamageCustom() ) )
  8883. {
  8884. int iLoadoutSlot = pEconItemView
  8885. ? pEconItemView->GetStaticData()->GetDefaultLoadoutSlot()
  8886. : LOADOUT_POSITION_INVALID;
  8887. if ( iLoadoutSlot == LOADOUT_POSITION_HEAD || iLoadoutSlot == LOADOUT_POSITION_MISC )
  8888. continue;
  8889. }
  8890. C_EconWearableGib *pAttachment = new C_EconWearableGib;
  8891. if ( !pAttachment )
  8892. return;
  8893. const char *pszModelName = modelinfo->GetModelName( pItem->GetModel() );
  8894. if ( !pszModelName || pszModelName[ 0 ] == '\0' || pszModelName[ 0 ] == '?' )
  8895. continue;
  8896. // We need to set the item now, so that it can pull data out during Initialize();
  8897. if ( pEconItemView )
  8898. {
  8899. Assert( pAttachment->GetAttributeContainer() );
  8900. pAttachment->GetAttributeContainer()->SetItem( pEconItemView );
  8901. }
  8902. pAttachment->SetModelName( AllocPooledString( pszModelName ) );
  8903. if ( !pAttachment->Initialize( true ) )
  8904. {
  8905. pAttachment->Release();
  8906. continue;
  8907. }
  8908. pAttachment->m_iTeamNum = pRagdoll->GetRagdollTeam();
  8909. pAttachment->m_nSkin = pItem->GetSkin();
  8910. pAttachment->AttachEntityToBone( this, -1, Vector(0,0,0), QAngle(0,0,0) );
  8911. if ( pEconItemView )
  8912. {
  8913. if ( pEconItemView->GetStaticData()->UsesPerClassBodygroups( GetTeamNumber() ) )
  8914. {
  8915. // Classes start at 1, bodygroups at 0, so we shift them all back 1.
  8916. pAttachment->SetBodygroup( 1, (pRagdoll->GetClass()-1) );
  8917. }
  8918. }
  8919. }
  8920. }
  8921. //-----------------------------------------------------------------------------
  8922. // Purpose: Loop through all non-standard items carried by this player, and pick the next one.
  8923. // pLastItem - pointer to the int that stores the last item found, for iteration purposes.
  8924. //-----------------------------------------------------------------------------
  8925. CEconItemView *C_TFPlayer::GetInspectItem( int *pLastItem )
  8926. {
  8927. int iItemsFound = 0;
  8928. CEconItemView *pFirstItem = NULL;
  8929. int nCount = WeaponCount();
  8930. for ( int i = 0; i < nCount; ++i )
  8931. {
  8932. C_BaseCombatWeapon *pWeapon = GetWeapon(i);
  8933. if ( !pWeapon )
  8934. continue;
  8935. CEconItemView *pTmp = pWeapon->GetAttributeContainer()->GetItem();
  8936. if ( !pTmp->IsValid() )
  8937. continue;
  8938. // don't show hidden items in the inspect panel
  8939. if ( pTmp->GetItemDefinition() && pTmp->GetItemDefinition()->IsHidden() )
  8940. continue;
  8941. if ( !pFirstItem )
  8942. {
  8943. pFirstItem = pTmp;
  8944. }
  8945. iItemsFound++;
  8946. if ( iItemsFound <= *pLastItem )
  8947. continue;
  8948. // Found the next item, we're done.
  8949. *pLastItem = iItemsFound;
  8950. return pTmp;
  8951. }
  8952. // Check wearables too
  8953. nCount = GetNumWearables();
  8954. for ( int i = 0; i < nCount; ++i )
  8955. {
  8956. C_EconWearable *pWearable = GetWearable(i);
  8957. if ( pWearable )
  8958. {
  8959. CEconItemView *pTmp = pWearable->GetAttributeContainer()->GetItem();
  8960. if ( !pTmp->IsValid() )
  8961. continue;
  8962. if ( !pFirstItem )
  8963. {
  8964. pFirstItem = pTmp;
  8965. }
  8966. iItemsFound++;
  8967. if ( iItemsFound <= *pLastItem )
  8968. continue;
  8969. // Found the next item, we're done.
  8970. *pLastItem = iItemsFound;
  8971. return pTmp;
  8972. }
  8973. }
  8974. // If we didn't find an item, go back to the first one
  8975. if ( pFirstItem )
  8976. {
  8977. *pLastItem = 1;
  8978. return pFirstItem;
  8979. }
  8980. return NULL;
  8981. }
  8982. //-----------------------------------------------------------------------------
  8983. // Purpose:
  8984. //-----------------------------------------------------------------------------
  8985. bool C_TFPlayer::CanUseFirstPersonCommand( void )
  8986. {
  8987. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  8988. if ( pLocalPlayer )
  8989. {
  8990. if ( pLocalPlayer->m_Shared.InCond( TF_COND_PHASE ) ||
  8991. pLocalPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ||
  8992. pLocalPlayer->m_Shared.IsControlStunned() ||
  8993. pLocalPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  8994. {
  8995. return false;
  8996. }
  8997. }
  8998. return BaseClass::CanUseFirstPersonCommand();
  8999. }
  9000. //-----------------------------------------------------------------------------
  9001. // Purpose:
  9002. //-----------------------------------------------------------------------------
  9003. void C_TFPlayer::UpdateDemomanEyeEffect( int iDecapitations )
  9004. {
  9005. if ( m_pEyeEffect )
  9006. {
  9007. ParticleProp()->StopEmission( m_pEyeEffect );
  9008. m_pEyeEffect = NULL;
  9009. }
  9010. if ( iDecapitations == 0 )
  9011. return;
  9012. iDecapitations = MIN( iDecapitations, MAX_DECAPITATIONS );
  9013. const char* pszEffect = GetDemomanEyeEffectName( iDecapitations );
  9014. if ( pszEffect )
  9015. {
  9016. m_pEyeEffect = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "eyeglow_L" );
  9017. }
  9018. }
  9019. //-----------------------------------------------------------------------------
  9020. const char *C_TFPlayer::GetDemomanEyeEffectName( int iDecapitations )
  9021. {
  9022. if ( iDecapitations < 1 )
  9023. return NULL;
  9024. switch ( iDecapitations )
  9025. {
  9026. case 1:
  9027. return "eye_powerup_green_lvl_1";
  9028. case 2:
  9029. return "eye_powerup_green_lvl_2";
  9030. case 3:
  9031. return "eye_powerup_green_lvl_3";
  9032. default:
  9033. return "eye_powerup_green_lvl_4";
  9034. }
  9035. }
  9036. //-----------------------------------------------------------------------------
  9037. void GetVectorColorForParticleSystem ( int iSystem, bool bIsBlueTeam, bool bUseColor2, Vector &vecColor )
  9038. {
  9039. if ( iSystem < 0 || iSystem >= ARRAYSIZE( g_KillStreakEffectsBase ) )
  9040. return;
  9041. killstreak_params_t params = g_KillStreakEffectsBase[iSystem];
  9042. if ( bIsBlueTeam && g_KillStreakEffectsBase[iSystem].m_bHasTeamColor )
  9043. {
  9044. Assert( iSystem > 0 && iSystem < ARRAYSIZE( g_KillStreakEffectsBlue ) );
  9045. params = g_KillStreakEffectsBlue[iSystem];
  9046. }
  9047. if ( bUseColor2 )
  9048. {
  9049. vecColor = Vector( params.m_color2_r, params.m_color2_g, params.m_color2_b );
  9050. }
  9051. else
  9052. {
  9053. vecColor = Vector( params.m_color1_r, params.m_color1_g, params.m_color1_b );
  9054. }
  9055. return;
  9056. }
  9057. //-----------------------------------------------------------------------------
  9058. void C_TFPlayer::UpdateKillStreakEffects( int iCount, bool bKillScored /* = false */ )
  9059. {
  9060. #ifdef STAGING_ONLY
  9061. const int LOW_GLOW = 2001;
  9062. #endif
  9063. const int HIGH_GLOW = 20000;
  9064. // Staging only hack to test eye glows on players
  9065. if ( m_pEyeGlowEffect[0] )
  9066. {
  9067. ParticleProp()->StopEmission( m_pEyeGlowEffect[ 0 ] );
  9068. m_pEyeGlowEffect[ 0 ] = NULL;
  9069. }
  9070. if ( m_pEyeGlowEffect[1] )
  9071. {
  9072. ParticleProp()->StopEmission( m_pEyeGlowEffect[ 1 ] );
  9073. m_pEyeGlowEffect[ 1 ] = NULL;
  9074. }
  9075. CTFWeaponBase *pActiveWeapon = GetActiveTFWeapon();
  9076. if ( !pActiveWeapon )
  9077. return;
  9078. // Disguised Spies, Use targets kill streak. Otherwise nothing
  9079. if ( m_Shared.InCond( TF_COND_DISGUISED ) )
  9080. {
  9081. C_TFPlayer *pDisguiseTarget = ToTFPlayer( m_Shared.GetDisguiseTarget() );
  9082. if ( pDisguiseTarget )
  9083. iCount = pDisguiseTarget->m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills );
  9084. else
  9085. iCount = 0;
  9086. }
  9087. #ifdef STAGING_ONLY
  9088. // !TEST!
  9089. if ( tf_test_setkillcount.GetInt() > 0 )
  9090. {
  9091. iCount = tf_test_setkillcount.GetInt();
  9092. }
  9093. #endif
  9094. // Check if they have the appropriate attribute.
  9095. int iKillStreakEffectIndex = 0;
  9096. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iKillStreakEffectIndex, killstreak_effect );
  9097. int iKillStreakColorIndex = 0;
  9098. CALL_ATTRIB_HOOK_INT_ON_OTHER( pActiveWeapon, iKillStreakColorIndex, killstreak_idleeffect );
  9099. // !TEST!
  9100. #ifdef STAGING_ONLY
  9101. if ( tf_killstreak_eyeglow.GetInt() != 0 )
  9102. {
  9103. iKillStreakEffectIndex = tf_killstreak_eyeglow.GetInt();
  9104. }
  9105. if ( tf_killstreak_color.GetInt() != 0 )
  9106. {
  9107. iKillStreakColorIndex = tf_killstreak_color.GetInt();
  9108. }
  9109. #endif // staging_only
  9110. // Need a color and eyeeffect to continue, otherwise you only have a sheen at best
  9111. if ( iKillStreakColorIndex == 0 || iKillStreakEffectIndex == 0 )
  9112. {
  9113. // Just in case search weapon wearables for killstreak effects
  9114. // This only applies for soldier (mantreads) and the demoshields
  9115. if ( IsPlayerClass( TF_CLASS_SOLDIER ) || IsPlayerClass( TF_CLASS_DEMOMAN ) )
  9116. {
  9117. // Iterate over all of our wearables
  9118. for ( int i = 0; i < GetNumWearables(); ++i )
  9119. {
  9120. CEconWearable *pWearable = GetWearable( i );
  9121. if ( pWearable && pWearable->GetAttributeContainer( )->GetItem( )->GetEquippedPositionForClass( GetPlayerClass( )->GetClassIndex( ) ) == LOADOUT_POSITION_SECONDARY )
  9122. {
  9123. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWearable, iKillStreakEffectIndex, killstreak_effect );
  9124. CALL_ATTRIB_HOOK_INT_ON_OTHER( pWearable, iKillStreakColorIndex, killstreak_idleeffect );
  9125. break;
  9126. }
  9127. }
  9128. }
  9129. }
  9130. if ( iKillStreakColorIndex == 0 || iKillStreakEffectIndex == 0 )
  9131. {
  9132. m_pszEyeGlowEffectName[0] = '\0';
  9133. m_vEyeGlowColor1.Zero();
  9134. return;
  9135. }
  9136. // Play the pop effect on all kills
  9137. GetVectorColorForParticleSystem( iKillStreakColorIndex, GetTeamNumber() == TF_TEAM_BLUE, false, m_vEyeGlowColor1 );
  9138. GetVectorColorForParticleSystem( iKillStreakColorIndex, GetTeamNumber() == TF_TEAM_BLUE, true, m_vEyeGlowColor2 );
  9139. // Do not render in first person
  9140. if ( !pActiveWeapon->IsFirstPersonView() && bKillScored )
  9141. {
  9142. int iAttachment = 0;
  9143. iAttachment = LookupAttachment( "eyeglow_R" );
  9144. if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
  9145. {
  9146. CNewParticleEffect* pEffect = ParticleProp()->Create( "killstreak_t0_lvl1_flash", PATTACH_POINT_FOLLOW, iAttachment );
  9147. if ( pEffect )
  9148. {
  9149. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, m_vEyeGlowColor1 );
  9150. }
  9151. }
  9152. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_DEMOMAN )
  9153. {
  9154. iAttachment = LookupAttachment( "eyeglow_L" );
  9155. if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
  9156. {
  9157. CNewParticleEffect* pEffect = ParticleProp()->Create( "killstreak_t0_lvl1_flash", PATTACH_POINT_FOLLOW, iAttachment );
  9158. if ( pEffect )
  9159. {
  9160. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, m_vEyeGlowColor1 );
  9161. }
  9162. }
  9163. }
  9164. }
  9165. // only render eye glows if they have enough kills
  9166. if ( iCount < tf_killstreakeyes_minkills.GetInt() || m_Shared.InCond( TF_COND_STEALTHED ) )
  9167. {
  9168. m_pszEyeGlowEffectName[0] = '\0';
  9169. return;
  9170. }
  9171. bool bForceEyeGlow = false;
  9172. #ifdef STAGING_ONLY
  9173. bForceEyeGlow = tf_eyeglow_wip.GetBool();
  9174. #endif
  9175. int iEyeGlowEffectIndex = iKillStreakEffectIndex;
  9176. if ( ( iEyeGlowEffectIndex > 0 && iCount > 0 ) || bForceEyeGlow )
  9177. {
  9178. // 2,4,6,8 for effects
  9179. const char* pszEffect = NULL;
  9180. // Verify the system is in the desired range
  9181. #ifdef STAGING_ONLY
  9182. // Legacy support for when staging had test eye glow indexes
  9183. if ( iEyeGlowEffectIndex < LOW_GLOW )
  9184. {
  9185. iEyeGlowEffectIndex = LOW_GLOW;
  9186. }
  9187. #endif // STAGING_ONLY
  9188. // This is the wrong type, value is too large should not exist so just bail
  9189. if ( iEyeGlowEffectIndex > HIGH_GLOW )
  9190. return;
  9191. // Hack do not have eyeglows for tier0
  9192. if ( iEyeGlowEffectIndex == 2001 )
  9193. return;
  9194. // Should we be using a High_glow
  9195. if ( iCount >= tf_killstreakeyes_maxkills.GetInt() )
  9196. {
  9197. iEyeGlowEffectIndex += HIGH_GLOW;
  9198. }
  9199. attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iEyeGlowEffectIndex );
  9200. if ( pSystem )
  9201. {
  9202. // Check for TeamColor EyeGlows
  9203. if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pSystem->pszSystemName, "_teamcolor_red" ))
  9204. {
  9205. static char pBlue[256];
  9206. V_StrSubst( pSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 );
  9207. pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pBlue );
  9208. }
  9209. else if ( GetTeamNumber() == TF_TEAM_RED && V_stristr( pSystem->pszSystemName, "_teamcolor_blue" ))
  9210. {
  9211. // Guard against accidentally giving out the blue team color (support tool)
  9212. static char pRed[256];
  9213. V_StrSubst( pSystem->pszSystemName, "_teamcolor_blue", "_teamcolor_red", pRed, 256 );
  9214. pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pRed );
  9215. }
  9216. }
  9217. if ( pSystem )
  9218. {
  9219. pszEffect = pSystem->pszSystemName;
  9220. }
  9221. #ifdef STAGING_ONLY
  9222. if ( tf_eyeglow_wip.GetBool() )
  9223. {
  9224. pszEffect = "killstreak_wip";
  9225. }
  9226. #endif // STAGING_ONLY
  9227. if ( pszEffect )
  9228. {
  9229. // Do not render in first person
  9230. if ( !pActiveWeapon->IsFirstPersonView() )
  9231. {
  9232. int iAttachment = 0;
  9233. iAttachment = LookupAttachment( "eyeglow_R" );
  9234. if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
  9235. {
  9236. CNewParticleEffect* pEffect = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, iAttachment );
  9237. if ( pEffect )
  9238. {
  9239. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, m_vEyeGlowColor1 );
  9240. }
  9241. m_pEyeGlowEffect[0] = pEffect;
  9242. }
  9243. // do not put glow on left eye, that is reserved for eyelander
  9244. if ( GetPlayerClass()->GetClassIndex() != TF_CLASS_DEMOMAN )
  9245. {
  9246. iAttachment = LookupAttachment( "eyeglow_L" );
  9247. if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
  9248. {
  9249. CNewParticleEffect* pEffect = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, iAttachment );
  9250. if ( pEffect )
  9251. {
  9252. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, m_vEyeGlowColor1 );
  9253. }
  9254. m_pEyeGlowEffect[1] = pEffect;
  9255. }
  9256. }
  9257. }
  9258. V_strcpy_safe( m_pszEyeGlowEffectName, pszEffect );
  9259. }
  9260. }
  9261. else
  9262. {
  9263. m_pszEyeGlowEffectName[0] = '\0';
  9264. }
  9265. }
  9266. void C_TFPlayer::UpdateMVMEyeGlowEffect( bool bVisible )
  9267. {
  9268. if ( !TFGameRules() || !TFGameRules()->IsMannVsMachineMode() || GetTeamNumber() != TF_TEAM_PVE_INVADERS )
  9269. {
  9270. return;
  9271. }
  9272. // Remove the eye glows
  9273. ParticleProp()->StopParticlesNamed( "bot_eye_glow", true );
  9274. m_pMVMEyeGlowEffect[ 0 ] = NULL;
  9275. m_pMVMEyeGlowEffect[ 1 ] = NULL;
  9276. if ( bVisible )
  9277. {
  9278. // Set color based on skill
  9279. Vector vColor = m_nBotSkill >= 2 ? Vector( 255, 180, 36 ) : Vector( 0, 240, 255 );
  9280. // Create the effects
  9281. int nAttachement = LookupAttachment( IsMiniBoss() ? "eye_boss_1" : "eye_1" );
  9282. if ( nAttachement > 0 )
  9283. {
  9284. m_pMVMEyeGlowEffect[ 0 ] = ParticleProp()->Create( "bot_eye_glow", PATTACH_POINT_FOLLOW, nAttachement );
  9285. if ( m_pMVMEyeGlowEffect[ 0 ] )
  9286. {
  9287. m_pMVMEyeGlowEffect[ 0 ]->SetControlPoint( 1, vColor );
  9288. }
  9289. }
  9290. nAttachement = LookupAttachment( IsMiniBoss() ? "eye_boss_2" : "eye_2" );
  9291. if ( nAttachement > 0 )
  9292. {
  9293. m_pMVMEyeGlowEffect[ 1 ] = ParticleProp()->Create( "bot_eye_glow", PATTACH_POINT_FOLLOW, nAttachement );
  9294. if ( m_pMVMEyeGlowEffect[ 1 ] )
  9295. {
  9296. m_pMVMEyeGlowEffect[ 1 ]->SetControlPoint( 1, vColor );
  9297. }
  9298. }
  9299. }
  9300. }
  9301. //-----------------------------------------------------------------------------
  9302. // Purpose: Check if local player should see spy as disguised body
  9303. //-----------------------------------------------------------------------------
  9304. bool C_TFPlayer::ShouldDrawSpyAsDisguised()
  9305. {
  9306. if ( C_BasePlayer::GetLocalPlayer() && m_Shared.InCond( TF_COND_DISGUISED ) &&
  9307. ( GetEnemyTeam( GetTeamNumber() ) == C_BasePlayer::GetLocalPlayer()->GetTeamNumber() ) )
  9308. {
  9309. if ( m_Shared.GetDisguiseClass() == TF_CLASS_SPY &&
  9310. m_Shared.GetDisguiseTeam() == C_BasePlayer::GetLocalPlayer()->GetTeamNumber() )
  9311. {
  9312. // This enemy is disguised as a friendly spy.
  9313. // Show the spy's original bodygroups.
  9314. return false;
  9315. }
  9316. else
  9317. {
  9318. // This enemy is disguised. Show the disguise body.
  9319. return true;
  9320. }
  9321. }
  9322. return false;
  9323. }
  9324. //-----------------------------------------------------------------------------
  9325. // Purpose:
  9326. //-----------------------------------------------------------------------------
  9327. int C_TFPlayer::GetBody( void )
  9328. {
  9329. if ( ShouldDrawSpyAsDisguised() )
  9330. {
  9331. // This enemy is disguised. Show the disguise body.
  9332. return m_Shared.GetDisguiseBody();
  9333. }
  9334. else
  9335. {
  9336. return BaseClass::GetBody();
  9337. }
  9338. }
  9339. //-----------------------------------------------------------------------------
  9340. // Purpose:
  9341. //-----------------------------------------------------------------------------
  9342. const Vector& C_TFPlayer::GetRenderOrigin( void )
  9343. {
  9344. if ( GetPlayerClass()->HasCustomModel() )
  9345. {
  9346. m_vecCustomModelOrigin = BaseClass::GetRenderOrigin() + GetPlayerClass()->GetCustomModelOffset();
  9347. return m_vecCustomModelOrigin;
  9348. }
  9349. return BaseClass::GetRenderOrigin();
  9350. }
  9351. //-----------------------------------------------------------------------------
  9352. // Purpose:
  9353. //-----------------------------------------------------------------------------
  9354. ConVar tf_taunt_hint_max_distance( "tf_taunt_hint_max_distance", "256", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
  9355. bool C_TFPlayer::ShouldTauntHintIconBeVisible() const
  9356. {
  9357. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  9358. if ( !pLocalTFPlayer || pLocalTFPlayer == this || pLocalTFPlayer->IsTaunting() )
  9359. return false;
  9360. if ( !IsTaunting() || !IsReadyToTauntWithPartner() )
  9361. return false;
  9362. if ( m_Shared.InCond( TF_COND_TAUNTING ) && m_Shared.GetTauntIndex() == TAUNT_LONG )
  9363. {
  9364. return GetAbsOrigin().DistToSqr( pLocalTFPlayer->GetAbsOrigin() ) < Square( tf_taunt_hint_max_distance.GetFloat() );
  9365. }
  9366. return false;
  9367. }
  9368. //-----------------------------------------------------------------------------
  9369. // Purpose:
  9370. //-----------------------------------------------------------------------------
  9371. bool C_TFPlayer::IsHealthBarVisible( void ) const
  9372. {
  9373. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  9374. {
  9375. if ( GetTeamNumber() == TF_TEAM_PVE_INVADERS || m_Shared.InCond( TF_COND_REPROGRAMMED ) )
  9376. {
  9377. float flRegenAmount = 0;
  9378. CALL_ATTRIB_HOOK_FLOAT( flRegenAmount, add_health_regen );
  9379. if ( (int)flRegenAmount != 0 )
  9380. {
  9381. return true;
  9382. }
  9383. }
  9384. }
  9385. #ifdef STAGING_ONLY
  9386. if ( TFGameRules() && TFGameRules()->IsBountyMode() && tf_bountymode_showhealth.GetInt() )
  9387. {
  9388. return true;
  9389. }
  9390. #endif // STAGING_ONLY
  9391. return IsMiniBoss();
  9392. }
  9393. //-----------------------------------------------------------------------------
  9394. // Purpose:
  9395. //-----------------------------------------------------------------------------
  9396. const char* C_TFPlayer::GetBossProgressImageName() const
  9397. {
  9398. if ( m_bUseBossHealthBar )
  9399. {
  9400. return GetPlayerClass()->GetClassIconName();
  9401. }
  9402. return NULL;
  9403. }
  9404. //-----------------------------------------------------------------------------
  9405. // Purpose:
  9406. //-----------------------------------------------------------------------------
  9407. float C_TFPlayer::GetBossStatusProgress() const
  9408. {
  9409. float flProgress = float( GetHealth() ) / float( GetMaxHealth() );
  9410. return flProgress;
  9411. }
  9412. //-----------------------------------------------------------------------------
  9413. // Purpose: Handle karts.
  9414. //-----------------------------------------------------------------------------
  9415. void C_TFPlayer::NotifyShouldTransmit( ShouldTransmitState_t state )
  9416. {
  9417. BaseClass::NotifyShouldTransmit( state );
  9418. if ( state == SHOULDTRANSMIT_START )
  9419. {
  9420. if ( m_Shared.WasInCond( TF_COND_HALLOWEEN_KART ) && m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  9421. {
  9422. // Readd the condition for halloween karts.
  9423. // This deals with the situation documented here:
  9424. // 1) Out of PVS
  9425. // 2) In PVS, add cart
  9426. // 3) Out of PVS, remove cart
  9427. // 4) Returned to PVS, add cart
  9428. //
  9429. // This situation occurs in the halloween 2014 event map, where you are teleported to and from hell
  9430. // Need to research a better fix, but this one works.
  9431. m_Shared.ForceRecondNextSync( TF_COND_HALLOWEEN_KART );
  9432. // Other PVS bugs can probably be fixed here.
  9433. }
  9434. }
  9435. }
  9436. bool C_TFPlayer::IsEffectRateLimited( EBonusEffectFilter_t effect, const C_TFPlayer* pAttacker ) const
  9437. {
  9438. // Check if we're rate limited
  9439. switch( effect )
  9440. {
  9441. case kEffectFilter_AttackerOnly:
  9442. case kEffectFilter_VictimOnly:
  9443. case kEffectFilter_AttackerAndVictimOnly:
  9444. return false;
  9445. case kEffectFilter_AttackerTeam:
  9446. case kEffectFilter_VictimTeam:
  9447. case kEffectFilter_BothTeams:
  9448. // Dont rate limit ourselves
  9449. if( pAttacker == this )
  9450. return false;
  9451. return true;
  9452. default:
  9453. AssertMsg1( 0, "EBonusEffectFilter_t type not handled in %s", __FUNCTION__ );
  9454. return false;
  9455. }
  9456. }
  9457. bool C_TFPlayer::ShouldPlayEffect( EBonusEffectFilter_t filter, const C_TFPlayer* pAttacker, const C_TFPlayer* pVictim ) const
  9458. {
  9459. Assert( pAttacker );
  9460. Assert( pVictim );
  9461. if( !pAttacker || !pVictim )
  9462. return false;
  9463. // Check if the right player relationship
  9464. switch( filter )
  9465. {
  9466. case kEffectFilter_AttackerOnly:
  9467. return ( pAttacker == this );
  9468. case kEffectFilter_AttackerTeam:
  9469. return ( pAttacker->GetTeamNumber() == this->GetTeamNumber() );
  9470. case kEffectFilter_VictimOnly:
  9471. return ( pVictim == this );
  9472. case kEffectFilter_VictimTeam:
  9473. return ( pVictim->GetTeamNumber() == this->GetTeamNumber() );
  9474. case kEffectFilter_AttackerAndVictimOnly:
  9475. return ( pAttacker == this || pVictim == this );
  9476. case kEffectFilter_BothTeams:
  9477. return ( pAttacker->GetTeamNumber() == this->GetTeamNumber() || pVictim->GetTeamNumber() == this->GetTeamNumber() );
  9478. default:
  9479. AssertMsg1( 0, "EBonusEffectFilter_t type not handled in %s", __FUNCTION__ );
  9480. return false;
  9481. };
  9482. }
  9483. //-----------------------------------------------------------------------------
  9484. // Purpose:
  9485. //-----------------------------------------------------------------------------
  9486. void C_TFPlayer::FireGameEvent( IGameEvent *event )
  9487. {
  9488. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  9489. if ( FStrEq( event->GetName(), "player_hurt" ) )
  9490. {
  9491. static BonusEffect_t bonusEffects[] =
  9492. {
  9493. // Sound name Particle name Particle filter Sound filter Sound plays in attacker's ears for them, the world for everyone else
  9494. BonusEffect_t( "TFPlayer.CritHit", "crit_text", kEffectFilter_AttackerOnly, kEffectFilter_AttackerOnly, true ),
  9495. BonusEffect_t( "TFPlayer.CritHitMini", "minicrit_text", kEffectFilter_AttackerOnly, kEffectFilter_AttackerOnly, true ),
  9496. BonusEffect_t( "TFPlayer.DoubleDonk", "doubledonk_text", kEffectFilter_BothTeams, kEffectFilter_BothTeams, true ),
  9497. BonusEffect_t( NULL, "sploosh_text", kEffectFilter_BothTeams, kEffectFilter_BothTeams, true )
  9498. };
  9499. COMPILE_TIME_ASSERT( ARRAYSIZE( bonusEffects ) == kBonusEffect_Count );
  9500. // These only should affect the local player
  9501. if ( !pLocalPlayer || pLocalPlayer != this )
  9502. return;
  9503. // By default we get kBonusEffect_None. We want to use whatever value we get here if it's not kBonusEffect_None.
  9504. // If it's not, then check for crit or minicrit
  9505. EAttackBonusEffects_t eBonusEffect = (EAttackBonusEffects_t)event->GetInt( "bonuseffect", (int)kBonusEffect_None );
  9506. if( eBonusEffect == kBonusEffect_None )
  9507. {
  9508. // Keep reading for these fields to keep replays happy
  9509. eBonusEffect = event->GetBool( "minicrit", false ) ? kBonusEffect_MiniCrit : eBonusEffect;
  9510. eBonusEffect = event->GetBool( "crit", false ) ? kBonusEffect_Crit : eBonusEffect;
  9511. }
  9512. // No effect to show? Bail
  9513. if( eBonusEffect == kBonusEffect_None || eBonusEffect >= kBonusEffect_Count )
  9514. return;
  9515. const int iAttacker = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
  9516. C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( iAttacker ) );
  9517. const int iVictim = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
  9518. C_TFPlayer *pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictim ) );
  9519. // No pointers to players? Bail
  9520. if( !pAttacker || !pVictim )
  9521. return;
  9522. bool bShowDisguisedCrit = event->GetBool( "showdisguisedcrit", 0 );
  9523. // Victim is disguised and we're not showing disguised effects? Bail
  9524. if ( pVictim->m_Shared.InCond( TF_COND_DISGUISED ) && !bShowDisguisedCrit )
  9525. return;
  9526. // Victim is carrying Resist Powerup, which is immune to crit damage
  9527. if ( pVictim && pVictim->m_Shared.GetCarryingRuneType() == RUNE_RESIST &&
  9528. ( eBonusEffect == kBonusEffect_Crit || eBonusEffect == kBonusEffect_MiniCrit ) )
  9529. {
  9530. return;
  9531. }
  9532. // Support old system. If "allseecrit" is set that means we want this to show for our whole team.
  9533. EBonusEffectFilter_t eParticleFilter = bonusEffects[ eBonusEffect ].m_eParticleFilter;
  9534. EBonusEffectFilter_t eSoundFilter = bonusEffects[ eBonusEffect ].m_eSoundFilter;
  9535. if( event->GetBool( "allseecrit", false ) )
  9536. {
  9537. eParticleFilter = kEffectFilter_AttackerTeam;
  9538. eSoundFilter = kEffectFilter_AttackerTeam;
  9539. }
  9540. // Check if either of our effects are rate limited
  9541. if( IsEffectRateLimited( eParticleFilter, pAttacker ) || IsEffectRateLimited( eSoundFilter, pAttacker ) )
  9542. {
  9543. // Check if we're cooling down
  9544. if( !pVictim->CanDisplayAllSeeEffect( eBonusEffect ) )
  9545. {
  9546. // Too often! Return
  9547. return;
  9548. }
  9549. // Set cooldown period
  9550. pVictim->SetNextAllSeeEffectTime( eBonusEffect, gpGlobals->curtime + 0.5f );
  9551. }
  9552. ConVarRef hud_combattext( "hud_combattext", false );
  9553. ConVarRef hud_combattext_doesnt_block_overhead_text( "hud_combattext_doesnt_block_overhead_text", false );
  9554. bool bCombatTextBlocks = hud_combattext.GetBool() && !hud_combattext_doesnt_block_overhead_text.GetBool();
  9555. // Show the effect, unless combat text blocks
  9556. if( ShouldPlayEffect( eParticleFilter, pAttacker, pVictim ) && !bCombatTextBlocks )
  9557. {
  9558. pVictim->ParticleProp()->Create( bonusEffects[ eBonusEffect ].m_pszParticleName, PATTACH_POINT_FOLLOW, "head" );
  9559. }
  9560. // Play the sound!
  9561. if( ShouldPlayEffect( eSoundFilter, pAttacker, pVictim ) && bonusEffects[ eBonusEffect ].m_pszSoundName != NULL )
  9562. {
  9563. const bool& bPlayInAttackersEars = bonusEffects[ eBonusEffect ].m_bPlaySoundInAttackersEars;
  9564. // sound effects
  9565. EmitSound_t params;
  9566. params.m_flSoundTime = 0;
  9567. params.m_pflSoundDuration = 0;
  9568. params.m_pSoundName = bonusEffects[ eBonusEffect ].m_pszSoundName;
  9569. CPASFilter filter( pVictim->GetAbsOrigin() );
  9570. if( bPlayInAttackersEars && pAttacker == this )
  9571. {
  9572. // Don't let the attacker hear this version if its to be played in their ears
  9573. filter.RemoveRecipient( pAttacker );
  9574. // Play a sound in the ears of the attacker
  9575. CSingleUserRecipientFilter attackerFilter( pAttacker );
  9576. EmitSound( attackerFilter, pAttacker->entindex(), params );
  9577. }
  9578. EmitSound( filter, pVictim->entindex(), params );
  9579. }
  9580. }
  9581. else if ( FStrEq( event->GetName(), "hltv_changed_mode" ) )
  9582. {
  9583. int iTarget = event->GetInt( "obs_target" );
  9584. if ( iTarget == entindex() )
  9585. {
  9586. int iOld = event->GetInt( "oldmode" );
  9587. int iNew = event->GetInt( "newmode" );
  9588. if ( iOld == OBS_MODE_IN_EYE || iNew == OBS_MODE_IN_EYE )
  9589. {
  9590. C_TFWeaponBase *pWeapon = m_Shared.GetActiveTFWeapon();
  9591. if ( pWeapon )
  9592. {
  9593. pWeapon->UpdateAttachmentModels();
  9594. }
  9595. // Update visibility of any worn items.
  9596. UpdateWearables();
  9597. SetBodygroupsDirty();
  9598. }
  9599. }
  9600. }
  9601. else if ( FStrEq( event->GetName(), "hltv_changed_target" ) )
  9602. {
  9603. int iOldTarget = event->GetInt( "old_target" );
  9604. int iTarget = event->GetInt( "obs_target" );
  9605. if ( iTarget == entindex() || iOldTarget == entindex() )
  9606. {
  9607. int iMode = event->GetInt( "mode" );
  9608. if ( iMode == OBS_MODE_IN_EYE )
  9609. {
  9610. C_TFWeaponBase *pWeapon = m_Shared.GetActiveTFWeapon();
  9611. if ( pWeapon )
  9612. {
  9613. pWeapon->UpdateAttachmentModels();
  9614. }
  9615. }
  9616. // Update visibility of any worn items.
  9617. UpdateWearables();
  9618. SetBodygroupsDirty();
  9619. }
  9620. }
  9621. else if ( FStrEq( event->GetName(), "post_inventory_application" ) )
  9622. {
  9623. const int iPlayer = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
  9624. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayer ) );
  9625. if ( pPlayer )
  9626. {
  9627. pPlayer->SetBodygroupsDirty();
  9628. }
  9629. }
  9630. else if ( FStrEq( event->GetName(), "rocket_jump" ) || FStrEq( event->GetName(), "sticky_jump" ) )
  9631. {
  9632. // Play a special sound when blast jumping with weapons that don't hurt the player
  9633. const int iUserID = event->GetInt( "userid" );
  9634. bool bWhistle = event->GetBool( "playsound" );
  9635. if ( bWhistle && GetUserID() == iUserID )
  9636. {
  9637. if ( !m_pBlastJumpLoop )
  9638. {
  9639. CBroadcastRecipientFilter filter;
  9640. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  9641. m_pBlastJumpLoop = controller.SoundCreate( filter, entindex(), "BlastJump.Whistle" );
  9642. controller.Play( m_pBlastJumpLoop, 0.25, 200 );
  9643. m_flBlastJumpLaunchTime = gpGlobals->curtime;
  9644. }
  9645. }
  9646. }
  9647. else if ( FStrEq( event->GetName(), "player_spawn" ) )
  9648. {
  9649. StopBlastJumpLoopSound( event->GetInt( "userid" ) );
  9650. const int iUserID = event->GetInt( "userid" );
  9651. if ( pLocalPlayer && GetUserID() == pLocalPlayer->GetUserID() && iUserID == pLocalPlayer->GetUserID() )
  9652. {
  9653. // ADD EconNotification to equip spellbook here
  9654. if ( TFGameRules() && TFGameRules()->IsUsingSpells() )
  9655. {
  9656. int iCount = NotificationQueue_Count( &CEquipSpellbookNotification::IsNotificationType );
  9657. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( event->GetInt( "class"), LOADOUT_POSITION_ACTION );
  9658. // no spell book
  9659. if ( !pItem || !pItem->GetStaticData()->GetItemClass() || !FStrEq( pItem->GetStaticData()->GetItemClass(), "tf_weapon_spellbook" ) )
  9660. {
  9661. if ( iCount == 0 )
  9662. {
  9663. CEquipSpellbookNotification *pNotification = new CEquipSpellbookNotification();
  9664. pNotification->SetText( "#TF_SpellBook_EquipAction" );
  9665. pNotification->SetLifetime( 10.0f );
  9666. NotificationQueue_Add( pNotification );
  9667. }
  9668. }
  9669. else
  9670. {
  9671. NotificationQueue_Remove( &CEquipSpellbookNotification::IsNotificationType );
  9672. }
  9673. }
  9674. // ADD EconNotification to equip grapplinghook here
  9675. else if ( TFGameRules() && TFGameRules()->IsUsingGrapplingHook() )
  9676. {
  9677. int iCount = NotificationQueue_Count( &CEquipGrapplingHookNotification::IsNotificationType );
  9678. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( event->GetInt( "class"), LOADOUT_POSITION_ACTION );
  9679. // no spell book
  9680. if ( !pItem || !pItem->GetStaticData()->GetItemClass() || !FStrEq( pItem->GetStaticData()->GetItemClass(), "tf_weapon_grapplinghook" ) )
  9681. {
  9682. if ( iCount == 0 )
  9683. {
  9684. CEquipGrapplingHookNotification *pNotification = new CEquipGrapplingHookNotification();
  9685. pNotification->SetText( "#TF_GrapplingHook_EquipAction" );
  9686. pNotification->SetLifetime( 10.0f );
  9687. NotificationQueue_Add( pNotification );
  9688. }
  9689. }
  9690. else
  9691. {
  9692. NotificationQueue_Remove( &CEquipGrapplingHookNotification::IsNotificationType );
  9693. }
  9694. }
  9695. // Add EconNotification to equip Canteen here
  9696. else if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  9697. {
  9698. int iCount = NotificationQueue_Count( &CEquipMvMCanteenNotification::IsNotificationType );
  9699. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( event->GetInt( "class" ), LOADOUT_POSITION_ACTION );
  9700. // no spell book
  9701. if ( !pItem || !pItem->GetStaticData()->GetItemClass() || !FStrEq( pItem->GetStaticData()->GetItemClass(), "tf_powerup_bottle" ) )
  9702. {
  9703. if ( iCount == 0 )
  9704. {
  9705. CEquipMvMCanteenNotification *pNotification = new CEquipMvMCanteenNotification();
  9706. pNotification->SetText( "#TF_Canteen_EquipAction" );
  9707. pNotification->SetLifetime( 10.0f );
  9708. NotificationQueue_Add( pNotification );
  9709. }
  9710. }
  9711. else
  9712. {
  9713. NotificationQueue_Remove( &CEquipMvMCanteenNotification::IsNotificationType );
  9714. }
  9715. }
  9716. }
  9717. }
  9718. else if ( FStrEq( event->GetName(), "rocket_jump_landed" ) || FStrEq( event->GetName(), "sticky_jump_landed" ) )
  9719. {
  9720. StopBlastJumpLoopSound( event->GetInt( "userid" ) );
  9721. }
  9722. else if( FStrEq( event->GetName(), "damage_resisted" ) )
  9723. {
  9724. const int index_ = event->GetInt( "entindex" );
  9725. if ( index_ == entindex() )
  9726. {
  9727. m_flLastResistTime = gpGlobals->curtime;
  9728. }
  9729. }
  9730. else if ( FStrEq( event->GetName(), "revive_player_notify" ) )
  9731. {
  9732. if ( !pLocalPlayer )
  9733. return;
  9734. const int index_ = event->GetInt( "entindex" );
  9735. if ( pLocalPlayer == this && entindex() == index_ && !m_hRevivePrompt )
  9736. {
  9737. const int nMarkerIndex = event->GetInt( "marker_entindex" );
  9738. CBaseEntity *pMarker = ClientEntityList().GetEnt( nMarkerIndex );
  9739. if ( pMarker )
  9740. {
  9741. m_hRevivePrompt = ShowRevivePrompt( pMarker, "#TF_Prompt_Revive_Title", "#TF_Prompt_Revive_Message", "#TF_Prompt_Revive_Cancel", &PromptAcceptReviveCallback, NULL, NULL );
  9742. if ( m_hRevivePrompt )
  9743. {
  9744. m_hRevivePrompt->SetKeyBoardInputEnabled( false );
  9745. }
  9746. }
  9747. }
  9748. }
  9749. else if ( FStrEq( event->GetName(), "revive_player_stopped" ) )
  9750. {
  9751. if ( !pLocalPlayer )
  9752. return;
  9753. if ( m_hRevivePrompt )
  9754. {
  9755. m_hRevivePrompt->MarkForDeletion();
  9756. m_hRevivePrompt = NULL;
  9757. }
  9758. }
  9759. else if ( FStrEq( event->GetName(), "player_changeclass" ) )
  9760. {
  9761. if ( TFGameRules() && TFGameRules()->IsMatchTypeCompetitive() )
  9762. {
  9763. if ( g_PR &&
  9764. pLocalPlayer &&
  9765. pLocalPlayer == this &&
  9766. TFGameRules() &&
  9767. TFGameRules()->IsCompetitiveMode() &&
  9768. TFGameRules()->State_Get() == GR_STATE_RND_RUNNING )
  9769. {
  9770. CBaseHudChat *pHudChat = (CBaseHudChat*)GET_HUDELEMENT( CHudChat );
  9771. if ( pHudChat )
  9772. {
  9773. C_BasePlayer *pEventPlayer = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  9774. if ( pEventPlayer && pLocalPlayer->GetTeamNumber() == g_PR->GetTeam( pEventPlayer->entindex() ) )
  9775. {
  9776. int nClassID = event->GetInt( "class" );
  9777. if ( nClassID >= 0 && nClassID < ARRAYSIZE( g_aPlayerClassNames ) )
  9778. {
  9779. wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH];
  9780. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( pEventPlayer->entindex() ), wszPlayerName, sizeof( wszPlayerName ) );
  9781. wchar_t wszLocalized[100];
  9782. g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Class_Change" ), 2, wszPlayerName, g_pVGuiLocalize->Find( g_aPlayerClassNames[nClassID] ) );
  9783. char szLocalized[100];
  9784. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof( szLocalized ) );
  9785. pHudChat->ChatPrintf( pLocalPlayer->entindex(), CHAT_FILTER_NAMECHANGE, "%s", szLocalized );
  9786. }
  9787. }
  9788. }
  9789. }
  9790. }
  9791. }
  9792. else if ( FStrEq( event->GetName(), "player_abandoned_match" ) )
  9793. {
  9794. if ( pLocalPlayer && pLocalPlayer == this )
  9795. {
  9796. wchar_t wzNotification[1024] = L"";
  9797. const wchar_t *pwzTitle = g_pVGuiLocalize->Find( "#TF_Competitive_Abandoned" );
  9798. g_pVGuiLocalize->ConstructString_safe( wzNotification, pwzTitle, 0 );
  9799. if ( event->GetBool( "game_over" ) )
  9800. {
  9801. ShowMessageBox( "#TF_Competitive_AbandonedTitle", wzNotification, "#GameUI_OK" );
  9802. }
  9803. else
  9804. {
  9805. CBaseHudChat *pHudChat = (CBaseHudChat*)GET_HUDELEMENT( CHudChat );
  9806. if ( pHudChat )
  9807. {
  9808. char szLocalized[1024];
  9809. g_pVGuiLocalize->ConvertUnicodeToANSI( wzNotification, szLocalized, sizeof( szLocalized ) );
  9810. pHudChat->ChatPrintf( pLocalPlayer->entindex(), CHAT_FILTER_SERVERMSG, "%s", szLocalized );
  9811. }
  9812. }
  9813. }
  9814. }
  9815. #ifdef STAGING_ONLY
  9816. else if ( FStrEq( event->GetName(), "player_death" ) && tf_random_item_min.GetInt() > 0 && tf_random_item_max.GetInt() > tf_random_item_min.GetInt() )
  9817. {
  9818. const int iUserID = event->GetInt( "userid" );
  9819. if ( pLocalPlayer && GetUserID() == pLocalPlayer->GetUserID() && iUserID == pLocalPlayer->GetUserID() )
  9820. {
  9821. // Give random items and tell the user there items have been changed
  9822. // Get a list of all cosmetics for this class in this item range
  9823. CUtlVector<CEconItemView*> vecItemViews;
  9824. //GetItemDef
  9825. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  9826. if ( !pLocalInv )
  9827. return;
  9828. int iClass = GetPlayerClass()->GetClassIndex();
  9829. for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
  9830. {
  9831. CEconItemView *pItem = pLocalInv->GetItem( i );
  9832. const GameItemDefinition_t *pItemDef = pItem->GetItemDefinition();
  9833. if ( IsMiscSlot( pItemDef->GetLoadoutSlot( iClass ) ) && pItemDef->GetDefinitionIndex() >= tf_random_item_min.GetInt() && pItemDef->GetDefinitionIndex() <= tf_random_item_max.GetInt() )
  9834. {
  9835. vecItemViews.AddToTail( pItem );
  9836. }
  9837. }
  9838. equip_region_mask_t unCumulativeRegionMask = 0;
  9839. // Unequip everything
  9840. InventoryManager()->UpdateInventoryEquippedState( pLocalInv, INVALID_ITEM_ID, iClass, LOADOUT_POSITION_HEAD );
  9841. InventoryManager()->UpdateInventoryEquippedState( pLocalInv, INVALID_ITEM_ID, iClass, LOADOUT_POSITION_MISC );
  9842. InventoryManager()->UpdateInventoryEquippedState( pLocalInv, INVALID_ITEM_ID, iClass, LOADOUT_POSITION_MISC2 );
  9843. // pick a random item slot2
  9844. for ( int iSlot = 0; iSlot < 3; ++iSlot )
  9845. {
  9846. int iLoadoutPos = LOADOUT_POSITION_HEAD;
  9847. switch ( iSlot )
  9848. {
  9849. case 0: iLoadoutPos = LOADOUT_POSITION_HEAD; break;
  9850. case 1: iLoadoutPos = LOADOUT_POSITION_MISC; break;
  9851. case 2: iLoadoutPos = LOADOUT_POSITION_MISC2; break;
  9852. }
  9853. int iRandomItem = RandomInt( 0, vecItemViews.Count() - 1 );
  9854. for ( int i = 0; i < vecItemViews.Count(); ++i )
  9855. {
  9856. CEconItemView *pItemView = vecItemViews[( iRandomItem + i ) % vecItemViews.Count()];
  9857. equip_region_mask_t unItemEquipMask = pItemView->GetItemDefinition()->GetEquipRegionMask();
  9858. if ( !( unItemEquipMask & unCumulativeRegionMask ) )
  9859. {
  9860. TFInventoryManager()->EquipItemInLoadout( pLocalPlayer->GetPlayerClass()->GetClassIndex(), iLoadoutPos, pItemView->GetID() );
  9861. vecItemViews.Remove( ( iRandomItem + i ) % vecItemViews.Count() );
  9862. unCumulativeRegionMask |= unItemEquipMask;
  9863. break;
  9864. }
  9865. }
  9866. }
  9867. // Notify player there items have changed
  9868. CEconNotification *pNotification = new CEconNotification();
  9869. pNotification->SetText( "#TF_Test_ItemsChanged" );
  9870. pNotification->SetLifetime( 6.0f );
  9871. NotificationQueue_Add( pNotification );
  9872. }
  9873. }
  9874. #endif // STAGING_ONLY
  9875. BaseClass::FireGameEvent( event );
  9876. }
  9877. const char* C_TFPlayer::ModifyEventParticles( const char* token )
  9878. {
  9879. if ( GetPlayerClass()->IsClass( TF_CLASS_SCOUT ) )
  9880. {
  9881. if ( !Q_strcmp( token, "doublejump_puff" ) )
  9882. {
  9883. if ( m_Shared.GetAirDash() > 1 )
  9884. {
  9885. return "doublejump_puff_alt";
  9886. }
  9887. }
  9888. }
  9889. return BaseClass::ModifyEventParticles( token );
  9890. }
  9891. void C_TFPlayer::SetTauntCameraTargets( float back, float up )
  9892. {
  9893. m_flTauntCamTargetDist = back;
  9894. m_flTauntCamTargetDistUp = up;
  9895. // Force this on
  9896. m_bTauntInterpolating = true;
  9897. }
  9898. CampaignMedalDisplayType_t C_TFPlayer::GetCampaignMedalType( void )
  9899. {
  9900. // static CSchemaItemDefHandle pItemDef_Summer2015Operation( "Activated Summer 2015 Operation Pass" );
  9901. // static CSchemaItemDefHandle pItemDef_InvasionPass( "Activated Invasion Pass" );
  9902. // static CSchemaItemDefHandle pItemDef_HalloweenPass( "Activated Halloween Pass" );
  9903. // static CSchemaItemDefHandle pItemDef_Winter2016Pass( "Activated Operation Tough Break Pass" );
  9904. CampaignMedalDisplayType_t retVal = CAMPAIGN_MEDAL_DISPLAY_TYPE_NONE;
  9905. /*
  9906. if ( HasCampaignMedal( CAMPAIGN_MEDAL_WINTER2016 ) )
  9907. {
  9908. CTFPlayerInventory *pInv = Inventory();
  9909. if ( pInv )
  9910. {
  9911. for ( int i = 0; i < pInv->GetItemCount(); ++i )
  9912. {
  9913. CEconItemView *pItem = pInv->GetItem( i );
  9914. if ( pItem && ( pItem->GetItemDefinition() == pItemDef_Winter2016Pass ) )
  9915. {
  9916. style_index_t iStyle = pItem->GetItemStyle();
  9917. if ( iStyle != INVALID_STYLE_INDEX )
  9918. {
  9919. iStyle += ( ( entindex()%2 < 1 ) ? CAMPAIGN_MEDAL_DISPLAY_TYPE_WINTER2016_GRAVEL1 : CAMPAIGN_MEDAL_DISPLAY_TYPE_WINTER2016_GRAVEL2 ); // styles start at 0 and Winter2016 images start at CAMPAIGN_MEDAL_DISPLAY_TYPE_WINTER2016_GRAVEL1
  9920. if ( ( iStyle >= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_WINTER2016_GRAVEL1 ) && ( iStyle <= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_WINTER2016_GOLD2 ) )
  9921. {
  9922. retVal = (CampaignMedalDisplayType_t)iStyle;
  9923. break;
  9924. }
  9925. }
  9926. }
  9927. }
  9928. }
  9929. }
  9930. if ( IsPlayingInvasionMap() && HasCampaignMedal( CAMPAIGN_MEDAL_INVASION ) )
  9931. {
  9932. retVal = CAMPAIGN_MEDAL_DISPLAY_TYPE_INVASION;
  9933. }
  9934. else if ( HasCampaignMedal( CAMPAIGN_MEDAL_HALLOWEEN ) )
  9935. {
  9936. CTFPlayerInventory *pInv = Inventory();
  9937. if ( pInv )
  9938. {
  9939. for ( int i = 0; i < pInv->GetItemCount(); ++i )
  9940. {
  9941. CEconItemView *pItem = pInv->GetItem( i );
  9942. if ( pItem && ( pItem->GetItemDefinition() == pItemDef_HalloweenPass ) )
  9943. {
  9944. style_index_t iStyle = pItem->GetItemStyle();
  9945. if ( iStyle != INVALID_STYLE_INDEX )
  9946. {
  9947. iStyle += CAMPAIGN_MEDAL_DISPLAY_TYPE_HALLOWEEN_GRAVEL; // styles start at 0 and Halloween images start at CAMPAIGN_MEDAL_DISPLAY_TYPE_HALLOWEEN_GRAVEL
  9948. if ( ( iStyle >= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_HALLOWEEN_GRAVEL ) && ( iStyle <= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_HALLOWEEN_GOLD ) )
  9949. {
  9950. retVal = (CampaignMedalDisplayType_t)iStyle;
  9951. break;
  9952. }
  9953. }
  9954. }
  9955. }
  9956. }
  9957. }
  9958. else if ( HasCampaignMedal( CAMPAIGN_MEDAL_SUMMER2015 ) )
  9959. {
  9960. CTFPlayerInventory *pInv = Inventory();
  9961. if ( pInv )
  9962. {
  9963. for ( int i = 0; i < pInv->GetItemCount(); ++i )
  9964. {
  9965. CEconItemView *pItem = pInv->GetItem( i );
  9966. if ( pItem && ( pItem->GetItemDefinition() == pItemDef_Summer2015Operation ) )
  9967. {
  9968. style_index_t iStyle = pItem->GetItemStyle();
  9969. if ( iStyle != INVALID_STYLE_INDEX )
  9970. {
  9971. iStyle += 1; // styles start at 0 and images start at 1
  9972. if ( ( iStyle >= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_SUMMER2015_GRAVEL ) && ( iStyle <= (style_index_t)CAMPAIGN_MEDAL_DISPLAY_TYPE_SUMMER2015_GOLD ) )
  9973. {
  9974. retVal = (CampaignMedalDisplayType_t)iStyle;
  9975. break;
  9976. }
  9977. }
  9978. }
  9979. }
  9980. }
  9981. }*/
  9982. return retVal;
  9983. }
  9984. const char *C_TFPlayer::GetCampaignMedalImage( void )
  9985. {
  9986. return g_pszCampaignMedalIcons[GetCampaignMedalType()];
  9987. }
  9988. void C_TFPlayer::UpdateGlowEffect( void )
  9989. {
  9990. DestroyGlowEffect();
  9991. BaseClass::UpdateGlowEffect();
  9992. // create a new effect if we have a coach
  9993. if ( m_hCoach && m_hCoach->IsLocalPlayer() && m_hCoach->m_bIsCoaching )
  9994. {
  9995. float r, g, b;
  9996. GetGlowEffectColor( &r, &g, &b );
  9997. m_pStudentGlowEffect = new CGlowObject( this, Vector( r, g, b ), 1.0, true );
  9998. }
  9999. // create a power up effect if needed
  10000. if ( ShouldShowPowerupGlowEffect() )
  10001. {
  10002. float r, g, b;
  10003. GetPowerupGlowEffectColor( &r, &g, &b );
  10004. m_pPowerupGlowEffect = new CGlowObject( this, Vector( r, g, b ), 1.0, true );
  10005. }
  10006. }
  10007. void C_TFPlayer::DestroyGlowEffect( void )
  10008. {
  10009. BaseClass::DestroyGlowEffect();
  10010. if ( m_pStudentGlowEffect )
  10011. {
  10012. delete m_pStudentGlowEffect;
  10013. m_pStudentGlowEffect = NULL;
  10014. }
  10015. if ( m_pPowerupGlowEffect )
  10016. {
  10017. delete m_pPowerupGlowEffect;
  10018. m_pPowerupGlowEffect = NULL;
  10019. }
  10020. }
  10021. //-----------------------------------------------------------------------------
  10022. // Purpose:
  10023. //-----------------------------------------------------------------------------
  10024. void C_TFPlayer::UpdateGlowColor( void )
  10025. {
  10026. CGlowObject* pGlowObject = GetGlowObject();
  10027. if ( pGlowObject )
  10028. {
  10029. float r, g, b;
  10030. GetGlowEffectColor( &r, &g, &b );
  10031. pGlowObject->SetColor( Vector( r, g, b ) );
  10032. }
  10033. if ( m_pPowerupGlowEffect )
  10034. {
  10035. float r, g, b;
  10036. GetPowerupGlowEffectColor( &r, &g, &b );
  10037. m_pPowerupGlowEffect->SetColor( Vector( r, g, b ) );
  10038. }
  10039. }
  10040. //-----------------------------------------------------------------------------
  10041. // Purpose:
  10042. //-----------------------------------------------------------------------------
  10043. void C_TFPlayer::GetGlowEffectColor( float *r, float *g, float *b )
  10044. {
  10045. #ifdef TF_CREEP_MODE
  10046. if ( TFGameRules() && TFGameRules()->IsCreepWaveMode() )
  10047. {
  10048. if ( GetTeamNumber() == TF_TEAM_RED )
  10049. {
  10050. *r = 255;
  10051. *g = 0;
  10052. *b = 0;
  10053. }
  10054. else
  10055. {
  10056. *r = 0;
  10057. *g = 0;
  10058. *b = 255;
  10059. }
  10060. return;
  10061. }
  10062. #endif // TF_CREEP_MODE
  10063. int nTeam = GetTeamNumber();
  10064. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  10065. // In CTF, show health color glow for alive player
  10066. if ( pLocalPlayer && pLocalPlayer->IsAlive() && TFGameRules() && ( TFGameRules()->GetGameType() == TF_GAMETYPE_CTF ) && HasTheFlag() )
  10067. {
  10068. float flHealth = (float)GetHealth() / (float)GetMaxHealth();
  10069. if ( flHealth > 0.6 )
  10070. {
  10071. *r = 0.33f;
  10072. *g = 0.75f;
  10073. *b = 0.23f;
  10074. }
  10075. else if( flHealth > 0.3 )
  10076. {
  10077. *r = 0.75f;
  10078. *g = 0.72f;
  10079. *b = 0.23f;
  10080. }
  10081. else
  10082. {
  10083. *r = 0.75f;
  10084. *g = 0.23f;
  10085. *b = 0.23f;
  10086. }
  10087. return;
  10088. }
  10089. if ( !engine->IsHLTV() && ( GetLocalPlayerTeam() >= FIRST_GAME_TEAM ) )
  10090. {
  10091. if ( IsPlayerClass( TF_CLASS_SPY ) && m_Shared.InCond( TF_COND_DISGUISED ) && ( GetTeamNumber() != GetLocalPlayerTeam() ) )
  10092. {
  10093. nTeam = m_Shared.GetDisguiseTeam();
  10094. }
  10095. }
  10096. TFGameRules()->GetTeamGlowColor( nTeam, *r, *g, *b );
  10097. }
  10098. //-----------------------------------------------------------------------------
  10099. // Purpose:
  10100. //-----------------------------------------------------------------------------
  10101. bool C_TFPlayer::ShouldShowPowerupGlowEffect()
  10102. {
  10103. // should local player see enemy glow with powerup related
  10104. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  10105. if ( pLocalPlayer->IsAlive() && this != pLocalPlayer && GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
  10106. {
  10107. // give advantage to local player who doesn't have rune to fight against enemy with rune by glowing their health
  10108. if ( m_Shared.IsCarryingRune() && !pLocalPlayer->m_Shared.IsCarryingRune() )
  10109. {
  10110. // only show glow when the enemy is lower than 30% HP
  10111. float flHealth = ( float )GetHealth() / ( float )GetMaxHealth();
  10112. return flHealth <= 0.3 && pLocalPlayer->IsLineOfSightClear( this, IGNORE_ACTORS );
  10113. }
  10114. // local player with supernova can see enemy glow within supernova range
  10115. else if ( pLocalPlayer->m_Shared.GetCarryingRuneType() == RUNE_SUPERNOVA && pLocalPlayer->m_Shared.IsRuneCharged() && !m_Shared.IsStealthed() )
  10116. {
  10117. const float flEffectRadiusSqr = Sqr( 1500.f );
  10118. Vector toPlayer = WorldSpaceCenter() - pLocalPlayer->WorldSpaceCenter();
  10119. return toPlayer.LengthSqr() <= flEffectRadiusSqr && pLocalPlayer->IsLineOfSightClear( this, IGNORE_ACTORS );
  10120. }
  10121. }
  10122. return false;
  10123. }
  10124. //-----------------------------------------------------------------------------
  10125. // Purpose:
  10126. //-----------------------------------------------------------------------------
  10127. void C_TFPlayer::GetPowerupGlowEffectColor( float *r, float *g, float *b )
  10128. {
  10129. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  10130. // no need to add extra logics here. we already know that other players are glowing from SUPERNOVA
  10131. if ( pLocalPlayer->m_Shared.GetCarryingRuneType() == RUNE_SUPERNOVA )
  10132. {
  10133. *r = 255;
  10134. *g = 255;
  10135. *b = 0;
  10136. }
  10137. else
  10138. {
  10139. GetGlowEffectColor( r, g, b );
  10140. }
  10141. }
  10142. //-----------------------------------------------------------------------------
  10143. // Purpose:
  10144. //-----------------------------------------------------------------------------
  10145. static ConVar tf_inspect_hint_count( "tf_inspect_hint_count", "0", FCVAR_ARCHIVE );
  10146. void C_TFPlayer::HandleInspectHint()
  10147. {
  10148. int nNotifyCount = tf_inspect_hint_count.GetInt();
  10149. if ( nNotifyCount > 10 )
  10150. return;
  10151. if ( m_bNotifiedWeaponInspectThisLife )
  10152. return;
  10153. CHudNotificationPanel *pNotifyPanel = GET_HUDELEMENT( CHudNotificationPanel );
  10154. if ( pNotifyPanel )
  10155. {
  10156. wchar_t szNotification[1024]=L"";
  10157. wchar_t wKeyBind[80] = L"";
  10158. const wchar_t *wpszFormat = g_pVGuiLocalize->Find( "#Hint_inspect_weapon" );
  10159. if ( wpszFormat )
  10160. {
  10161. const char *key = engine->Key_LookupBinding( "+inspect" );
  10162. if ( !key || FStrEq( key, "(null)" ) )
  10163. {
  10164. key = "< not bound >";
  10165. }
  10166. g_pVGuiLocalize->ConvertANSIToUnicode( key, wKeyBind, sizeof( wKeyBind ) );
  10167. g_pVGuiLocalize->ConstructString_safe( szNotification, wpszFormat, 1, wKeyBind );
  10168. pNotifyPanel->SetupNotifyCustom( szNotification, "", GetTeamNumber() );
  10169. tf_inspect_hint_count.SetValue( nNotifyCount + 1 );
  10170. }
  10171. m_bNotifiedWeaponInspectThisLife = true;
  10172. }
  10173. }
  10174. //-----------------------------------------------------------------------------
  10175. // Purpose:
  10176. //-----------------------------------------------------------------------------
  10177. bool C_TFPlayer::AddOverheadEffect( const char *pszEffectName )
  10178. {
  10179. int index_ = m_mapOverheadEffects.Find( pszEffectName );
  10180. // particle is added already
  10181. if ( index_ != m_mapOverheadEffects.InvalidIndex() )
  10182. return false;
  10183. CNewParticleEffect *pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, 0, GetOverheadEffectPosition() );
  10184. if ( pEffect )
  10185. {
  10186. if ( m_mapOverheadEffects.Count() == 0 )
  10187. {
  10188. m_flOverheadEffectStartTime = gpGlobals->curtime;
  10189. }
  10190. m_mapOverheadEffects.Insert( pszEffectName, pEffect );
  10191. return true;
  10192. }
  10193. return false;
  10194. }
  10195. //-----------------------------------------------------------------------------
  10196. // Purpose:
  10197. //-----------------------------------------------------------------------------
  10198. void C_TFPlayer::RemoveOverheadEffect( const char *pszEffectName, bool bRemoveInstantly )
  10199. {
  10200. int index_ = m_mapOverheadEffects.Find( pszEffectName );
  10201. // particle is added already
  10202. if ( index_ != m_mapOverheadEffects.InvalidIndex() )
  10203. {
  10204. if ( bRemoveInstantly )
  10205. ParticleProp()->StopEmissionAndDestroyImmediately( m_mapOverheadEffects[index_] );
  10206. ParticleProp()->StopParticlesNamed( pszEffectName, bRemoveInstantly );
  10207. m_mapOverheadEffects.RemoveAt( index_ );
  10208. }
  10209. }
  10210. void C_TFPlayer::UpdateOverheadEffects()
  10211. {
  10212. if ( IsLocalPlayer() )
  10213. return;
  10214. const int nOverheadEffectCount = m_mapOverheadEffects.Count();
  10215. if ( nOverheadEffectCount == 0 )
  10216. return;
  10217. Vector vecOverheadEffectPosition = GetOverheadEffectPosition();
  10218. C_TFPlayer *pLocalPlayer = GetLocalTFPlayer();
  10219. if ( !pLocalPlayer )
  10220. return;
  10221. Vector vecHeadToHead = EyePosition() - pLocalPlayer->EyePosition();
  10222. const float flEffectGap = 24.f;
  10223. Vector vecRightOffset = CrossProduct( vecHeadToHead, Vector( 0, 0, 1 ) ).Normalized();
  10224. float flFirstEffectOffset = -flEffectGap * 0.5f * ( nOverheadEffectCount - 1 );
  10225. int iValidParticleIndex = 0;
  10226. FOR_EACH_MAP_FAST( m_mapOverheadEffects, i )
  10227. {
  10228. HPARTICLEFFECT hEffect = m_mapOverheadEffects[i];
  10229. if ( hEffect )
  10230. {
  10231. float flCurrentOffset = flFirstEffectOffset + flEffectGap * iValidParticleIndex;
  10232. Vector vecOffset = vecOverheadEffectPosition + flCurrentOffset * vecRightOffset;
  10233. ParticleProp()->AddControlPoint( hEffect, 0, this, PATTACH_ABSORIGIN_FOLLOW, 0, vecOffset );
  10234. iValidParticleIndex++;
  10235. }
  10236. }
  10237. }
  10238. Vector C_TFPlayer::GetOverheadEffectPosition()
  10239. {
  10240. return GetClassEyeHeight() + Vector( 0, 0, 20 );
  10241. }
  10242. //------------------------------------------------------------------------------
  10243. // The serverbrowser has just added a server to the favorite list.
  10244. // Send this information to the GC.
  10245. static void cc_tf_register_favorite_with_gc( const CCommand &args )
  10246. {
  10247. netadr_t netaddr( args.ArgS() );
  10248. CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_FavoriteServer );
  10249. msg.Body().m_unIP = netaddr.GetIPNetworkByteOrder(); // <<<< Note: this is wrong. But it was wrong before. And we can fix the data if it is *consistently* wrong.
  10250. msg.Body().m_usPort = netaddr.GetPort();
  10251. msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromServerBrowser;
  10252. GCClientSystem()->BSendMessage( msg );
  10253. }
  10254. static ConCommand tf_register_favorite_with_gc( "rfgc", cc_tf_register_favorite_with_gc, "", FCVAR_HIDDEN );
  10255. //------------------------------------------------------------------------------
  10256. // The serverbrowser has just added a server to the blacklist.
  10257. // Send this information to the GC.
  10258. static void cc_tf_register_blacklist_with_gc( const CCommand &args )
  10259. {
  10260. netadr_t netaddr( args.ArgS() );
  10261. CGCMsg< MsgGCServerBrowser_Server_t > msg( k_EMsgGCServerBrowser_BlacklistServer );
  10262. msg.Body().m_unIP = netaddr.GetIPNetworkByteOrder(); // <<<< Note: this is wrong. But it was wrong before. And we can fix the data if it is *consistently* wrong.
  10263. msg.Body().m_usPort = netaddr.GetPort();
  10264. msg.Body().m_ubSource = k_EGCMsgServerBrowser_FromServerBrowser;
  10265. GCClientSystem()->BSendMessage( msg );
  10266. }
  10267. static ConCommand tf_register_blacklist_with_gc( "rbgc", cc_tf_register_blacklist_with_gc, "", FCVAR_HIDDEN );
  10268. static void cc_taunt_by_name( const CCommand &args )
  10269. {
  10270. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  10271. if ( !pPlayer || !pPlayer->IsAlive() )
  10272. return;
  10273. const char *pszTauntItemName = args.ArgS();
  10274. int iClass = pPlayer->GetPlayerClass()->GetClassIndex();
  10275. CTFPlayerInventory *pInv = pPlayer->Inventory();
  10276. if ( !pInv )
  10277. return;
  10278. CUtlStringList strTauntList;
  10279. for ( int iSlot = LOADOUT_POSITION_TAUNT; iSlot<= LOADOUT_POSITION_TAUNT8; ++iSlot )
  10280. {
  10281. CEconItemView *pItem = pInv->GetItemInLoadout( iClass, iSlot );
  10282. if ( !pItem || !pItem->IsValid() )
  10283. continue;
  10284. static char pszItemName[512];
  10285. g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find ( pItem->GetStaticData()->GetItemBaseName() ) , pszItemName, sizeof(pszItemName) );
  10286. strTauntList.CopyAndAddToTail( pszItemName );
  10287. if ( V_stricmp( pszTauntItemName, pszItemName ) == 0 )
  10288. {
  10289. int iTauntSlot = iSlot - LOADOUT_POSITION_TAUNT + 1;
  10290. engine->ClientCmd( CFmtStr( "taunt %d", iTauntSlot ) );
  10291. return;
  10292. }
  10293. }
  10294. Msg( "taunt_by_name failed. Taunt [%s] is not equipped in the loadout.\n", pszTauntItemName );
  10295. Msg( "[Taunt(s) in loadout]\n");
  10296. for ( int i=0; i<strTauntList.Count(); ++i )
  10297. {
  10298. Msg( "%s\n", strTauntList[i] );
  10299. }
  10300. }
  10301. static ConCommand taunt_by_name( "taunt_by_name", cc_taunt_by_name, "Use equipped taunt by name." );
  10302. #ifdef STAGING_ONLY
  10303. CON_COMMAND( force_reset_gesture_slot_vcd, "" )
  10304. {
  10305. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  10306. if ( pLocalPlayer )
  10307. {
  10308. pLocalPlayer->m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
  10309. }
  10310. }
  10311. #endif // STAGING_ONLY