Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5737 lines
174 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for Portal.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "portal_player.h"
  8. #include "globalstate.h"
  9. #include "game_timescale_shared.h"
  10. #include "trains.h"
  11. #include "game.h"
  12. #include "portal_player_shared.h"
  13. #include "predicted_viewmodel.h"
  14. #include "in_buttons.h"
  15. #include "portal_gamerules.h"
  16. #include "portal_mp_gamerules.h"
  17. #include "weapon_portalgun.h"
  18. #include "portal/weapon_physcannon.h"
  19. #include "KeyValues.h"
  20. #include "team.h"
  21. #include "eventqueue.h"
  22. #include "weapon_portalbase.h"
  23. #include "engine/IEngineSound.h"
  24. #include "ai_basenpc.h"
  25. #include "SoundEmitterSystem/isoundemittersystembase.h"
  26. #include "portal_base2d_shared.h"
  27. #include "player_pickup.h" // for player pickup code
  28. #include "vphysics/player_controller.h"
  29. #include "datacache/imdlcache.h"
  30. #include "bone_setup.h"
  31. #include "portal_gamestats.h"
  32. #include "physicsshadowclone.h"
  33. #include "physics_prop_ragdoll.h"
  34. #include "soundenvelope.h"
  35. #include "ai_baseactor.h" // For expressors, vcd playing
  36. #include "ai_speech.h" // For expressors, vcd playing
  37. #include "sceneentity.h" // has the VCD precache function
  38. #include "gamemovement.h"
  39. #include "particle_parse.h" // for dispatching particle effects
  40. #include "collisionutils.h"
  41. #include "mp_shareddefs.h"
  42. #include "prop_portal_shared.h"
  43. #include "world.h"
  44. #include "pointsurvey.h"
  45. #include "weapon_paintgun.h"
  46. #include "paint_swap_guns.h"
  47. #include "info_camera_link.h"
  48. #include "prop_weightedcube.h"
  49. #include "props.h"
  50. #include "sendprop_priorities.h"
  51. #include "env_portal_laser.h"
  52. #include "npc_portal_turret_floor.h"
  53. #include "dt_utlvector_send.h"
  54. #include "portal_mp_stats.h"
  55. #include "inetchannelinfo.h"
  56. #include "trigger_catapult.h"
  57. #include "portal_gamestats.h"
  58. #include "portal_ui_controller.h"
  59. #include "matchmaking/imatchframework.h"
  60. #include "matchmaking/portal2/imatchext_portal2.h"
  61. #include "portal2_research_data_tracker.h"
  62. #if !defined(NO_STEAM) && !defined(_PS3)
  63. #include "gc_serversystem.h"
  64. #endif
  65. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  66. #include "econ_gcmessages.h"
  67. #endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  68. #define PORTAL_RESPAWN_DELAY 1.0f // Seconds
  69. #define PORTAL_WALK_SPEED 175
  70. #define CATCHPATNERNOTCONNECTING_THINK_CONTEXT "CatchPatnerNotConnectingThinkContext"
  71. //HACKHACK: Keep track of which player has which gun between levels
  72. int g_iPortalGunPlayerTeam = TEAM_BLUE;
  73. extern CBaseEntity *g_pLastSpawn;
  74. extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
  75. #if USE_SLOWTIME
  76. ConVar slowtime_regen_per_second( "slowtime_regen_per_second", "4" );
  77. ConVar slowtime_max( "slowtime_max", "8", FCVAR_REPLICATED );
  78. ConVar slowtime_must_refill( "slowtime_must_refill", "0" );
  79. ConVar slowtime_speed( "slowtime_speed", "0.1", FCVAR_REPLICATED );
  80. #endif // USE_SLOWTIME
  81. ConVar playtest_random_death( "playtest_random_death", "0", FCVAR_NONE );
  82. float flNextDeathTime = 0.0f; // Used by the random death system to randomly kill a player to death
  83. ConVar sv_portal_coop_ping_cooldown_time( "sv_portal_coop_ping_cooldown_time", "0.25", FCVAR_CHEAT, "Time (in seconds) between coop pings", true, 0.1f, false, 60.0f );
  84. ConVar sv_portal_coop_ping_indicator_show_to_all_players( "sv_portal_coop_ping_indicator_show_to_all_players", "0" );
  85. extern ConVar sv_player_funnel_gimme_dot;
  86. ConVar sv_zoom_stop_movement_threashold("sv_zoom_stop_movement_threashold", "4.0", FCVAR_REPLICATED, "Move command amount before breaking player out of toggle zoom." );
  87. ConVar sv_zoom_stop_time_threashold("sv_zoom_stop_time_threashold", "5.0", FCVAR_REPLICATED, "Time amount before breaking player out of toggle zoom." );
  88. extern ConVar sv_player_funnel_into_portals;
  89. #define sv_can_carry_both_guns 0 //ConVar sv_can_carry_both_guns("sv_can_carry_both_guns", "0", FCVAR_REPLICATED | FCVAR_CHEAT);
  90. #define sv_can_swap_guns 1 //ConVar sv_can_swap_guns("sv_can_swap_guns", "1", FCVAR_REPLICATED | FCVAR_CHEAT);
  91. #define sv_can_swap_guns_anytime 1 //ConVar sv_can_swap_guns_anytime( "sv_can_swap_guns_anytime", "1", FCVAR_CHEAT );
  92. static ConVar portal_tauntcam_dist( "portal_tauntcam_dist", "75", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  93. ConVar sp_fade_and_force_respawn( "sp_fade_and_force_respawn", "1", FCVAR_CHEAT );
  94. ConVar mp_taunt_item( "mp_taunt_item", "", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Temporary for testing what will happen when a taunt item is in inventory." );
  95. extern ConVar mp_should_gib_bots;
  96. extern ConVar breakable_disable_gib_limit;
  97. extern ConVar breakable_multiplayer;
  98. ConVar mp_server_player_team( "mp_server_player_team", "0", FCVAR_DEVELOPMENTONLY );
  99. ConVar mp_wait_for_other_player_timeout( "mp_wait_for_other_player_timeout", "100", FCVAR_CHEAT, "Maximum time that we wait in the transition loading screen for the other player." );
  100. ConVar mp_wait_for_other_player_notconnecting_timeout( "mp_wait_for_other_player_notconnecting_timeout", "10", FCVAR_CHEAT, "Maximum time that we wait in the transition loading screen after we fully loaded for partner to start loading." );
  101. ConVar mp_dev_wait_for_other_player( "mp_dev_wait_for_other_player", "1", FCVAR_DEVELOPMENTONLY, "Force waiting for the other player." );
  102. extern ConVar sv_speed_normal;
  103. extern ConVar sv_post_teleportation_box_time;
  104. extern ConVar sv_press_jump_to_bounce;
  105. extern ConVar sv_use_trace_duration;
  106. extern ConVar sv_bonus_challenge;
  107. extern ConVar ai_debug_dyninteractions;
  108. extern void PaintPowerPickup( int colorIndex, CBasePlayer *pPlayer );
  109. #define COOP_PING_DECAL_NAME "overlays/coop_ping_decal"
  110. #define COOP_PING_SOUNDSCRIPT_NAME "Player.Coop_Ping"
  111. #define COOP_PING_PARTICLE_NAME "command_target_ping"
  112. #define TLK_PLAYER_KILLED "TLK_PLAYER_KILLED"
  113. #define TLK_PLAYER_SHOT "TLK_PLAYER_SHOT"
  114. #define TLK_PLAYER_BURNED "TLK_PLAYER_BURNED"
  115. #define ALLOWED_TEAM_TAUNT_Z_DIST 30.f
  116. // FIXME: Used for temp damage scaling -- jdw
  117. extern ConVar sk_dmg_take_scale1;
  118. const char *g_pszBallBotHelmetModel = "models/player/ballbot/ballbot_cage.mdl";
  119. const char *g_pszEggBotHelmetModel = "models/player/eggbot/eggbot_cage.mdl";
  120. const char *g_pszBallBotAntennaModel = "models/player/ballbot/ballbot_flag.mdl";
  121. const char *g_pszEggBotAntennaModel = "models/player/eggbot/eggbot_flag.mdl";
  122. // -------------------------------------------------------------------------------- //
  123. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  124. // -------------------------------------------------------------------------------- //
  125. class CTEPlayerAnimEvent : public CBaseTempEntity
  126. {
  127. public:
  128. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  129. DECLARE_SERVERCLASS();
  130. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  131. {
  132. }
  133. CNetworkHandle( CBasePlayer, m_hPlayer );
  134. CNetworkVar( int, m_iEvent );
  135. CNetworkVar( int, m_nData );
  136. };
  137. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  138. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  139. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  140. SendPropInt( SENDINFO( m_nData ), 32 ),
  141. END_SEND_TABLE()
  142. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  143. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  144. {
  145. CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
  146. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  147. g_TEPlayerAnimEvent.m_iEvent = event;
  148. g_TEPlayerAnimEvent.m_nData = nData;
  149. g_TEPlayerAnimEvent.Create( filter, 0 );
  150. }
  151. //=================================================================================
  152. //
  153. // Ragdoll Entity
  154. //
  155. class CPortalRagdoll : public CBaseAnimatingOverlay, public CDefaultPlayerPickupVPhysics
  156. {
  157. public:
  158. DECLARE_CLASS( CPortalRagdoll, CBaseAnimatingOverlay );
  159. DECLARE_SERVERCLASS();
  160. DECLARE_DATADESC();
  161. CPortalRagdoll()
  162. {
  163. m_hPlayer.Set( NULL );
  164. m_vecRagdollOrigin.Init();
  165. m_vecRagdollVelocity.Init();
  166. }
  167. // Transmit ragdolls to everyone.
  168. virtual int UpdateTransmitState()
  169. {
  170. return SetTransmitState( FL_EDICT_ALWAYS );
  171. }
  172. // In case the client has the player entity, we transmit the player index.
  173. // In case the client doesn't have it, we transmit the player's model index, origin, and angles
  174. // so they can create a ragdoll in the right place.
  175. CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
  176. CNetworkVector( m_vecRagdollVelocity );
  177. CNetworkVector( m_vecRagdollOrigin );
  178. };
  179. LINK_ENTITY_TO_CLASS( portal_ragdoll, CPortalRagdoll );
  180. IMPLEMENT_SERVERCLASS_ST_NOBASE( CPortalRagdoll, DT_PortalRagdoll )
  181. SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ),
  182. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  183. SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
  184. SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
  185. SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
  186. SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
  187. END_SEND_TABLE()
  188. BEGIN_DATADESC( CPortalRagdoll )
  189. DEFINE_FIELD( m_vecRagdollOrigin, FIELD_POSITION_VECTOR ),
  190. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  191. DEFINE_FIELD( m_vecRagdollVelocity, FIELD_VECTOR ),
  192. END_DATADESC()
  193. CEntityPortalledNetworkMessage::CEntityPortalledNetworkMessage( void )
  194. {
  195. m_hEntity = NULL;
  196. m_hPortal = NULL;
  197. m_fTime = 0.0f;
  198. m_bForcedDuck = false;
  199. m_iMessageCount = 0;;
  200. }
  201. BEGIN_SEND_TABLE_NOBASE( CEntityPortalledNetworkMessage, DT_EntityPortalledNetworkMessage )
  202. SendPropEHandle( SENDINFO_NOCHECK(m_hEntity) ),
  203. SendPropEHandle( SENDINFO_NOCHECK(m_hPortal) ),
  204. SendPropFloat( SENDINFO_NOCHECK(m_fTime), -1, SPROP_NOSCALE, 0.0f, HIGH_DEFAULT ),
  205. SendPropBool( SENDINFO_NOCHECK(m_bForcedDuck) ),
  206. SendPropInt( SENDINFO_NOCHECK(m_iMessageCount) ),
  207. END_SEND_TABLE()
  208. extern void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID );
  209. // specific to the local player
  210. BEGIN_SEND_TABLE_NOBASE( CPortal_Player, DT_PortalLocalPlayerExclusive )
  211. // send a hi-res origin and view offset to the local player for use in prediction
  212. SendPropVectorXY(SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE, 0.0f, HIGH_DEFAULT, SendProxy_OriginXY, SENDPROP_LOCALPLAYER_ORIGINXY_PRIORITY ),
  213. SendPropFloat (SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_NOSCALE, 0.0f, HIGH_DEFAULT, SendProxy_OriginZ, SENDPROP_LOCALPLAYER_ORIGINZ_PRIORITY ),
  214. SendPropVector (SENDINFO(m_vecViewOffset), -1, SPROP_NOSCALE, 0.0f, HIGH_DEFAULT ),
  215. SendPropQAngles( SENDINFO( m_vecCarriedObjectAngles ) ),
  216. SendPropVector( SENDINFO( m_vecCarriedObject_CurPosToTargetPos ) ),
  217. SendPropQAngles( SENDINFO( m_vecCarriedObject_CurAngToTargetAng ) ),
  218. //a message buffer for entity teleportations that's guaranteed to be in sync with the post-teleport updates for said entities
  219. SendPropUtlVector( SENDINFO_UTLVECTOR( m_EntityPortalledNetworkMessages ), CPortal_Player::MAX_ENTITY_PORTALLED_NETWORK_MESSAGES, SendPropDataTable( NULL, 0, &REFERENCE_SEND_TABLE( DT_EntityPortalledNetworkMessage ) ) ),
  220. SendPropInt( SENDINFO( m_iEntityPortalledNetworkMessageCount ) ),
  221. END_SEND_TABLE()
  222. // all players except the local player
  223. BEGIN_SEND_TABLE_NOBASE( CPortal_Player, DT_PortalNonLocalPlayerExclusive )
  224. // send a lo-res origin and view offset to other players
  225. // send a lo-res origin to other players
  226. SendPropVectorXY( SENDINFO( m_vecOrigin ), CELL_BASEENTITY_ORIGIN_CELL_BITS, SPROP_CELL_COORD_LOWPRECISION, 0.0f, HIGH_DEFAULT, CBaseEntity::SendProxy_CellOriginXY, SENDPROP_NONLOCALPLAYER_ORIGINXY_PRIORITY ),
  227. SendPropFloat ( SENDINFO_VECTORELEM( m_vecOrigin, 2 ), CELL_BASEENTITY_ORIGIN_CELL_BITS, SPROP_CELL_COORD_LOWPRECISION, 0.0f, HIGH_DEFAULT, CBaseEntity::SendProxy_CellOriginZ, SENDPROP_NONLOCALPLAYER_ORIGINZ_PRIORITY ),
  228. SendPropFloat (SENDINFO_VECTORELEM(m_vecViewOffset, 0), 10, SPROP_CHANGES_OFTEN, -128.0, 128.0f),
  229. SendPropFloat (SENDINFO_VECTORELEM(m_vecViewOffset, 1), 10, SPROP_CHANGES_OFTEN, -128.0, 128.0f),
  230. SendPropFloat (SENDINFO_VECTORELEM(m_vecViewOffset, 2), 10, SPROP_CHANGES_OFTEN, -128.0, 128.0f),
  231. END_SEND_TABLE()
  232. BEGIN_SEND_TABLE_NOBASE( CPortalPlayerShared, DT_PortalPlayerShared )
  233. SendPropInt( SENDINFO( m_nPlayerCond ), PORTAL_COND_LAST, (SPROP_UNSIGNED|SPROP_CHANGES_OFTEN) ),
  234. END_SEND_TABLE()
  235. LINK_ENTITY_TO_CLASS( player, CPortal_Player );
  236. IMPLEMENT_SERVERCLASS_ST(CPortal_Player, DT_Portal_Player)
  237. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  238. SendPropExclude( "DT_LocalPlayerExclusive", "m_vecViewOffset[0]" ),
  239. SendPropExclude( "DT_LocalPlayerExclusive", "m_vecViewOffset[1]" ),
  240. SendPropExclude( "DT_LocalPlayerExclusive", "m_vecViewOffset[2]" ),
  241. #ifdef PORTAL_PLAYER_PREDICTION
  242. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  243. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  244. SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
  245. SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
  246. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  247. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  248. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  249. SendPropExclude( "DT_BaseFlex", "m_viewtarget" ),
  250. SendPropExclude( "DT_BaseFlex", "m_flexWeight" ),
  251. SendPropExclude( "DT_BaseFlex", "m_blinktoggle" ),
  252. // portal_playeranimstate and clientside animation takes care of these on the client
  253. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  254. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  255. #endif // PORTAL_PLAYER_PREDICTION
  256. SendPropDataTable(SENDINFO_DT(m_PortalLocal), &REFERENCE_SEND_TABLE(DT_PortalLocal), SendProxy_SendLocalDataTable),
  257. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ),
  258. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ),
  259. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  260. SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
  261. SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ),
  262. SendPropBool( SENDINFO( m_bHeldObjectOnOppositeSideOfPortal) ),
  263. SendPropBool( SENDINFO( m_bPitchReorientation ) ),
  264. SendPropEHandle( SENDINFO( m_hPortalEnvironment ) ),
  265. SendPropBool( SENDINFO( m_bIsHoldingSomething ) ),
  266. SendPropBool( SENDINFO( m_bPingDisabled ) ),
  267. SendPropBool( SENDINFO( m_bTauntDisabled ) ),
  268. SendPropBool( SENDINFO( m_bTauntRemoteView ) ),
  269. SendPropVector( SENDINFO( m_vecRemoteViewOrigin ) ),
  270. SendPropVector( SENDINFO( m_vecRemoteViewAngles ) ),
  271. SendPropFloat( SENDINFO( m_fTauntCameraDistance ) ),
  272. SendPropInt( SENDINFO( m_nTeamTauntState ) ),
  273. SendPropVector( SENDINFO( m_vTauntPosition ) ),
  274. SendPropQAngles( SENDINFO( m_vTauntAngles ) ),
  275. SendPropQAngles( SENDINFO( m_vPreTauntAngles ) ),
  276. SendPropBool( SENDINFO( m_bTrickFire ) ),
  277. SendPropEHandle( SENDINFO( m_hTauntPartnerInRange ) ),
  278. SendPropString( SENDINFO( m_szTauntForce ) ),
  279. SendPropBool( SENDINFO( m_bUseVMGrab ) ),
  280. SendPropBool( SENDINFO( m_bUsingVMGrabState ) ),
  281. SendPropEHandle( SENDINFO( m_hAttachedObject ) ),
  282. SendPropEHandle( SENDINFO( m_hHeldObjectPortal ) ),
  283. SendPropFloat( SENDINFO( m_flMotionBlurAmount ) ),
  284. // Data that only gets sent to the local player
  285. SendPropDataTable( "portallocaldata", 0, &REFERENCE_SEND_TABLE(DT_PortalLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
  286. // Data that gets sent to all other players
  287. SendPropDataTable( "portalnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_PortalNonLocalPlayerExclusive), SendProxy_SendNonLocalDataTable ),
  288. SendPropBool( SENDINFO( m_bWantsToSwapGuns ) ),
  289. SendPropBool( SENDINFO( m_bPotatos ) ),
  290. // Shared info
  291. SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_PortalPlayerShared ) ),
  292. SendPropFloat( SENDINFO( m_flHullHeight ) ),
  293. SendPropBool( SENDINFO( m_iSpawnCounter ) ),
  294. SendPropDataTable( SENDINFO_DT( m_StatsThisLevel ), &REFERENCE_SEND_TABLE(DT_PortalPlayerStatistics), SendProxy_SendLocalDataTable ),
  295. END_SEND_TABLE()
  296. BEGIN_DATADESC( CPortal_Player )
  297. DEFINE_SOUNDPATCH( m_pWooshSound ),
  298. DEFINE_SOUNDPATCH( m_pGrabSound ),
  299. DEFINE_FIELD( m_bHeldObjectOnOppositeSideOfPortal, FIELD_BOOLEAN ),
  300. DEFINE_FIELD( m_hHeldObjectPortal, FIELD_EHANDLE ),
  301. DEFINE_FIELD( m_bIntersectingPortalPlane, FIELD_BOOLEAN ),
  302. DEFINE_FIELD( m_bStuckOnPortalCollisionObject, FIELD_BOOLEAN ),
  303. DEFINE_FIELD( m_fTimeLastNumSecondsUpdate, FIELD_TIME ),
  304. DEFINE_FIELD( m_iNumCamerasDetatched, FIELD_INTEGER ),
  305. DEFINE_FIELD( m_bPitchReorientation, FIELD_BOOLEAN ),
  306. DEFINE_FIELD( m_fNeuroToxinDamageTime, FIELD_TIME ),
  307. DEFINE_FIELD( m_hPortalEnvironment, FIELD_EHANDLE ),
  308. DEFINE_FIELD( m_vecTotalBulletForce, FIELD_VECTOR ),
  309. DEFINE_FIELD( m_bSilentDropAndPickup, FIELD_BOOLEAN ),
  310. DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ),
  311. DEFINE_FIELD( m_angEyeAngles, FIELD_VECTOR ),
  312. DEFINE_FIELD( m_iPlayerSoundType, FIELD_INTEGER ),
  313. DEFINE_FIELD( m_vWorldSpaceCenterHolder, FIELD_POSITION_VECTOR ),
  314. //DEFINE_FIELD( m_hRemoteTauntCamera, FIELD_EHANDLE ),
  315. DEFINE_FIELD( m_flLastPingTime, FIELD_FLOAT ),
  316. DEFINE_FIELD( m_bClientCheckPVSDirty, FIELD_BOOLEAN ),
  317. DEFINE_FIELD( m_flUseKeyCooldownTime, FIELD_TIME ),
  318. DEFINE_FIELD( m_bIsHoldingSomething, FIELD_BOOLEAN ),
  319. DEFINE_FIELD( m_iLastWeaponFireUsercmd, FIELD_INTEGER ),
  320. DEFINE_FIELD( m_iSpawnInterpCounter, FIELD_INTEGER ),
  321. DEFINE_FIELD( m_bPingDisabled, FIELD_BOOLEAN ),
  322. DEFINE_FIELD( m_bTauntDisabled, FIELD_BOOLEAN ),
  323. DEFINE_FIELD( m_bTauntRemoteView, FIELD_BOOLEAN ),
  324. DEFINE_FIELD( m_bTauntRemoteViewFOVFixup, FIELD_BOOLEAN ),
  325. DEFINE_FIELD( m_vecRemoteViewOrigin, FIELD_POSITION_VECTOR ),
  326. DEFINE_FIELD( m_vecRemoteViewAngles, FIELD_VECTOR ),
  327. DEFINE_FIELD( m_fTauntCameraDistance, FIELD_FLOAT ),
  328. DEFINE_FIELD( m_nTeamTauntState, FIELD_INTEGER ),
  329. DEFINE_FIELD( m_vTauntPosition, FIELD_POSITION_VECTOR ),
  330. DEFINE_FIELD( m_vTauntAngles, FIELD_VECTOR ),
  331. DEFINE_FIELD( m_vPreTauntAngles, FIELD_VECTOR ),
  332. DEFINE_FIELD( m_bTrickFire, FIELD_BOOLEAN ),
  333. DEFINE_FIELD( m_hTauntPartnerInRange, FIELD_EHANDLE ),
  334. DEFINE_FIELD( m_flMotionBlurAmount, FIELD_FLOAT ),
  335. DEFINE_AUTO_ARRAY( m_szTauntForce, FIELD_CHARACTER ),
  336. #if USE_SLOWTIME
  337. DEFINE_FIELD( m_bHasPlayedSlowTimeStopSound, FIELD_BOOLEAN ),
  338. DEFINE_FIELD( m_pSlowTimeColorFX, FIELD_CLASSPTR ),
  339. #endif // USE_SLOWTIME
  340. DEFINE_FIELD( m_hGrabbedEntity, FIELD_EHANDLE ),
  341. DEFINE_FIELD( m_hPortalThroughWhichGrabOccured, FIELD_EHANDLE ),
  342. DEFINE_FIELD( m_bForcingDrop, FIELD_BOOLEAN ),
  343. DEFINE_FIELD( m_bUseVMGrab, FIELD_BOOLEAN ),
  344. DEFINE_FIELD( m_bUsingVMGrabState, FIELD_BOOLEAN ),
  345. DEFINE_FIELD( m_flUseKeyStartTime, FIELD_TIME ),
  346. DEFINE_FIELD( m_flAutoGrabLockOutTime, FIELD_TIME ),
  347. DEFINE_FIELD( m_hAttachedObject, FIELD_EHANDLE ),
  348. DEFINE_FIELD( m_ForcedGrabController, FIELD_INTEGER ),
  349. DEFINE_FIELD( m_flTimeLastTouchedGround, FIELD_TIME ),
  350. DEFINE_FIELD( m_nPortalsEnteredInAirFlags, FIELD_INTEGER ),
  351. DEFINE_FIELD( m_nAirTauntCount, FIELD_INTEGER ),
  352. DEFINE_FIELD( m_nWheatleyMonitorDestructionCount, FIELD_INTEGER ),
  353. DEFINE_FIELD( m_bPotatos, FIELD_BOOLEAN ),
  354. DEFINE_FIELD( m_PlayerGunType, FIELD_INTEGER ),
  355. DEFINE_FIELD( m_bSpawnFromDeath, FIELD_BOOLEAN ),
  356. DEFINE_FIELD( m_flHullHeight, FIELD_FLOAT ),
  357. DEFINE_FIELD( m_bWasDroppedByOtherPlayerWhileTaunting, FIELD_BOOLEAN ),
  358. DEFINE_EMBEDDED( m_PortalLocal ),
  359. //DEFINE_FIELD ( m_PlayerAnimState, CPortalPlayerAnimState ),
  360. DEFINE_EMBEDDED( m_StatsThisLevel ),
  361. //DEFINE_FIELD( m_bPlayUseDenySound, FIELD_BOOLEAN ),
  362. DEFINE_THINKFUNC( PlayerTransitionCompleteThink ),
  363. DEFINE_THINKFUNC( PlayerCatchPatnerNotConnectingThink ),
  364. END_DATADESC()
  365. BEGIN_ENT_SCRIPTDESC( CPortal_Player, CBaseMultiplayerPlayer , "Player" )
  366. DEFINE_SCRIPTFUNC( IncWheatleyMonitorDestructionCount, "Set number of wheatley monitors destroyed by the player." )
  367. DEFINE_SCRIPTFUNC( GetWheatleyMonitorDestructionCount, "Get number of wheatley monitors destroyed by the player." )
  368. DEFINE_SCRIPTFUNC( TurnOffPotatos, "Turns Off the Potatos material light" )
  369. DEFINE_SCRIPTFUNC( TurnOnPotatos, "Turns On the Potatos material light" )
  370. END_SCRIPTDESC();
  371. extern const char *g_pszPlayerModel;
  372. const char* g_pszPlayerAnimations = "models/player_animations.mdl";
  373. const char* g_pszBallBotAnimations = "models/ballbot_animations.mdl";
  374. const char* g_pszEggBotAnimations = "models/eggbot_animations.mdl";
  375. class CPortalPlayerModelPrecacher : public CBaseResourcePrecacher
  376. {
  377. public:
  378. CPortalPlayerModelPrecacher() : CBaseResourcePrecacher( GLOBAL, "CPortalPlayerModelPrecacher" ) {}
  379. virtual void Cache( IPrecacheHandler *pPrecacheHandler, bool bPrecache, ResourceList_t hResourceList, bool bIgnoreConditionals )
  380. {
  381. bool bIsMultiplayer;
  382. if( bPrecache )
  383. {
  384. bIsMultiplayer = g_pGameRules ? g_pGameRules->IsMultiplayer() : (Q_strnicmp( gpGlobals->mapname.ToCStr(), "mp", 2 ) == 0); //either gamerules says it's multiplayer, or the map name implies it
  385. m_bPreCacheWasMultiplayer = bIsMultiplayer;
  386. }
  387. else
  388. {
  389. bIsMultiplayer = m_bPreCacheWasMultiplayer;
  390. }
  391. if( bIsMultiplayer || bIgnoreConditionals )
  392. {
  393. int iModelIndex;
  394. pPrecacheHandler->CacheResource( MODEL, GetBallBotModel(), bPrecache, hResourceList, &iModelIndex );
  395. pPrecacheHandler->CacheResource( MODEL, g_pszBallBotAnimations, bPrecache, hResourceList, NULL );
  396. PrecacheGibsForModel( iModelIndex );
  397. pPrecacheHandler->CacheResource( MODEL, GetEggBotModel(), bPrecache, hResourceList, &iModelIndex );
  398. pPrecacheHandler->CacheResource( MODEL, g_pszEggBotAnimations, bPrecache, hResourceList, NULL );
  399. PrecacheGibsForModel( iModelIndex );
  400. pPrecacheHandler->CacheResource( MODEL, g_pszBallBotHelmetModel, bPrecache, hResourceList, NULL );
  401. pPrecacheHandler->CacheResource( MODEL, g_pszEggBotHelmetModel, bPrecache, hResourceList, NULL );
  402. pPrecacheHandler->CacheResource( MODEL, g_pszBallBotAntennaModel, bPrecache, hResourceList, NULL );
  403. pPrecacheHandler->CacheResource( MODEL, g_pszEggBotAntennaModel, bPrecache, hResourceList, NULL );
  404. }
  405. if( !bIsMultiplayer || bIgnoreConditionals )
  406. {
  407. pPrecacheHandler->CacheResource( MODEL, g_pszPlayerModel, bPrecache, hResourceList, NULL );
  408. pPrecacheHandler->CacheResource( MODEL, g_pszPlayerAnimations, bPrecache, hResourceList, NULL );
  409. }
  410. }
  411. bool m_bPreCacheWasMultiplayer; //just being a little paranoid that precaches and uncaches sync up consistently
  412. };
  413. CPortalPlayerModelPrecacher s_PortalModelPrecacher;
  414. #define MAX_COMBINE_MODELS 4
  415. #define MODEL_CHANGE_INTERVAL 5.0f
  416. #define TEAM_CHANGE_INTERVAL 5.0f
  417. #define PORTALPLAYER_PHYSDAMAGE_SCALE 4.0f
  418. extern ConVar sv_turbophysics;
  419. //----------------------------------------------------
  420. // Player Physics Shadow
  421. //----------------------------------------------------
  422. #define VPHYS_MAX_DISTANCE 2.0
  423. #define VPHYS_MAX_VEL 10
  424. #define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE)
  425. #define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL)
  426. extern float IntervalDistance( float x, float x0, float x1 );
  427. //----------------------------------------------------
  428. // Clear UI for both clients - useful on transitions
  429. //----------------------------------------------------
  430. void ClearClientUI()
  431. {
  432. CReliableBroadcastRecipientFilter filter;
  433. filter.AddAllPlayers();
  434. UserMessageBegin( filter, "ChallengeModeCloseAllUI" );
  435. MessageEnd();
  436. }
  437. //disable 'this' : used in base member initializer list
  438. #pragma warning( disable : 4355 )
  439. CPortal_Player::CPortal_Player()
  440. : m_vInputVector( 0.0f, 0.0f, 0.0f ),
  441. m_flCachedJumpPowerTime( -FLT_MAX ),
  442. m_flSpeedDecelerationTime( 0.0f ),
  443. m_flPredictedJumpTime( 0.f ),
  444. m_flUsePostTeleportationBoxTime( 0.0f ),
  445. m_bJumpWasPressedWhenForced( false ),
  446. m_bWantsToSwapGuns( false ),
  447. m_bSendSwapProximityFailEvent( false ),
  448. m_PlayerGunType( PLAYER_NO_GUN ),
  449. m_bSpawnFromDeath( false ),
  450. m_nBounceCount( 0 ),
  451. m_LastGroundBouncePlaneDistance( 0.0f ),
  452. m_flLastSuppressedBounceTime( 0 ),
  453. m_bIsFullyConnected( false ),
  454. m_pGrabSound( NULL ),
  455. m_nAirTauntCount( 0 ),
  456. m_nWheatleyMonitorDestructionCount( 0 ),
  457. m_bPotatos( true ),
  458. m_flMotionBlurAmount( -1.0f ),
  459. m_bIsBendy( false )
  460. {
  461. // Taunt code
  462. m_Shared.Init( this );
  463. m_Shared.m_flTauntRemoveTime = 0.0f;
  464. m_PlayerAnimState = CreatePortalPlayerAnimState( this );
  465. UseClientSideAnimation();
  466. m_angEyeAngles.Init();
  467. m_iLastWeaponFireUsercmd = 0;
  468. m_iSpawnInterpCounter = 0;
  469. m_bHeldObjectOnOppositeSideOfPortal = false;
  470. m_bIntersectingPortalPlane = false;
  471. m_bPitchReorientation = false;
  472. m_bSilentDropAndPickup = false;
  473. m_bClientCheckPVSDirty = false;
  474. m_flUseKeyCooldownTime = 0.0f;
  475. m_hGrabbedEntity = NULL;
  476. m_flLastPingTime = 0.0f;
  477. m_hPortalThroughWhichGrabOccured = NULL;
  478. m_ForcedGrabController = FORCE_GRAB_CONTROLLER_DEFAULT;
  479. #if USE_SLOWTIME
  480. m_bHasPlayedSlowTimeStopSound = true;
  481. #endif // USE_SLOWTIME
  482. m_flImplicitVerticalStepSpeed = 0.0f;
  483. m_flTimeSinceLastTouchedPower[0] = FLT_MAX;
  484. m_flTimeSinceLastTouchedPower[1] = FLT_MAX;
  485. m_flTimeSinceLastTouchedPower[2] = FLT_MAX;
  486. m_flHullHeight = GetHullHeight();
  487. m_EntityPortalledNetworkMessages.SetCount( MAX_ENTITY_PORTALLED_NETWORK_MESSAGES );
  488. m_PlayerGunTypeWhenDead = PLAYER_NO_GUN;
  489. m_bReadyForDLCItemUpdates = false;
  490. }
  491. CPortal_Player::~CPortal_Player( void )
  492. {
  493. #ifdef PORTAL2
  494. if ( GameRules() && GameRules()->IsMultiplayer() && !IsSplitScreenPlayer() )
  495. {
  496. CPortal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherPlayer( this ) );
  497. if ( pOtherPlayer )
  498. {
  499. pOtherPlayer->RemovePictureInPicturePlayer( this );
  500. }
  501. }
  502. #endif
  503. ClearSceneEvents( NULL, true );
  504. if ( m_PlayerAnimState )
  505. m_PlayerAnimState->Release();
  506. CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
  507. if( pRagdoll )
  508. {
  509. UTIL_Remove( pRagdoll );
  510. }
  511. }
  512. CEG_NOINLINE CPortal_Player *CPortal_Player::CreatePlayer( const char *className, edict_t *ed )
  513. {
  514. CPortal_Player::s_PlayerEdict = ed;
  515. return (CPortal_Player*)CreateEntityByName( className );
  516. }
  517. CEG_PROTECT_STATIC_MEMBER_FUNCTION( CPortal_Player_CreatePlayer, CPortal_Player::CreatePlayer );
  518. void CPortal_Player::UpdateOnRemove( void )
  519. {
  520. #if USE_SLOWTIME
  521. if ( m_pSlowTimeColorFX )
  522. {
  523. UTIL_Remove( m_pSlowTimeColorFX );
  524. m_pSlowTimeColorFX = NULL;
  525. }
  526. #endif // USE_SLOWTIME
  527. #if !defined(NO_STEAM) && !defined( NO_STEAM_GAMECOORDINATOR )
  528. m_Inventory.RemoveListener( this );
  529. #endif
  530. BaseClass::UpdateOnRemove();
  531. }
  532. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  533. //-----------------------------------------------------------------------------
  534. // Purpose: Request this player's inventories from the steam backend
  535. //-----------------------------------------------------------------------------
  536. void CPortal_Player::UpdateInventory( bool bInit )
  537. {
  538. if ( IsFakeClient() )
  539. return;
  540. if ( bInit )
  541. {
  542. if ( steamgameserverapicontext->SteamGameServer() )
  543. {
  544. CSteamID steamIDForPlayer;
  545. if ( GetSteamID( &steamIDForPlayer ) )
  546. {
  547. PortalInventoryManager()->SteamRequestInventory( &m_Inventory, steamIDForPlayer, this );
  548. }
  549. }
  550. }
  551. // If we have an SOCache, we've got a connection to the GC
  552. bool bInvalid = true;
  553. if ( m_Inventory.GetSOC() )
  554. {
  555. bInvalid = m_Inventory.GetSOC()->BIsInitialized() == false;
  556. }
  557. m_Shared.SetLoadoutUnavailable( bInvalid );
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Purpose: Steam has just notified us that the player changed his inventory
  561. //-----------------------------------------------------------------------------
  562. void CPortal_Player::InventoryUpdated( CPlayerInventory *pInventory )
  563. {
  564. m_Shared.SetLoadoutUnavailable( false );
  565. // Make sure we're wearing the right skin.
  566. SetPlayerModel();
  567. if ( m_bReadyForDLCItemUpdates )
  568. {
  569. bool bMultiplayer = g_pGameRules->IsMultiplayer();
  570. bool bIs2GunsMap = ( V_stristr( gpGlobals->mapname.ToCStr(), "2guns" ) != NULL ) || ( GlobalEntity_GetState( "paintgun_map" ) == GLOBAL_ON );
  571. if ( !bMultiplayer || !bIs2GunsMap )
  572. {
  573. GiveDefaultItems();
  574. }
  575. }
  576. }
  577. //-----------------------------------------------------------------------------
  578. // Purpose: Requests that the GC confirm that this player is supposed to have
  579. // an SO cache on this gameserver and send it again if so.
  580. //-----------------------------------------------------------------------------
  581. void CPortal_Player::VerifySOCache()
  582. {
  583. if ( IsFakeClient() )
  584. return;
  585. CSteamID steamIDForPlayer;
  586. GetSteamID( &steamIDForPlayer );
  587. if( steamIDForPlayer.BIndividualAccount() )
  588. {
  589. // if we didn't find an inventory ask the GC to refresh us
  590. GCSDK::CGCMsg<MsgGCVerifyCacheSubscription_t> msgVerifyCache( k_EMsgGCVerifyCacheSubscription );
  591. msgVerifyCache.Body().m_ulSteamID = steamIDForPlayer.ConvertToUint64();
  592. GCClientSystem()->BSendMessage( msgVerifyCache );
  593. }
  594. else
  595. {
  596. Msg( "Cannot verify load for invalid steam ID %s\n", steamIDForPlayer.Render() );
  597. }
  598. }
  599. CEconItemView *CPortal_Player::GetItemInLoadoutSlot( int iLoadoutSlot )
  600. {
  601. // Portal players just instantly equip things.
  602. int iBot = ( GetTeamNumber() == TEAM_BLUE ) ? P2BOT_ATLAS : P2BOT_PBODY;
  603. return m_Inventory.GetItemInLoadout( iBot, iLoadoutSlot );
  604. }
  605. #endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  606. void CPortal_Player::Precache( void )
  607. {
  608. BaseClass::Precache();
  609. PrecacheScriptSound( "PortalPlayer.EnterPortal" );
  610. PrecacheScriptSound( "PortalPlayer.ExitPortal" );
  611. PrecacheScriptSound( "PortalPlayer.Woosh" );
  612. PrecacheScriptSound( "PortalPlayer.FallRecover" );
  613. PrecacheScriptSound( "PortalPlayer.ObjectUse" );
  614. PrecacheScriptSound( "PortalPlayer.UseDeny" );
  615. PrecacheScriptSound( "PortalPlayer.ObjectUseNoGun" );
  616. PrecacheScriptSound( "PortalPlayer.UseDenyNoGun" );
  617. PrecacheScriptSound( "JumpLand.HighVelocityImpactCeiling" );
  618. PrecacheScriptSound( "JumpLand.HighVelocityImpact" );
  619. #if USE_SLOWTIME
  620. // Slow time
  621. PrecacheScriptSound( "Player.SlowTime_Start" );
  622. PrecacheScriptSound( "Player.SlowTime_Loop" );
  623. PrecacheScriptSound( "Player.SlowTime_Stop" );
  624. #endif // USE_SLOWTIME
  625. // Precache based on our game type
  626. if ( GameRules()->IsMultiplayer() )
  627. {
  628. PrecacheParticleSystem( COOP_PING_PARTICLE_NAME );
  629. PrecacheParticleSystem( "command_target_ping_just_arrows" );
  630. PrecacheParticleSystem( "robot_point_beam" );
  631. PrecacheScriptSound( COOP_PING_SOUNDSCRIPT_NAME );
  632. UTIL_PrecacheDecal( COOP_PING_DECAL_NAME );
  633. // Player models
  634. PrecacheModel( GetBallBotModel() );
  635. PrecacheModel( g_pszBallBotAnimations );
  636. PrecacheModel( GetEggBotModel() );
  637. PrecacheModel( g_pszEggBotAnimations );
  638. PrecacheScriptSound( "CoopBot.WallSlam" );
  639. PrecacheScriptSound( "CoopBot.Explode_Gib" );
  640. PrecacheScriptSound( "CoopBot.CoopBotBulletImpact" );
  641. int iModelIndex = PrecacheModel( GetBallBotModel() );
  642. PrecacheGibsForModel( iModelIndex );
  643. iModelIndex = PrecacheModel( GetEggBotModel() );
  644. PrecacheGibsForModel( iModelIndex );
  645. PrecacheModel( g_pszBallBotHelmetModel );
  646. PrecacheModel( g_pszEggBotHelmetModel );
  647. PrecacheModel( g_pszBallBotAntennaModel );
  648. PrecacheModel( g_pszEggBotAntennaModel );
  649. }
  650. else
  651. {
  652. PrecacheModel( g_pszPlayerModel );
  653. PrecacheModel( g_pszPlayerAnimations );
  654. }
  655. // paint effect
  656. PrecacheParticleSystem( "boomer_vomit_screeneffect" );
  657. PrecacheParticleSystem( "boomer_vomit_survivor" );
  658. // paint sound
  659. PrecacheScriptSound( "Player.JumpPowerUse" );
  660. PrecacheScriptSound( "Player.EnterBouncePaint" );
  661. PrecacheScriptSound( "Player.ExitBouncePaint" );
  662. PrecacheScriptSound( "Player.EnterSpeedPaint" );
  663. PrecacheScriptSound( "Player.ExitSpeedPaint" );
  664. PrecacheScriptSound( "Player.EnterStickPaint" );
  665. PrecacheScriptSound( "Player.ExitStickPaint" );
  666. PrecacheParticleSystem( "electrical_arc_01" );
  667. }
  668. void CPortal_Player::CreateSounds()
  669. {
  670. if ( !m_pWooshSound )
  671. {
  672. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  673. CPASAttenuationFilter filter( this );
  674. m_pWooshSound = controller.SoundCreate( filter, entindex(), "PortalPlayer.Woosh" );
  675. controller.Play( m_pWooshSound, 0, 100 );
  676. }
  677. }
  678. void CPortal_Player::StopLoopingSounds()
  679. {
  680. if ( m_pWooshSound )
  681. {
  682. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  683. controller.SoundDestroy( m_pWooshSound );
  684. m_pWooshSound = NULL;
  685. }
  686. BaseClass::StopLoopingSounds();
  687. }
  688. void CPortal_Player::GiveAllItems( void )
  689. {
  690. CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( GiveNamedItem( "weapon_portalgun" ) );
  691. if ( !pPortalGun )
  692. {
  693. pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( "weapon_portalgun" ) );
  694. }
  695. if ( pPortalGun )
  696. {
  697. pPortalGun->SetCanFirePortal1();
  698. pPortalGun->SetCanFirePortal2();
  699. }
  700. }
  701. void CPortal_Player::GiveDefaultItems( void )
  702. {
  703. if ( GameRules()->IsMultiplayer() )
  704. {
  705. if ( PortalMPGameRules() && !PortalMPGameRules()->SupressSpawnPortalgun( GetTeamNumber() ) )
  706. {
  707. // Give the player an upgraded portal gun.
  708. if ( !Weapon_OwnsThisType("weapon_portalgun", 0) )
  709. {
  710. CWeaponPortalgun *pPortalGun = (CWeaponPortalgun *)CreateEntityByName("weapon_portalgun");
  711. if ( pPortalGun != NULL )
  712. {
  713. pPortalGun->SetLocalOrigin( GetLocalOrigin() );
  714. pPortalGun->AddSpawnFlags( SF_NORESPAWN );
  715. pPortalGun->SetSubType( 0 );
  716. DispatchSpawn( pPortalGun );
  717. if ( !pPortalGun->IsMarkedForDeletion() )
  718. {
  719. pPortalGun->SetCanFirePortal1();
  720. pPortalGun->SetCanFirePortal2();
  721. Weapon_Equip( pPortalGun );
  722. }
  723. }
  724. }
  725. }
  726. if ( g_nPortal2PromoFlags & PORTAL2_PROMO_HELMETS )
  727. {
  728. // Don't give me a rollcage if I have a hat equipped
  729. bool bHasHeadgearEquipped = false;
  730. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  731. CEconItemView *pItem = GetItemInLoadoutSlot( LOADOUT_POSITION_HEAD );
  732. bHasHeadgearEquipped = ( pItem && pItem->IsValid() );
  733. #endif
  734. if ( !bHasHeadgearEquipped )
  735. {
  736. GivePlayerWearable( GetTeamNumber() == TEAM_BLUE ? "weapon_promo_helmet_ball" : "weapon_promo_helmet_egg" );
  737. }
  738. else
  739. {
  740. RemovePlayerWearable( GetTeamNumber() == TEAM_BLUE ? "weapon_promo_helmet_ball" : "weapon_promo_helmet_egg" );
  741. }
  742. }
  743. if ( g_nPortal2PromoFlags & PORTAL2_PROMO_ANTENNA )
  744. {
  745. bool bHasFlagEquipped = false;
  746. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  747. // Don't give me an antenna if I have a flag equipped
  748. CEconItemView *pItem = GetItemInLoadoutSlot( LOADOUT_POSITION_MISC );
  749. if ( pItem && pItem->IsValid() )
  750. {
  751. if ( pItem->GetStaticData() && pItem->GetStaticData()->GetItemTypeName() )
  752. {
  753. bHasFlagEquipped = Q_stricmp( pItem->GetStaticData()->GetItemTypeName(), "#P2_WearableType_Flag" ) == 0;
  754. }
  755. }
  756. #endif
  757. if ( !bHasFlagEquipped )
  758. {
  759. GivePlayerWearable( GetTeamNumber() == TEAM_BLUE ? "weapon_promo_antenna_ball" : "weapon_promo_antenna_egg" );
  760. }
  761. else
  762. {
  763. RemovePlayerWearable( GetTeamNumber() == TEAM_BLUE ? "weapon_promo_antenna_ball" : "weapon_promo_antenna_egg" );
  764. }
  765. }
  766. }
  767. m_bReadyForDLCItemUpdates = true;
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Purpose: Sets specific defaults.
  771. //-----------------------------------------------------------------------------
  772. void CPortal_Player::Spawn(void)
  773. {
  774. Precache();
  775. if( g_pGameRules->IsMultiplayer() )
  776. {
  777. switch( GetTeamNumber() )
  778. {
  779. case TEAM_UNASSIGNED:
  780. //case TEAM_SPECTATOR:
  781. PickTeam();
  782. }
  783. }
  784. SetPlayerModel();
  785. BaseClass::Spawn();
  786. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  787. // Check the make sure we have our inventory each time we spawn
  788. UpdateInventory( false );
  789. if( m_Shared.IsLoadoutUnavailable() )
  790. {
  791. VerifySOCache();
  792. }
  793. #endif
  794. // For the ratings, we don't need to bleed -- jdw
  795. // WE AINT GOT TIME TO BLEEED - mtw
  796. // Needed in Spawn for MP and Activate for SP
  797. SetBloodColor( DONT_BLEED );
  798. CreateSounds();
  799. pl.deadflag = false;
  800. RemoveSolidFlags( FSOLID_NOT_SOLID );
  801. RemoveEffects( EF_NODRAW );
  802. StopObserverMode();
  803. //GiveDefaultItems();
  804. m_nRenderFX = kRenderNormal;
  805. m_Local.m_iHideHUD = 0;
  806. AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.
  807. m_impactEnergyScale = PORTALPLAYER_PHYSDAMAGE_SCALE;
  808. RemoveFlag( FL_FROZEN );
  809. m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
  810. m_Local.m_bDucked = false;
  811. SetPlayerUnderwater(false);
  812. SetMaxSpeed( PORTAL_WALK_SPEED );
  813. #if USE_SLOWTIME
  814. m_pSlowTimeColorFX = CreateEntityByName( "color_correction" );
  815. if ( m_pSlowTimeColorFX )
  816. {
  817. m_pSlowTimeColorFX->KeyValue( "filename", "scripts/colorcorrection/fling_color.raw" );
  818. m_pSlowTimeColorFX->KeyValue( "StartDisabled", "1" );
  819. m_pSlowTimeColorFX->KeyValue( "fadeInDuration", "0.05" );
  820. m_pSlowTimeColorFX->KeyValue( "fadeOutDuration", "0.1" );
  821. m_pSlowTimeColorFX->KeyValue( "minfalloff", "0.0" );
  822. m_pSlowTimeColorFX->KeyValue( "maxfalloff", "0.0" );
  823. m_pSlowTimeColorFX->KeyValue( "maxWeight", "1.0" );
  824. m_pSlowTimeColorFX->SetAbsOrigin( GetAbsOrigin() );
  825. m_pSlowTimeColorFX->SetParent( this );
  826. DispatchSpawn( m_pSlowTimeColorFX );
  827. m_pSlowTimeColorFX->Activate();
  828. }
  829. #endif // USE_SLOWTIME
  830. SetMaxSpeed( sv_speed_normal.GetFloat() );
  831. m_vPrevGroundNormal = Vector(0,0,1);
  832. m_PortalLocal.m_PaintedPowerTimer.Invalidate();
  833. GivePortalPlayerItems();
  834. // Clear out taunt state on respawn
  835. m_bTauntRemoteView = false;
  836. m_hRemoteTauntCamera = NULL;
  837. m_nTeamTauntState = TEAM_TAUNT_NONE;
  838. m_bTrickFire = false;
  839. m_hTauntPartnerInRange = NULL;
  840. m_iSpawnCounter = !m_iSpawnCounter;
  841. // clear animation state
  842. m_PlayerAnimState->ClearAnimationState();
  843. if ( GameRules() && GameRules()->IsMultiplayer() )
  844. {
  845. bool bIsBlue = GetTeamNumber() == TEAM_BLUE;
  846. if ( IsFullyConnected() )
  847. {
  848. if ( bIsBlue )
  849. {
  850. IGameEvent * event = gameeventmanager->CreateEvent( "player_spawn_blue" );
  851. if ( event )
  852. {
  853. gameeventmanager->FireEvent( event );
  854. }
  855. }
  856. else
  857. {
  858. IGameEvent * event = gameeventmanager->CreateEvent( "player_spawn_orange" );
  859. if ( event )
  860. {
  861. gameeventmanager->FireEvent( event );
  862. }
  863. }
  864. }
  865. else if ( !PortalMPGameRules()->IsPlayerDataReceived( 0 ) || !PortalMPGameRules()->IsPlayerDataReceived( 1 ) )
  866. {
  867. if ( !engine->GetSplitScreenPlayerAttachToEdict( 1 ) && !engine->GetSplitScreenPlayerAttachToEdict( 2 ) )
  868. {
  869. if ( bIsBlue )
  870. {
  871. engine->ClientCommand( edict(), "playvideo_end_level_transition coop_bluebot_load 1" );
  872. }
  873. else
  874. {
  875. engine->ClientCommand( edict(), "playvideo_end_level_transition coop_orangebot_load 1" );
  876. }
  877. }
  878. }
  879. }
  880. // Want to render the player models in the world imposter views and water views.
  881. AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
  882. AddEffects( EF_SHADOWDEPTH_NOCACHE );
  883. // reset was dropped state
  884. m_bWasDroppedByOtherPlayerWhileTaunting = false;
  885. // Reset bounce count
  886. m_nBounceCount = 0;
  887. m_LastGroundBouncePlaneDistance = 0.0f;
  888. // remove conds and reset PIP
  889. m_Shared.RemoveAllCond();
  890. // init prev position
  891. m_vPrevPosition = GetAbsOrigin();
  892. #if !defined( _GAMECONSOLE )
  893. g_Portal2ResearchDataTracker.SetPlayerName( this );
  894. #endif // !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  895. }
  896. void CPortal_Player::Activate( void )
  897. {
  898. BaseClass::Activate();
  899. // For the ratings, we don't need to bleed -- jdw
  900. // WE AINT GOT TIME TO BLEEED - mtw
  901. // Needed in Spawn for MP and Activate for SP
  902. SetBloodColor( DONT_BLEED );
  903. m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
  904. SetMaxSpeed( sv_speed_normal.GetFloat() );
  905. // Turn off PIP for all players as a new level starts
  906. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  907. {
  908. CBasePlayer *pToPlayer = UTIL_PlayerByIndex( i );
  909. if ( pToPlayer )
  910. {
  911. engine->ClientCommand( pToPlayer->edict(), "-remote_view" );
  912. }
  913. }
  914. if ( GetModelPtr() )
  915. {
  916. ParseScriptedInteractions();
  917. }
  918. // Let's kill the player!
  919. if ( playtest_random_death.GetBool() )
  920. {
  921. flNextDeathTime = gpGlobals->curtime + random->RandomFloat( 0.5f*60.0f, 2*60.0f );
  922. }
  923. }
  924. void CPortal_Player::OnFullyConnected()
  925. {
  926. // Don't worry about waiting for the other player in dev 0
  927. if ( GameRules()->IsMultiplayer() )
  928. {
  929. // Waiting for the other player
  930. m_takedamage = DAMAGE_NO;
  931. pl.deadflag = true;
  932. m_lifeState = LIFE_DEAD;
  933. SetMoveType( MOVETYPE_NONE );
  934. // Set any splitscreen players associated as waiting too
  935. Assert( GetSplitScreenPlayers().Count() == 0 || GetSplitScreenPlayers().Count() == 1 );
  936. // Respawn
  937. SetThink( &CPortal_Player::PlayerTransitionCompleteThink );
  938. SetNextThink( gpGlobals->curtime + 1.0f );
  939. if ( !PortalMPGameRules()->IsPlayerDataReceived( 0 ) || !PortalMPGameRules()->IsPlayerDataReceived( 1 ) )
  940. {
  941. bool bIsCommunityCoopHub = PortalMPGameRules()->IsCommunityCoopHub();
  942. float flOtherPlayerTimeout = bIsCommunityCoopHub ? 0.0f : mp_wait_for_other_player_timeout.GetFloat();
  943. float flNotConnectingTimeout = bIsCommunityCoopHub ? 0.0f : mp_wait_for_other_player_notconnecting_timeout.GetFloat();
  944. // Timeout and spawn eventually if the other player doesn't connect
  945. SetNextThink( gpGlobals->curtime + flOtherPlayerTimeout ); // Wait 40 seconds for other players to connect
  946. SetContextThink( &CPortal_Player::PlayerCatchPatnerNotConnectingThink, gpGlobals->curtime + flNotConnectingTimeout, CATCHPATNERNOTCONNECTING_THINK_CONTEXT );
  947. }
  948. // Self is not in this list. With 1 splitscreen partner this list has 1 player
  949. for ( int i = 0; i < GetSplitScreenPlayers().Count(); ++i )
  950. {
  951. CPortal_Player *pPlayer = static_cast< CPortal_Player* >( GetSplitScreenPlayers()[ i ].Get() );
  952. if ( pPlayer )
  953. {
  954. pPlayer->OnFullyConnected();
  955. }
  956. }
  957. }
  958. else
  959. {
  960. // Single player just needs to shut down the transition video
  961. const char *szVideoCommand = "stopvideos_fadeout";
  962. char szClientCmd[256];
  963. Q_snprintf( szClientCmd, sizeof(szClientCmd), "%s %f\n", szVideoCommand, 1.5f );
  964. engine->ClientCommand( edict(), szClientCmd );
  965. ChallengePlayersReady();
  966. }
  967. m_bIsFullyConnected = true;
  968. }
  969. void CPortal_Player::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  970. {
  971. // On teleport, we send event for tracking fling achievements
  972. if ( eventType == NOTIFY_EVENT_TELEPORT )
  973. {
  974. CProp_Portal *pEnteredPortal = dynamic_cast<CProp_Portal*>( pNotify );
  975. IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_portaled" );
  976. if ( event )
  977. {
  978. event->SetInt( "userid", GetUserID() );
  979. event->SetBool( "portal2", pEnteredPortal->m_bIsPortal2 );
  980. gameeventmanager->FireEvent( event );
  981. }
  982. }
  983. BaseClass::NotifySystemEvent( pNotify, eventType, params );
  984. }
  985. void CPortal_Player::OnSave( IEntitySaveUtils *pUtils )
  986. {
  987. char const *pchSaveFile = engine->GetSaveFileName();
  988. bool bIsQuicksave = V_stricmp( pchSaveFile, "SAVE\\quick.sav" ) == 0;
  989. bool bIsAutosave = V_stricmp( pchSaveFile, "SAVE\\autosave.sav" ) == 0 || V_stricmp( pchSaveFile, "SAVE\\autosavedangerous.sav" ) == 0;
  990. if ( bIsAutosave || bIsQuicksave )
  991. {
  992. IGameEvent *event = gameeventmanager->CreateEvent( bIsQuicksave ? "quicksave" : "autosave" );
  993. if ( event )
  994. {
  995. gameeventmanager->FireEvent( event );
  996. }
  997. }
  998. BaseClass::OnSave( pUtils );
  999. }
  1000. void CPortal_Player::OnRestore( void )
  1001. {
  1002. BaseClass::OnRestore();
  1003. // HACK: Designers have added the ability to override the type of grabcontroller...
  1004. // these changes go across transitions if somebody forgets to change it back to default
  1005. // and are generating bugs. By request, we're reverting the state to default after
  1006. // each level transition in case some entity io fails to change it back.
  1007. if ( gpGlobals->eLoadType == MapLoad_Transition )
  1008. {
  1009. SetForcedGrabControllerType( FORCE_GRAB_CONTROLLER_DEFAULT );
  1010. }
  1011. // Saving is not allowed and they loaded from a save. Kill the player to prevent
  1012. // bogus score in challenge mode.
  1013. if ( GetBonusChallenge() != 0 )
  1014. {
  1015. // Make sure god mode is off
  1016. RemoveFlag( FL_GODMODE );
  1017. // Murder
  1018. CTakeDamageInfo info(NULL, this, FLT_MAX, 0 );
  1019. TakeDamage( info );
  1020. // Force cheats on to make sure their score won't get recorded
  1021. sv_cheats->SetValue( true );
  1022. }
  1023. }
  1024. //bool CPortal_Player::StartObserverMode( int mode )
  1025. //{
  1026. // //Do nothing.
  1027. //
  1028. // return false;
  1029. //}
  1030. void CPortal_Player::ClearScriptedInteractions( void )
  1031. {
  1032. m_ScriptedInteractions.RemoveAll();
  1033. }
  1034. void CPortal_Player::ParseScriptedInteractions( void )
  1035. {
  1036. // Already parsed them?
  1037. if ( m_ScriptedInteractions.Count() )
  1038. return;
  1039. // Parse the model's key values and find any dynamic interactions
  1040. KeyValues *modelKeyValues = new KeyValues("");
  1041. CUtlBuffer buf( 1024, 0, CUtlBuffer::TEXT_BUFFER );
  1042. KeyValues::AutoDelete autodelete_key( modelKeyValues );
  1043. if (! modelinfo->GetModelKeyValue( GetModel(), buf ))
  1044. return;
  1045. if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), buf ) )
  1046. {
  1047. // Do we have a dynamic interactions section?
  1048. KeyValues *pkvInteractions = modelKeyValues->FindKey("dynamic_interactions");
  1049. if ( pkvInteractions )
  1050. {
  1051. KeyValues *pkvNode = pkvInteractions->GetFirstSubKey();
  1052. while ( pkvNode )
  1053. {
  1054. ScriptedNPCInteraction_t sInteraction;
  1055. sInteraction.iszInteractionName = AllocPooledString( pkvNode->GetName() );
  1056. // Trigger method
  1057. const char *pszKeyString = pkvNode->GetString( "trigger", NULL );
  1058. if ( pszKeyString )
  1059. {
  1060. if ( !Q_strncmp( pszKeyString, "auto_in_combat", 14) )
  1061. {
  1062. sInteraction.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT;
  1063. }
  1064. }
  1065. // Loop Break trigger method
  1066. pszKeyString = pkvNode->GetString( "loop_break_trigger", NULL );
  1067. if ( pszKeyString )
  1068. {
  1069. char szTrigger[256];
  1070. Q_strncpy( szTrigger, pszKeyString, sizeof(szTrigger) );
  1071. char *pszParam = strtok( szTrigger, " " );
  1072. while (pszParam)
  1073. {
  1074. if ( !Q_strncmp( pszParam, "on_damage", 9) )
  1075. {
  1076. sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_DAMAGE;
  1077. }
  1078. if ( !Q_strncmp( pszParam, "on_flashlight_illum", 19) )
  1079. {
  1080. sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM;
  1081. }
  1082. pszParam = strtok(NULL," ");
  1083. }
  1084. }
  1085. // Origin
  1086. pszKeyString = pkvNode->GetString( "origin_relative", "0 0 0" );
  1087. UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), pszKeyString );
  1088. // Angles
  1089. pszKeyString = pkvNode->GetString( "angles_relative", NULL );
  1090. if ( pszKeyString )
  1091. {
  1092. sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES;
  1093. UTIL_StringToVector( sInteraction.angRelativeAngles.Base(), pszKeyString );
  1094. }
  1095. // Velocity
  1096. pszKeyString = pkvNode->GetString( "velocity_relative", NULL );
  1097. if ( pszKeyString )
  1098. {
  1099. sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_VELOCITY;
  1100. UTIL_StringToVector( sInteraction.vecRelativeVelocity.Base(), pszKeyString );
  1101. }
  1102. // Camera Distance
  1103. sInteraction.flCameraDistance = pkvNode->GetFloat( "distance_camera", portal_tauntcam_dist.GetFloat() );
  1104. // Camera Angles
  1105. pszKeyString = pkvNode->GetString( "angles_camera", NULL );
  1106. if ( pszKeyString )
  1107. {
  1108. UTIL_StringToVector( sInteraction.angCameraAngles.Base(), pszKeyString );
  1109. }
  1110. else
  1111. {
  1112. sInteraction.angCameraAngles[ PITCH ] = 20.0f;
  1113. sInteraction.angCameraAngles[ YAW ] = 160.0f;
  1114. sInteraction.angCameraAngles[ ROLL ] = 0.0f;
  1115. }
  1116. // Entry Sequence
  1117. pszKeyString = pkvNode->GetString( "entry_sequence", NULL );
  1118. if ( pszKeyString )
  1119. {
  1120. sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString( pszKeyString );
  1121. }
  1122. // Entry Activity
  1123. pszKeyString = pkvNode->GetString( "entry_activity", NULL );
  1124. if ( pszKeyString )
  1125. {
  1126. sInteraction.sPhases[SNPCINT_ENTRY].iActivity = ACT_INVALID;
  1127. DevWarning( "Activities not supported for player scripted sequences." );
  1128. }
  1129. // Sequence
  1130. pszKeyString = pkvNode->GetString( "sequence", NULL );
  1131. if ( pszKeyString )
  1132. {
  1133. sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( pszKeyString );
  1134. }
  1135. // Activity
  1136. pszKeyString = pkvNode->GetString( "activity", NULL );
  1137. if ( pszKeyString )
  1138. {
  1139. sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = ACT_INVALID;
  1140. DevWarning( "Activities not supported for player scripted sequences." );
  1141. }
  1142. // Exit Sequence
  1143. pszKeyString = pkvNode->GetString( "exit_sequence", NULL );
  1144. if ( pszKeyString )
  1145. {
  1146. sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString( pszKeyString );
  1147. }
  1148. // Exit Activity
  1149. pszKeyString = pkvNode->GetString( "exit_activity", NULL );
  1150. if ( pszKeyString )
  1151. {
  1152. sInteraction.sPhases[SNPCINT_EXIT].iActivity = ACT_INVALID;
  1153. DevWarning( "Activities not supported for player scripted sequences." );
  1154. }
  1155. // Delay
  1156. sInteraction.flDelay = pkvNode->GetFloat( "delay", 10.0 );
  1157. // Delta
  1158. sInteraction.flDistSqr = pkvNode->GetFloat( "origin_max_delta", (DSS_MAX_DIST * DSS_MAX_DIST) );
  1159. // Loop?
  1160. if ( pkvNode->GetFloat( "loop_in_action", 0 ) )
  1161. {
  1162. sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION;
  1163. }
  1164. // Fixup position?
  1165. pszKeyString = pkvNode->GetString( "dont_teleport_at_end", NULL );
  1166. if ( pszKeyString )
  1167. {
  1168. if ( !Q_stricmp( pszKeyString, "me" ) || !Q_stricmp( pszKeyString, "both" ) )
  1169. {
  1170. sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
  1171. }
  1172. else if ( !Q_stricmp( pszKeyString, "them" ) || !Q_stricmp( pszKeyString, "both" ) )
  1173. {
  1174. sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
  1175. }
  1176. }
  1177. // Needs a weapon?
  1178. pszKeyString = pkvNode->GetString( "needs_weapon", NULL );
  1179. if ( pszKeyString )
  1180. {
  1181. if ( !Q_strncmp( pszKeyString, "ME", 2 ) )
  1182. {
  1183. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME;
  1184. }
  1185. else if ( !Q_strncmp( pszKeyString, "THEM", 4 ) )
  1186. {
  1187. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM;
  1188. }
  1189. else if ( !Q_strncmp( pszKeyString, "BOTH", 4 ) )
  1190. {
  1191. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME;
  1192. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM;
  1193. }
  1194. }
  1195. // Specific weapon types
  1196. pszKeyString = pkvNode->GetString( "weapon_mine", NULL );
  1197. if ( pszKeyString )
  1198. {
  1199. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME;
  1200. sInteraction.iszMyWeapon = AllocPooledString( pszKeyString );
  1201. }
  1202. pszKeyString = pkvNode->GetString( "weapon_theirs", NULL );
  1203. if ( pszKeyString )
  1204. {
  1205. sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM;
  1206. sInteraction.iszTheirWeapon = AllocPooledString( pszKeyString );
  1207. }
  1208. // Add it to the list
  1209. AddScriptedInteraction( &sInteraction );
  1210. // Move to next interaction
  1211. pkvNode = pkvNode->GetNextKey();
  1212. }
  1213. }
  1214. }
  1215. }
  1216. void CPortal_Player::AddScriptedInteraction( ScriptedNPCInteraction_t *pInteraction )
  1217. {
  1218. int nNewIndex = m_ScriptedInteractions.AddToTail();
  1219. if ( ai_debug_dyninteractions.GetBool() )
  1220. {
  1221. Msg("%s(%s): Added dynamic interaction: %s\n", GetClassname(), GetDebugName(), STRING(pInteraction->iszInteractionName) );
  1222. }
  1223. // Copy the interaction over
  1224. ScriptedNPCInteraction_t *pNewInt = &(m_ScriptedInteractions[nNewIndex]);
  1225. memcpy( pNewInt, pInteraction, sizeof(ScriptedNPCInteraction_t) );
  1226. // Calculate the local to world matrix
  1227. m_ScriptedInteractions[nNewIndex].matDesiredLocalToWorld.SetupMatrixOrgAngles( pInteraction->vecRelativeOrigin, pInteraction->angRelativeAngles );
  1228. }
  1229. void CPortal_Player::FireConcept( const char *pConcept )
  1230. {
  1231. // Since the player doesn't really speak we are just shortcutting this and having the sphere speak these lines directly.
  1232. CAI_BaseActor *pSphere = dynamic_cast<CAI_BaseActor*>( gEntList.FindEntityByClassname( NULL, "npc_personality_core" ) );
  1233. if ( pSphere == NULL )
  1234. return;
  1235. pSphere->Speak( pConcept );
  1236. }
  1237. void CPortal_Player::SetTeamTauntState( int nTeamTauntState )
  1238. {
  1239. if ( m_nTeamTauntState == nTeamTauntState )
  1240. return;
  1241. m_nTeamTauntState = nTeamTauntState;
  1242. }
  1243. bool CPortal_Player::ValidatePlayerModel( const char *pModel )
  1244. {
  1245. if ( !Q_stricmp( GetPlayerModelName(), pModel ) )
  1246. return true;
  1247. if ( !Q_stricmp( g_pszPlayerModel, pModel ) )
  1248. return true;
  1249. return false;
  1250. }
  1251. void CPortal_Player::SetPlayerModel( void )
  1252. {
  1253. const char *szModelName = NULL;
  1254. const char *pszCurrentModelName = modelinfo->GetModelName( GetModel());
  1255. szModelName = engine->GetClientConVarValue( entindex(), "cl_playermodel" );
  1256. if ( ValidatePlayerModel( szModelName ) == false )
  1257. {
  1258. char szReturnString[512];
  1259. if ( ValidatePlayerModel( pszCurrentModelName ) == false )
  1260. {
  1261. pszCurrentModelName = GetPlayerModelName();
  1262. }
  1263. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName );
  1264. engine->ClientCommand ( edict(), szReturnString );
  1265. szModelName = pszCurrentModelName;
  1266. }
  1267. int modelIndex = modelinfo->GetModelIndex( szModelName );
  1268. if ( modelIndex == -1 )
  1269. {
  1270. szModelName = GetPlayerModelName();
  1271. char szReturnString[512];
  1272. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName );
  1273. engine->ClientCommand ( edict(), szReturnString );
  1274. }
  1275. bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
  1276. CBaseEntity::SetAllowPrecache( true );
  1277. PrecacheModel( GetPlayerModelName() );
  1278. CBaseEntity::SetAllowPrecache( allowPrecache );
  1279. SetModel( GetPlayerModelName() );
  1280. if ( GameRules()->IsMultiplayer() )
  1281. {
  1282. if ( g_nPortal2PromoFlags & PORTAL2_PROMO_SKINS )
  1283. {
  1284. m_nSkin = 1;
  1285. }
  1286. else
  1287. {
  1288. m_nSkin = 0;
  1289. }
  1290. }
  1291. m_iPlayerSoundType.Set( PLAYER_SOUNDS_CITIZEN );
  1292. }
  1293. bool CPortal_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex )
  1294. {
  1295. bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
  1296. return bRet;
  1297. }
  1298. //-----------------------------------------------------------------------------
  1299. // Purpose: Play a one-shot scene
  1300. // Input :
  1301. // Output :
  1302. //-----------------------------------------------------------------------------
  1303. float CPortal_Player::PlayScene( const char *pszScene, float flDelay, AI_Response *response, IRecipientFilter *filter )
  1304. {
  1305. MDLCACHE_CRITICAL_SECTION();
  1306. float flDuration = InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response, true, filter );
  1307. m_Shared.m_flTauntRemoveTime = gpGlobals->curtime + flDuration + 0.5f;
  1308. m_Shared.AddCond( PORTAL_COND_TAUNTING );
  1309. if ( V_strstr( pszScene, "_idle" ) != NULL )
  1310. {
  1311. if ( m_nTeamTauntState < TEAM_TAUNT_NEED_PARTNER )
  1312. {
  1313. SetTeamTauntState( TEAM_TAUNT_NEED_PARTNER );
  1314. }
  1315. }
  1316. // Fire off achievements for any taunts and keep track of the number of times we do it
  1317. if ( GameRules()->IsMultiplayer() )
  1318. {
  1319. CPortalMPStats *pStats = GetPortalMPStats();
  1320. if ( response && response->m_szMatchingRule && pStats )
  1321. {
  1322. bool bHelmetOpener = false;
  1323. // These match the exact name of the rule we want to fire achievements based on minus ballbot/eggbot prefix
  1324. if ( V_strstr( response->m_szMatchingRule, "teamgesturehighfive_success") != NULL )
  1325. {
  1326. UTIL_RecordAchievementEvent( "ACH.TAUNTS[1]", this );
  1327. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_HIGHFIVE );
  1328. }
  1329. else if ( V_strstr( response->m_szMatchingRule, "gesturesmallwave") != NULL ||
  1330. V_strstr( response->m_szMatchingRule, "gestureportalgunsmallwave") != NULL )
  1331. {
  1332. UTIL_RecordAchievementEvent( "ACH.TAUNTS[2]", this );
  1333. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_WAVE );
  1334. }
  1335. else if ( V_strstr( response->m_szMatchingRule, "teamgesturerps_success") != NULL )
  1336. {
  1337. UTIL_RecordAchievementEvent( "ACH.TAUNTS[3]", this );
  1338. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_RPS );
  1339. }
  1340. else if ( V_strstr( response->m_szMatchingRule, "gesturelaugh") != NULL )
  1341. {
  1342. UTIL_RecordAchievementEvent( "ACH.TAUNTS[4]", this );
  1343. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_LAUGH );
  1344. }
  1345. else if ( V_strstr( response->m_szMatchingRule, "gesturerobotdance") != NULL )
  1346. {
  1347. UTIL_RecordAchievementEvent( "ACH.TAUNTS[5]", this );
  1348. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_ROBOTDANCE );
  1349. }
  1350. else if ( V_strstr( response->m_szMatchingRule, "teamgestureteamhug_success") != NULL )
  1351. {
  1352. UTIL_RecordAchievementEvent( "ACH.TAUNTS[7]", this );
  1353. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_HUG );
  1354. }
  1355. else if ( V_strstr( response->m_szMatchingRule, "gesturetrickfire") != NULL )
  1356. {
  1357. UTIL_RecordAchievementEvent( "ACH.TAUNTS[8]", this );
  1358. m_bTrickFire = true;
  1359. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_TRICKFIRE );
  1360. }
  1361. else if ( V_strstr( response->m_szMatchingRule, "gesturebasketball") != NULL )
  1362. {
  1363. if ( GetTeamNumber() == TEAM_BLUE )
  1364. {
  1365. bHelmetOpener = true;
  1366. }
  1367. }
  1368. else
  1369. {
  1370. bool bEggTease = ( V_strstr( response->m_szMatchingRule, "teamgestureteameggtease_success") != NULL );
  1371. bool bBallTease = ( V_strstr( response->m_szMatchingRule, "teamgestureteamballtease_success") != NULL );
  1372. if ( bEggTease || bBallTease )
  1373. {
  1374. UTIL_RecordAchievementEvent( "ACH.TAUNTS[6]", this );
  1375. pStats->IncrementPlayerTauntsUsedMap( this, TAUNT_CORETEASE );
  1376. if ( bEggTease && GetTeamNumber() == TEAM_BLUE )
  1377. {
  1378. bHelmetOpener = true;
  1379. }
  1380. }
  1381. }
  1382. if ( bHelmetOpener )
  1383. {
  1384. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( "weapon_promo_helmet_ball", 0 );
  1385. if ( pWeapon )
  1386. {
  1387. pWeapon->ResetSequence( pWeapon->LookupSequence( "taunt_teamEggTease" ) );
  1388. }
  1389. }
  1390. }
  1391. if ( ( V_strstr( pszScene, "rps") != NULL ) && ( V_strstr( pszScene, "win") != NULL ) )
  1392. {
  1393. PortalMPGameRules()->PlayerWinRPS( this );
  1394. }
  1395. }
  1396. return flDuration;
  1397. }
  1398. //-----------------------------------------------------------------------------
  1399. // Purpose:
  1400. //-----------------------------------------------------------------------------
  1401. #if USE_SLOWTIME
  1402. void CPortal_Player::StartSlowingTime( float flDuration )
  1403. {
  1404. if( g_pGameRules->IsMultiplayer() )
  1405. return; //no slow time in multiplayer
  1406. IGameEvent *event = gameeventmanager->CreateEvent( "slowtime" );
  1407. if ( event )
  1408. {
  1409. gameeventmanager->FireEvent( event );
  1410. }
  1411. // Start up our sounds
  1412. EmitSound( "Player.SlowTime_Start" );
  1413. EmitSound( "Player.SlowTime_Loop" );
  1414. m_bHasPlayedSlowTimeStopSound = false;
  1415. m_PortalLocal.m_bSlowingTime = true;
  1416. // Make sure we start at out max if we're already higher
  1417. if ( m_PortalLocal.m_flSlowTimeMaximum != flDuration )
  1418. {
  1419. m_PortalLocal.m_flSlowTimeMaximum = ( flDuration > 0.0f ) ? flDuration : slowtime_max.GetFloat();
  1420. m_PortalLocal.m_flSlowTimeRemaining = m_PortalLocal.m_flSlowTimeMaximum;
  1421. }
  1422. GameTimescale()->SetCurrentTimescale( slowtime_speed.GetFloat() );
  1423. SetFOV( this, 70.0f, 0.05f );
  1424. /*
  1425. if ( m_pSlowTimeColorFX )
  1426. {
  1427. variant_t emptyVariant;
  1428. m_pSlowTimeColorFX->AcceptInput( "Enable", this, this, emptyVariant, USE_TOGGLE );
  1429. }
  1430. */
  1431. // Reset our fire times
  1432. CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( GetActiveWeapon() );
  1433. if ( pPortalGun )
  1434. {
  1435. pPortalGun->ResetRefireTime();
  1436. }
  1437. FirePlayerProxyOutput( "OnStartSlowingTime", variant_t(), this, this );
  1438. }
  1439. //-----------------------------------------------------------------------------
  1440. // Purpose:
  1441. //-----------------------------------------------------------------------------
  1442. void CPortal_Player::StopSlowingTime( void )
  1443. {
  1444. m_PortalLocal.m_bSlowingTime = false;
  1445. GameTimescale()->SetDesiredTimescale( 1.0f );
  1446. SetFOV( this, 0.0f, 0.5f );
  1447. /*
  1448. if ( m_pSlowTimeColorFX )
  1449. {
  1450. variant_t emptyVariant;
  1451. m_pSlowTimeColorFX->AcceptInput( "Disable", this, this, emptyVariant, USE_TOGGLE );
  1452. }
  1453. */
  1454. // Stop our looping sound
  1455. StopSound( entindex(), CHAN_STATIC, "Player.SlowTime_Loop" );
  1456. m_bHasPlayedSlowTimeStopSound = true;
  1457. FirePlayerProxyOutput( "OnStopSlowingTime", variant_t(), this, this );
  1458. }
  1459. #endif // USE_SLOWTIME
  1460. void CPortal_Player::ShowViewFinder( void )
  1461. {
  1462. if( !g_pGameRules->IsMultiplayer() )
  1463. return;
  1464. m_PortalLocal.m_bShowingViewFinder = true;
  1465. }
  1466. void CPortal_Player::HideViewFinder( void )
  1467. {
  1468. m_PortalLocal.m_bShowingViewFinder = false;
  1469. }
  1470. void CPortal_Player::PlayCoopPingEffect( void )
  1471. {
  1472. Vector vecForward;
  1473. AngleVectors( EyeAngles(), &vecForward );
  1474. // Hit anything they can 'see' thats directly down their crosshair
  1475. trace_t tr;
  1476. Ray_t ray;
  1477. ray.Init( EyePosition(), EyePosition() + vecForward*MAX_COORD_FLOAT );
  1478. bool bPortalBulletTrace = g_bBulletPortalTrace;
  1479. g_bBulletPortalTrace = true;
  1480. CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
  1481. traceFilter.AddClassnameToIgnore( "projected_wall_entity" );
  1482. traceFilter.AddClassnameToIgnore( "player" );
  1483. UTIL_Portal_TraceRay( ray, MASK_OPAQUE_AND_NPCS, &traceFilter, &tr );
  1484. g_bBulletPortalTrace = bPortalBulletTrace;
  1485. if ( tr.DidHit() )
  1486. {
  1487. IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_ping" );
  1488. if ( event )
  1489. {
  1490. event->SetInt("userid", GetUserID() );
  1491. event->SetFloat("ping_x", tr.endpos.x );
  1492. event->SetFloat("ping_y", tr.endpos.y );
  1493. event->SetFloat("ping_z", tr.endpos.z );
  1494. gameeventmanager->FireEvent( event );
  1495. }
  1496. CDisablePredictionFiltering filter(true);
  1497. DispatchParticleEffect( COOP_PING_PARTICLE_NAME, tr.endpos, vec3_angle );
  1498. EmitSound( COOP_PING_SOUNDSCRIPT_NAME );
  1499. UTIL_DecalTrace( &tr, "Portal2.CoopPingDecal" );
  1500. CReliableBroadcastRecipientFilter allplayers;
  1501. if ( sv_portal_coop_ping_indicator_show_to_all_players.GetBool() == false )
  1502. {
  1503. allplayers.RemoveRecipient( this );
  1504. }
  1505. UserMessageBegin( allplayers, "HudPingIndicator" );
  1506. WRITE_FLOAT( tr.endpos.x );
  1507. WRITE_FLOAT( tr.endpos.y );
  1508. WRITE_FLOAT( tr.endpos.z );
  1509. MessageEnd();
  1510. }
  1511. else
  1512. {
  1513. Warning( "Attempted to ping for player, but trace failed to hit anything.\n" );
  1514. }
  1515. // Note this in the player proxy
  1516. FirePlayerProxyOutput( "OnCoopPing", variant_t(), this, this );
  1517. }
  1518. //-----------------------------------------------------------------------------
  1519. // Purpose:
  1520. //-----------------------------------------------------------------------------
  1521. void CPortal_Player::PreThink( void )
  1522. {
  1523. QAngle vOldAngles = GetLocalAngles();
  1524. QAngle vTempAngles = GetLocalAngles();
  1525. vTempAngles = EyeAngles();
  1526. if ( vTempAngles[PITCH] > 180.0f )
  1527. {
  1528. vTempAngles[PITCH] -= 360.0f;
  1529. }
  1530. SetLocalAngles( vTempAngles );
  1531. // Let's kill the player!
  1532. if ( playtest_random_death.GetBool() )
  1533. {
  1534. if ( flNextDeathTime < gpGlobals->curtime )
  1535. {
  1536. TakeDamage( CTakeDamageInfo( this, this, NULL, Vector(0,0,100), WorldSpaceCenter(), 1000.0f, DMG_CLUB ) );
  1537. flNextDeathTime = gpGlobals->curtime + random->RandomFloat( 0.5f*60.0f, 2*60.0f );
  1538. }
  1539. }
  1540. // Decay the air control
  1541. if ( IsSuppressingAirControl() )
  1542. {
  1543. m_PortalLocal.m_flAirControlSupressionTime -= 1000.0f * gpGlobals->frametime;
  1544. if ( m_PortalLocal.m_flAirControlSupressionTime < 0.0f )
  1545. {
  1546. m_PortalLocal.m_flAirControlSupressionTime = 0.0f;
  1547. }
  1548. }
  1549. if ( m_Local.m_bSlowMovement && m_Local.m_fTBeamEndTime != 0.0f && gpGlobals->curtime > m_Local.m_fTBeamEndTime + 1.0f )
  1550. {
  1551. m_Local.m_bSlowMovement = false;
  1552. SetGravity( 1.0f );
  1553. if ( VPhysicsGetObject() )
  1554. {
  1555. VPhysicsGetObject()->EnableGravity( true );
  1556. }
  1557. }
  1558. BaseClass::PreThink();
  1559. if( (m_afButtonPressed & IN_JUMP) )
  1560. {
  1561. Jump();
  1562. FirePlayerProxyOutput( "OnJump", variant_t(), this, this );
  1563. }
  1564. if( (m_afButtonPressed & IN_DUCK) )
  1565. {
  1566. FirePlayerProxyOutput( "OnDuck", variant_t(), this, this );
  1567. }
  1568. if ( m_afButtonPressed & IN_GRENADE1 )
  1569. {
  1570. if ( !m_PortalLocal.m_bZoomedIn )
  1571. {
  1572. ZoomIn();
  1573. }
  1574. }
  1575. else if ( m_afButtonPressed & IN_GRENADE2 )
  1576. {
  1577. if ( m_PortalLocal.m_bZoomedIn )
  1578. {
  1579. ZoomOut();
  1580. }
  1581. }
  1582. else if ( m_afButtonPressed & IN_ZOOM )
  1583. {
  1584. if ( !m_PortalLocal.m_bZoomedIn )
  1585. {
  1586. ZoomIn();
  1587. }
  1588. else
  1589. {
  1590. ZoomOut();
  1591. }
  1592. }
  1593. #if USE_SLOWTIME
  1594. // Update slow time
  1595. if ( m_PortalLocal.m_bSlowingTime )
  1596. {
  1597. float flDrainAmount = ( gpGlobals->frametime / 0.1f );
  1598. m_PortalLocal.m_flSlowTimeRemaining = clamp( m_PortalLocal.m_flSlowTimeRemaining - flDrainAmount, 0.0f, slowtime_max.GetFloat() );
  1599. if ( m_bHasPlayedSlowTimeStopSound == false )
  1600. {
  1601. if ( m_PortalLocal.m_flSlowTimeRemaining < 1.5f )
  1602. {
  1603. EmitSound( "Player.SlowTime_Stop" );
  1604. m_bHasPlayedSlowTimeStopSound = true;
  1605. }
  1606. }
  1607. if ( m_PortalLocal.m_flSlowTimeRemaining <= 0.0f )
  1608. {
  1609. StopSlowingTime();
  1610. }
  1611. }
  1612. else
  1613. {
  1614. m_PortalLocal.m_flSlowTimeRemaining = clamp( m_PortalLocal.m_flSlowTimeRemaining + ( gpGlobals->frametime * slowtime_regen_per_second.GetFloat() ), 0.0f, m_PortalLocal.m_flSlowTimeMaximum );
  1615. }
  1616. // Modulate time!
  1617. if ( GlobalEntity_GetState( "slowtime_disabled" ) != GLOBAL_ON )
  1618. {
  1619. if ( m_afButtonPressed & IN_SLOWTIME )
  1620. {
  1621. if ( m_PortalLocal.m_bSlowingTime )
  1622. {
  1623. // Turn the effect off
  1624. StopSlowingTime();
  1625. }
  1626. else
  1627. {
  1628. if ( slowtime_must_refill.GetBool() == false || m_PortalLocal.m_flSlowTimeRemaining >= slowtime_max.GetFloat() )
  1629. {
  1630. StartSlowingTime( slowtime_max.GetFloat() );
  1631. }
  1632. else
  1633. {
  1634. EmitSound( "PortalPlayer.UseDeny" );
  1635. }
  1636. }
  1637. }
  1638. }
  1639. #endif // USE_SLOWTIME
  1640. if ( GameRules()->IsMultiplayer() )
  1641. {
  1642. // Send a ping
  1643. if ( m_afButtonPressed & IN_COOP_PING )
  1644. {
  1645. if ( ( m_flLastPingTime + sv_portal_coop_ping_cooldown_time.GetFloat() ) < gpGlobals->curtime )
  1646. {
  1647. PlayCoopPingEffect();
  1648. m_flLastPingTime = gpGlobals->curtime;
  1649. }
  1650. }
  1651. m_bPingDisabled = ( GetTeamNumber() == TEAM_BLUE && GlobalEntity_GetState( "no_pinging_blue" ) == GLOBAL_ON ||
  1652. GetTeamNumber() == TEAM_RED && GlobalEntity_GetState( "no_pinging_orange" ) == GLOBAL_ON );
  1653. m_bTauntDisabled = ( GetTeamNumber() == TEAM_BLUE && GlobalEntity_GetState( "no_taunting_blue" ) == GLOBAL_ON ||
  1654. GetTeamNumber() == TEAM_RED && GlobalEntity_GetState( "no_taunting_orange" ) == GLOBAL_ON );
  1655. if ( !m_bTauntDisabled )
  1656. {
  1657. CBaseEntity *pGround = GetGroundEntity();
  1658. if ( pGround && !pGround->GetAbsVelocity().IsZero() )
  1659. {
  1660. m_bTauntDisabled = true;
  1661. }
  1662. }
  1663. bool bHasPartnerInRange = false;
  1664. if ( !m_bTauntDisabled )
  1665. {
  1666. CPortal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherConnectedPlayer( this ) );
  1667. if ( pOtherPlayer )
  1668. {
  1669. if ( pOtherPlayer->m_nTeamTauntState == TEAM_TAUNT_NEED_PARTNER )
  1670. {
  1671. Vector vInitiatorPos, vAcceptorPos;
  1672. QAngle angInitiatorAng, angAcceptorAng;
  1673. bHasPartnerInRange = ValidateTeamTaunt( pOtherPlayer, vInitiatorPos, angInitiatorAng, vAcceptorPos, angAcceptorAng );
  1674. }
  1675. if ( bHasPartnerInRange )
  1676. {
  1677. m_hTauntPartnerInRange = pOtherPlayer;
  1678. }
  1679. }
  1680. }
  1681. if ( !bHasPartnerInRange )
  1682. {
  1683. m_hTauntPartnerInRange = NULL;
  1684. }
  1685. }
  1686. if ( GetGroundEntity() )
  1687. {
  1688. m_nPortalsEnteredInAirFlags = 0;
  1689. }
  1690. UpdateVMGrab( m_hAttachedObject );
  1691. //Reset bullet force accumulator, only lasts one frame
  1692. m_vecTotalBulletForce = vec3_origin;
  1693. SetLocalAngles( vOldAngles );
  1694. // Cache the velocity before impact
  1695. if( engine->HasPaintmap() )
  1696. m_PortalLocal.m_vPreUpdateVelocity = GetAbsVelocity();
  1697. // Update the painted power
  1698. UpdatePaintedPower();
  1699. // Fade the input scale back in if we lost some
  1700. UpdateAirInputScaleFadeIn();
  1701. // Attempt to resize the hull if there's a pending hull resize
  1702. TryToChangeCollisionBounds( m_PortalLocal.m_CachedStandHullMinAttempt,
  1703. m_PortalLocal.m_CachedStandHullMaxAttempt,
  1704. m_PortalLocal.m_CachedDuckHullMinAttempt,
  1705. m_PortalLocal.m_CachedDuckHullMaxAttempt );
  1706. // reset remote view
  1707. if ( !m_Shared.InCond( PORTAL_COND_TAUNTING ) && m_bTauntRemoteViewFOVFixup )
  1708. {
  1709. m_bTauntRemoteViewFOVFixup = false;
  1710. SetFOV( this, 0, 0.0f, 0 );
  1711. }
  1712. if( m_hAttachedObject && !m_pGrabSound )
  1713. {
  1714. CSoundEnvelopeController& controller = CSoundEnvelopeController::GetController();
  1715. CPASAttenuationFilter filter( this );
  1716. char const* soundName = GetActivePortalWeapon() ? "PortalPlayer.ObjectUse" : "PortalPlayer.ObjectUseNoGun";
  1717. m_pGrabSound = controller.SoundCreate( filter, entindex(), soundName );
  1718. controller.Play( m_pGrabSound, VOL_NORM, PITCH_NORM );
  1719. }
  1720. if( !m_hAttachedObject && m_pGrabSound )
  1721. {
  1722. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1723. controller.Shutdown( m_pGrabSound );
  1724. controller.SoundDestroy( m_pGrabSound );
  1725. m_pGrabSound = NULL;
  1726. }
  1727. }
  1728. void CPortal_Player::PlayerDeathThink( void )
  1729. {
  1730. float flForward;
  1731. SetNextThink( gpGlobals->curtime + 0.1f );
  1732. // wait until the crush animation is over before we respawn the player
  1733. if ( m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) || m_Shared.InCond( PORTAL_COND_DEATH_GIB ) )
  1734. {
  1735. return;
  1736. }
  1737. if (m_lifeState == LIFE_DYING)
  1738. {
  1739. m_bSpawnFromDeath = true;
  1740. }
  1741. // Clear any painted powers
  1742. CleansePaint();
  1743. if (GetFlags() & FL_ONGROUND)
  1744. {
  1745. flForward = GetAbsVelocity().Length() - 20;
  1746. if (flForward <= 0)
  1747. {
  1748. SetAbsVelocity( vec3_origin );
  1749. }
  1750. else
  1751. {
  1752. Vector vecNewVelocity = GetAbsVelocity();
  1753. VectorNormalize( vecNewVelocity );
  1754. vecNewVelocity *= flForward;
  1755. SetAbsVelocity( vecNewVelocity );
  1756. }
  1757. }
  1758. if ( HasWeapons() )
  1759. {
  1760. // we drop the guns here because weapons that have an area effect and can kill their user
  1761. // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
  1762. // player class sometimes is freed. It's safer to manipulate the weapons once we know
  1763. // we aren't calling into any of their code anymore through the player pointer.
  1764. PackDeadPlayerItems();
  1765. }
  1766. // We're not playing death animations right now-- no need to finish the cycle.
  1767. // If we add these to portal MP in the future, this block will let them finish before
  1768. // the player respawns.
  1769. #if 0
  1770. if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING))
  1771. {
  1772. StudioFrameAdvance( );
  1773. m_iRespawnFrames++;
  1774. if ( m_iRespawnFrames < 60 ) // animations should be no longer than this
  1775. return;
  1776. }
  1777. #endif
  1778. if (m_lifeState == LIFE_DYING)
  1779. m_lifeState = LIFE_DEAD;
  1780. StopAnimation();
  1781. // AddEffects( EF_NOINTERP );
  1782. m_flPlaybackRate = 0.0;
  1783. int fAnyButtonDown = (m_nButtons & ~IN_SCORE);
  1784. // Strip out the duck key from this check if it's toggled
  1785. if ( (fAnyButtonDown & IN_DUCK) && GetToggledDuckState())
  1786. {
  1787. fAnyButtonDown &= ~IN_DUCK;
  1788. }
  1789. // Strip out zoom toggle
  1790. fAnyButtonDown &= ~IN_ZOOM;
  1791. if ( GameRules()->IsMultiplayer() == false && sp_fade_and_force_respawn.GetBool() )
  1792. {
  1793. const float flFadeAndResapwnTime = 3.0f;
  1794. color32 clr;
  1795. clr.r = clr.g = clr.b = 0;
  1796. clr.a = 255;
  1797. UTIL_ScreenFade( this, clr, flFadeAndResapwnTime, flFadeAndResapwnTime + 1.0f, FFADE_OUT | FFADE_STAYOUT );
  1798. if ( gpGlobals->curtime > m_flDeathTime + flFadeAndResapwnTime )
  1799. {
  1800. RespawnPlayer();
  1801. return;
  1802. }
  1803. }
  1804. bool bMultiplayerForceRespawn = ( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() > 0 );
  1805. // wait for all buttons released
  1806. if ( m_lifeState == LIFE_DEAD )
  1807. {
  1808. if ( ( fAnyButtonDown && !bMultiplayerForceRespawn ) || gpGlobals->curtime < m_flDeathTime + PORTAL_RESPAWN_DELAY )
  1809. return;
  1810. if ( g_pGameRules->FPlayerCanRespawn( this ) )
  1811. {
  1812. m_lifeState = LIFE_RESPAWNABLE;
  1813. }
  1814. return;
  1815. }
  1816. // if the player has been dead for one second longer than allowed by forcerespawn,
  1817. // forcerespawn isn't on. Send the player off to an intermission camera until they
  1818. // choose to respawn.
  1819. if ( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() == 0 && gpGlobals->curtime > m_flDeathTime + DEATH_ANIMATION_TIME && !IsObserver() )
  1820. {
  1821. // go to dead camera.
  1822. StartObserverMode( m_iObserverLastMode );
  1823. }
  1824. // wait for any button down, or mp_forcerespawn is set and the respawn time is up
  1825. if ( ( fAnyButtonDown || bMultiplayerForceRespawn ) && gpGlobals->curtime >= m_flDeathTime + PORTAL_RESPAWN_DELAY )
  1826. {
  1827. RespawnPlayer();
  1828. }
  1829. }
  1830. void CPortal_Player::RespawnPlayer( void )
  1831. {
  1832. m_nButtons = 0;
  1833. m_iRespawnFrames = 0;
  1834. // All condition should be removed
  1835. m_Shared.RemoveAllCond();
  1836. SetTeamTauntState( TEAM_TAUNT_NONE );
  1837. if ( GameRules()->IsMultiplayer() )
  1838. {
  1839. // if the player was dropped by the other player and fell into goo, give PARTNER_DROP Ach. to the other player
  1840. if ( m_bWasDroppedByOtherPlayerWhileTaunting && GetWaterLevel() != WL_NotInWater )
  1841. {
  1842. CPortal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherConnectedPlayer( this ) );
  1843. if ( pOtherPlayer )
  1844. {
  1845. UTIL_RecordAchievementEvent( "ACH.PARTNER_DROP", pOtherPlayer );
  1846. }
  1847. }
  1848. }
  1849. //Msg( "Respawn\n");
  1850. if ( PortalGameRules() && GetBonusChallenge() > 0 )
  1851. {
  1852. // Single player challenge needs to respawn this way so we don't lose the session
  1853. engine->ChangeLevel( gpGlobals->mapname.ToCStr(), NULL );
  1854. }
  1855. else
  1856. {
  1857. respawn( this, !IsObserver() );// don't copy a corpse if we're in deathcam.
  1858. }
  1859. SetNextThink( TICK_NEVER_THINK );
  1860. }
  1861. void CPortal_Player::PlayerTransitionCompleteThink( void )
  1862. {
  1863. const char *szVideoCommand = "stop_transition_videos_fadeout";
  1864. char szClientCmd[256];
  1865. Q_snprintf( szClientCmd, sizeof(szClientCmd), "%s %f\n", szVideoCommand, 1.5f );
  1866. engine->ClientCommand( edict(), szClientCmd );
  1867. if ( mp_dev_wait_for_other_player.GetBool() && ( PortalMPGameRules() && !PortalMPGameRules()->IsCommunityCoopHub() ) )
  1868. {
  1869. // We wanted to wait for the other player
  1870. if ( PortalMPGameRules() && ( !PortalMPGameRules()->IsPlayerDataReceived( 0 ) || !PortalMPGameRules()->IsPlayerDataReceived( 1 ) ) )
  1871. {
  1872. // Both players haven't sent their data, shut it down!
  1873. DevMsg( "Player transitioned with no partner!\n" );
  1874. engine->ClientCommand( this->edict(), "disconnect \"Partner disconnected\"" );
  1875. }
  1876. }
  1877. ChallengePlayersReady();
  1878. // Respawn other players who were waiting
  1879. SetThink( &CBasePlayer::PlayerDeathThink );
  1880. SetNextThink( gpGlobals->curtime + 0.1f );
  1881. SetContextThink( NULL, 0, CATCHPATNERNOTCONNECTING_THINK_CONTEXT );
  1882. }
  1883. void CPortal_Player::PlayerCatchPatnerNotConnectingThink()
  1884. {
  1885. int numValidNetChannels = 0;
  1886. for (int i = 1; i <= gpGlobals->maxClients; i++ )
  1887. {
  1888. INetChannelInfo *pNetChan = engine->GetPlayerNetInfo( i );
  1889. if ( !pNetChan )
  1890. continue;
  1891. ++ numValidNetChannels;
  1892. }
  1893. if ( numValidNetChannels < 2 )
  1894. {
  1895. SetContextThink( NULL, 0, CATCHPATNERNOTCONNECTING_THINK_CONTEXT );
  1896. SetNextThink( gpGlobals->curtime + 0.1f );
  1897. }
  1898. else
  1899. {
  1900. SetContextThink( &CPortal_Player::PlayerCatchPatnerNotConnectingThink, gpGlobals->curtime + 1.0f, CATCHPATNERNOTCONNECTING_THINK_CONTEXT );
  1901. }
  1902. }
  1903. void CPortal_Player::UpdatePortalPlaneSounds( void )
  1904. {
  1905. CPortal_Base2D *pPortal = dynamic_cast<CProp_Portal *>(m_hPortalEnvironment.Get());
  1906. if ( pPortal && pPortal->IsActive() )
  1907. {
  1908. Vector vVelocity;
  1909. GetVelocity( &vVelocity, NULL );
  1910. if ( !vVelocity.IsZero() )
  1911. {
  1912. Vector vMin, vMax;
  1913. CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
  1914. Vector vEarCenter = ( vMax + vMin ) / 2.0f;
  1915. Vector vDiagonal = vMax - vMin;
  1916. if ( !m_bIntersectingPortalPlane )
  1917. {
  1918. vDiagonal *= 0.25f;
  1919. if ( UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
  1920. {
  1921. m_bIntersectingPortalPlane = true;
  1922. CPASAttenuationFilter filter( this );
  1923. CSoundParameters params;
  1924. if ( GetParametersForSound( "PortalPlayer.EnterPortal", params, NULL ) )
  1925. {
  1926. EmitSound_t ep( params );
  1927. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  1928. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  1929. EmitSound( filter, entindex(), ep );
  1930. }
  1931. }
  1932. }
  1933. else
  1934. {
  1935. vDiagonal *= 0.30f;
  1936. if ( !UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
  1937. {
  1938. m_bIntersectingPortalPlane = false;
  1939. CPASAttenuationFilter filter( this );
  1940. CSoundParameters params;
  1941. if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
  1942. {
  1943. EmitSound_t ep( params );
  1944. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  1945. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  1946. EmitSound( filter, entindex(), ep );
  1947. }
  1948. }
  1949. }
  1950. }
  1951. }
  1952. else if ( m_bIntersectingPortalPlane )
  1953. {
  1954. m_bIntersectingPortalPlane = false;
  1955. CPASAttenuationFilter filter( this );
  1956. CSoundParameters params;
  1957. if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
  1958. {
  1959. EmitSound_t ep( params );
  1960. Vector vVelocity;
  1961. GetVelocity( &vVelocity, NULL );
  1962. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  1963. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  1964. EmitSound( filter, entindex(), ep );
  1965. }
  1966. }
  1967. }
  1968. void CPortal_Player::UpdateWooshSounds( void )
  1969. {
  1970. if ( m_pWooshSound )
  1971. {
  1972. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1973. float fWooshVolume = GetAbsVelocity().Length() - MIN_FLING_SPEED;
  1974. if ( fWooshVolume < 0.0f )
  1975. {
  1976. controller.SoundChangeVolume( m_pWooshSound, 0.0f, 0.1f );
  1977. return;
  1978. }
  1979. fWooshVolume /= 2000.0f;
  1980. if ( fWooshVolume > 1.0f )
  1981. fWooshVolume = 1.0f;
  1982. controller.SoundChangeVolume( m_pWooshSound, fWooshVolume, 0.1f );
  1983. // controller.SoundChangePitch( m_pWooshSound, fWooshVolume + 0.5f, 0.1f );
  1984. }
  1985. }
  1986. void CPortal_Player::FireBullets ( const FireBulletsInfo_t &info )
  1987. {
  1988. NoteWeaponFired();
  1989. BaseClass::FireBullets( info );
  1990. }
  1991. void CPortal_Player::NoteWeaponFired( void )
  1992. {
  1993. Assert( m_pCurrentCommand );
  1994. if( m_pCurrentCommand )
  1995. {
  1996. m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
  1997. }
  1998. }
  1999. extern ConVar sv_maxunlag;
  2000. bool CPortal_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
  2001. {
  2002. // No need to lag compensate at all if we're not attacking in this command and
  2003. // we haven't attacked recently.
  2004. if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
  2005. return false;
  2006. // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
  2007. if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) )
  2008. return false;
  2009. const Vector &vMyOrigin = GetAbsOrigin();
  2010. const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
  2011. // get max distance player could have moved within max lag compensation time,
  2012. // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
  2013. float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
  2014. // If the player is within this distance, lag compensate them in case they're running past us.
  2015. if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance )
  2016. return true;
  2017. // If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
  2018. Vector vForward;
  2019. AngleVectors( pCmd->viewangles, &vForward );
  2020. Vector vDiff = vHisOrigin - vMyOrigin;
  2021. VectorNormalize( vDiff );
  2022. float flCosAngle = 0.707107f; // 45 degree angle
  2023. if ( vForward.Dot( vDiff ) < flCosAngle )
  2024. return false;
  2025. return true;
  2026. }
  2027. void CPortal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  2028. {
  2029. m_PlayerAnimState->DoAnimationEvent( event, nData );
  2030. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  2031. }
  2032. //-----------------------------------------------------------------------------
  2033. // Purpose: Override setup bones so that is uses the render angles from
  2034. // the Portal animation state to setup the hitboxes.
  2035. //-----------------------------------------------------------------------------
  2036. void CPortal_Player::SetupBones( matrix3x4a_t *pBoneToWorld, int boneMask )
  2037. {
  2038. VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
  2039. // Set the mdl cache semaphore.
  2040. MDLCACHE_CRITICAL_SECTION();
  2041. // Get the studio header.
  2042. Assert( GetModelPtr() );
  2043. CStudioHdr *pStudioHdr = GetModelPtr( );
  2044. Vector pos[MAXSTUDIOBONES];
  2045. QuaternionAligned q[MAXSTUDIOBONES];
  2046. // Adjust hit boxes based on IK driven offset.
  2047. Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
  2048. // FIXME: pass this into Studio_BuildMatrices to skip transforms
  2049. CBoneBitList boneComputed;
  2050. if ( m_pIk )
  2051. {
  2052. m_iIKCounter++;
  2053. m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
  2054. GetSkeleton( pStudioHdr, pos, q, boneMask );
  2055. m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
  2056. CalculateIKLocks( gpGlobals->curtime );
  2057. m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
  2058. }
  2059. else
  2060. {
  2061. GetSkeleton( pStudioHdr, pos, q, boneMask );
  2062. }
  2063. CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
  2064. if ( pParent )
  2065. {
  2066. // We're doing bone merging, so do special stuff here.
  2067. CBoneCache *pParentCache = pParent->GetBoneCache();
  2068. if ( pParentCache )
  2069. {
  2070. BuildMatricesWithBoneMerge(
  2071. pStudioHdr,
  2072. m_PlayerAnimState->GetRenderAngles(),
  2073. adjOrigin,
  2074. pos,
  2075. q,
  2076. pBoneToWorld,
  2077. pParent,
  2078. pParentCache );
  2079. return;
  2080. }
  2081. }
  2082. Studio_BuildMatrices(
  2083. pStudioHdr,
  2084. m_PlayerAnimState->GetRenderAngles(),
  2085. adjOrigin,
  2086. pos,
  2087. q,
  2088. -1,
  2089. GetModelScale(), // Scaling
  2090. pBoneToWorld,
  2091. boneMask );
  2092. }
  2093. // Set the activity based on an event or current state
  2094. void CPortal_Player::SetAnimation( PLAYER_ANIM playerAnim )
  2095. {
  2096. return;
  2097. }
  2098. extern int gEvilImpulse101;
  2099. //-----------------------------------------------------------------------------
  2100. // Purpose: Player reacts to bumping a weapon.
  2101. // Input : pWeapon - the weapon that the player bumped into.
  2102. // Output : Returns true if player picked up the weapon
  2103. //-----------------------------------------------------------------------------
  2104. bool CPortal_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
  2105. {
  2106. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  2107. // Can I have this weapon type?
  2108. if ( !IsAllowedToPickupWeapons() )
  2109. return false;
  2110. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  2111. {
  2112. if ( gEvilImpulse101 )
  2113. {
  2114. UTIL_Remove( pWeapon );
  2115. }
  2116. return false;
  2117. }
  2118. // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
  2119. if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) )
  2120. {
  2121. return false;
  2122. }
  2123. CWeaponPortalgun *pPickupPortalgun = dynamic_cast<CWeaponPortalgun*>( pWeapon );
  2124. bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
  2125. if ( bOwnsWeaponAlready == true )
  2126. {
  2127. // If we picked up a second portal gun set the bool to alow secondary fire
  2128. if ( pPickupPortalgun )
  2129. {
  2130. CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( pWeapon->GetClassname() ) );
  2131. if ( pPickupPortalgun->CanFirePortal1() )
  2132. pPortalGun->SetCanFirePortal1();
  2133. if ( pPickupPortalgun->CanFirePortal2() )
  2134. pPortalGun->SetCanFirePortal2();
  2135. UTIL_Remove( pWeapon );
  2136. return true;
  2137. }
  2138. //If we have room for the ammo, then "take" the weapon too.
  2139. if ( Weapon_EquipAmmoOnly( pWeapon ) )
  2140. {
  2141. pWeapon->CheckRespawn();
  2142. UTIL_Remove( pWeapon );
  2143. return true;
  2144. }
  2145. else
  2146. {
  2147. return false;
  2148. }
  2149. }
  2150. else if ( pPickupPortalgun )
  2151. {
  2152. // HACK HACK: In Portal 2's incenerator the gun wasn't set correctly and they fired an upgrade_portalgun
  2153. // command to work around it. Now that cheat commands are protected correctly we need a way to give the
  2154. // player both portals without modifying the map. So lets just check if it's the incenerator map and fix
  2155. // it up. -Jeep
  2156. if ( V_strcmp( gpGlobals->mapname.ToCStr(), "sp_a2_intro" ) == 0 )
  2157. {
  2158. pPickupPortalgun->SetCanFirePortal1();
  2159. pPickupPortalgun->SetCanFirePortal2();
  2160. }
  2161. if ( pPickupPortalgun->CanFirePortal2() )
  2162. {
  2163. IGameEvent *event = gameeventmanager->CreateEvent( "portal_enabled" );
  2164. if ( event )
  2165. {
  2166. event->SetInt( "userid", GetUserID() );
  2167. event->SetBool( "leftportal", false );
  2168. gameeventmanager->FireEvent( event );
  2169. }
  2170. }
  2171. if ( pPickupPortalgun->CanFirePortal1() )
  2172. {
  2173. IGameEvent *event = gameeventmanager->CreateEvent( "portal_enabled" );
  2174. if ( event )
  2175. {
  2176. event->SetInt( "userid", GetUserID() );
  2177. event->SetBool( "leftportal", true );
  2178. gameeventmanager->FireEvent( event );
  2179. }
  2180. }
  2181. }
  2182. pWeapon->CheckRespawn();
  2183. Weapon_Equip( pWeapon );
  2184. // If we're holding and object before picking up portalgun, drop it
  2185. if ( pPickupPortalgun )
  2186. {
  2187. ForceDropOfCarriedPhysObjects( GetPlayerHeldEntity( this ) );
  2188. }
  2189. return true;
  2190. }
  2191. void CPortal_Player::ShutdownUseEntity( void )
  2192. {
  2193. ShutdownPickupController( m_hUseEntity );
  2194. }
  2195. void CPortal_Player::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity, bool bUseSlowHighAccuracyContacts )
  2196. {
  2197. Vector oldOrigin = GetLocalOrigin();
  2198. QAngle oldAngles = GetLocalAngles();
  2199. BaseClass::Teleport( newPosition, newAngles, newVelocity, bUseSlowHighAccuracyContacts );
  2200. m_angEyeAngles = pl.v_angle;
  2201. m_PlayerAnimState->Teleport( newPosition, newAngles, this );
  2202. m_flUsePostTeleportationBoxTime = sv_post_teleportation_box_time.GetFloat();
  2203. const PaintPowerInfo_t& speedPower = GetPaintPower( SPEED_POWER );
  2204. if( !IsInactivePower( speedPower ) )
  2205. {
  2206. m_PortalLocal.m_flAirInputScale = 0.0f;
  2207. }
  2208. if ( newPosition )
  2209. {
  2210. m_vPrevPosition = *newPosition;
  2211. }
  2212. }
  2213. bool CPortal_Player::UseFoundEntity( CBaseEntity *pUseEntity, bool bAutoGrab )
  2214. {
  2215. bool usedSomething = false;
  2216. //!!!UNDONE: traceline here to prevent +USEing buttons through walls
  2217. int caps = pUseEntity->ObjectCaps();
  2218. variant_t emptyVariant;
  2219. if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) ||
  2220. ( ( (m_afButtonPressed & IN_USE) || bAutoGrab ) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) )
  2221. {
  2222. if ( caps & FCAP_CONTINUOUS_USE )
  2223. {
  2224. m_afPhysicsFlags |= PFLAG_USING;
  2225. }
  2226. pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
  2227. usedSomething = true;
  2228. }
  2229. // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
  2230. else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use
  2231. {
  2232. pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
  2233. usedSomething = true;
  2234. }
  2235. return usedSomething;
  2236. }
  2237. void CPortal_Player::PlayerUse( void )
  2238. {
  2239. CBaseEntity *pUseEnt = m_hGrabbedEntity.Get();
  2240. CPortal_Base2D *pUseThroughPortal = (CPortal_Base2D*)m_hPortalThroughWhichGrabOccured.Get();
  2241. if ( gpGlobals->curtime < m_flUseKeyCooldownTime )
  2242. {
  2243. return;
  2244. }
  2245. bool bUsePressed = (m_afButtonPressed & IN_USE) != 0;
  2246. if ( bUsePressed && m_hUseEntity )
  2247. {
  2248. // Currently using a latched entity?
  2249. if ( !ClearUseEntity() )
  2250. {
  2251. m_bPlayUseDenySound = true;
  2252. }
  2253. else
  2254. {
  2255. m_flAutoGrabLockOutTime = gpGlobals->curtime;
  2256. }
  2257. return;
  2258. }
  2259. if ( !pUseEnt )
  2260. {
  2261. PollForUseEntity( bUsePressed, &pUseEnt, &pUseThroughPortal );
  2262. }
  2263. // Was use pressed or released?
  2264. if ( !bUsePressed && !pUseEnt )
  2265. {
  2266. return;
  2267. }
  2268. // Only run the below if use is pressed and the client sent a grab entity
  2269. bool bUsedSomething = false;
  2270. if ( pUseEnt && m_hAttachedObject.Get() == NULL )
  2271. {
  2272. // Use the found entity, and skip the button down checks if we're forcing the grabbing of a client use entity
  2273. bUsedSomething = UseFoundEntity( pUseEnt, !bUsePressed );
  2274. if ( bUsedSomething && pUseThroughPortal )
  2275. {
  2276. SetHeldObjectOnOppositeSideOfPortal( true );
  2277. SetHeldObjectPortal( pUseThroughPortal );
  2278. }
  2279. else
  2280. {
  2281. SetHeldObjectOnOppositeSideOfPortal( false );
  2282. SetHeldObjectPortal( NULL );
  2283. }
  2284. // Debounce the use key
  2285. if ( bUsedSomething )
  2286. {
  2287. m_Local.m_nOldButtons |= IN_USE;
  2288. m_afButtonPressed &= ~IN_USE;
  2289. }
  2290. }
  2291. if ( bUsePressed && !bUsedSomething )
  2292. {
  2293. // No entity passed up with the use command, play deny sound
  2294. m_bPlayUseDenySound = true;
  2295. // Make the weapon "dry fire" to show we tried to +use
  2296. CWeaponPortalgun *pWeapon = dynamic_cast<CWeaponPortalgun *>(GetActivePortalWeapon());
  2297. if ( pWeapon )
  2298. {
  2299. pWeapon->UseDeny();
  2300. }
  2301. }
  2302. }
  2303. extern ConVar sv_enableholdrotation;
  2304. ConVar sv_holdrotationsensitivity( "sv_holdrotationsensitivity", "0.1", FCVAR_ARCHIVE );
  2305. void CPortal_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper)
  2306. {
  2307. if ( sv_enableholdrotation.GetBool() && !IsUsingVMGrab() )
  2308. {
  2309. if( (ucmd->buttons & IN_ATTACK2) && (GetPlayerHeldEntity( this ) != NULL) )
  2310. {
  2311. VectorCopy ( pl.v_angle, ucmd->viewangles );
  2312. float x, y;
  2313. if( abs( ucmd->mousedx ) > abs(ucmd->mousedy) )
  2314. {
  2315. x = ucmd->mousedx * sv_holdrotationsensitivity.GetFloat();
  2316. y = 0;
  2317. }
  2318. else
  2319. {
  2320. x = 0;
  2321. y = ucmd->mousedy * sv_holdrotationsensitivity.GetFloat();
  2322. }
  2323. RotatePlayerHeldObject( this, x, y, true );
  2324. }
  2325. }
  2326. // Can't use stuff while dead
  2327. if ( IsDead() )
  2328. {
  2329. ucmd->buttons &= ~IN_USE;
  2330. }
  2331. m_hGrabbedEntity = ucmd->player_held_entity > 0 ? CBaseEntity::Instance ( ucmd->player_held_entity ) : NULL;
  2332. m_hPortalThroughWhichGrabOccured = ucmd->held_entity_was_grabbed_through_portal > 0 ? CBaseEntity::Instance ( ucmd->held_entity_was_grabbed_through_portal ) : NULL;
  2333. //============================================================================
  2334. // Fix the eye angles after portalling. The client may have sent commands with
  2335. // the old view angles before it knew about the teleportation.
  2336. //sorry for crappy name, the client sent us a command, we acknowledged it, and they are now telling us the latest one they received an acknowledgement for in this brand new command
  2337. int iLastCommandAcknowledgementReceivedOnClientForThisCommand = ucmd->command_number - ucmd->command_acknowledgements_pending;
  2338. while( (m_PendingPortalTransforms.Count() > 0) && (iLastCommandAcknowledgementReceivedOnClientForThisCommand >= m_PendingPortalTransforms[0].command_number) )
  2339. {
  2340. m_PendingPortalTransforms.Remove( 0 );
  2341. }
  2342. // The server changed the angles, and the user command was created after the teleportation, but before the client knew they teleported. Need to fix up the angles into the new space
  2343. if( m_PendingPortalTransforms.Count() > ucmd->predictedPortalTeleportations )
  2344. {
  2345. matrix3x4_t matComputeFinalTransform[2];
  2346. int iFlip = 0;
  2347. //most common case will be exactly 1 transform
  2348. matComputeFinalTransform[0] = m_PendingPortalTransforms[ucmd->predictedPortalTeleportations].matTransform;
  2349. for( int i = ucmd->predictedPortalTeleportations + 1; i < m_PendingPortalTransforms.Count(); ++i )
  2350. {
  2351. ConcatTransforms( m_PendingPortalTransforms[i].matTransform, matComputeFinalTransform[iFlip], matComputeFinalTransform[1-iFlip] );
  2352. iFlip = 1 - iFlip;
  2353. }
  2354. //apply the final transform
  2355. matrix3x4_t matAngleTransformIn, matAngleTransformOut;
  2356. AngleMatrix( ucmd->viewangles, matAngleTransformIn );
  2357. ConcatTransforms( matComputeFinalTransform[iFlip], matAngleTransformIn, matAngleTransformOut );
  2358. MatrixAngles( matAngleTransformOut, ucmd->viewangles );
  2359. }
  2360. PreventCrouchJump( ucmd );
  2361. if ( m_PortalLocal.m_bZoomedIn )
  2362. {
  2363. if ( IsTaunting() )
  2364. {
  2365. // Pop out of zoom when I'm taunting
  2366. ZoomOut();
  2367. }
  2368. else
  2369. {
  2370. float fThreshold = sv_zoom_stop_movement_threashold.GetFloat();
  2371. if ( gpGlobals->curtime > GetFOVTime() + sv_zoom_stop_time_threashold.GetFloat() &&
  2372. ( fabsf( ucmd->forwardmove ) > fThreshold || fabsf( ucmd->sidemove ) > fThreshold ) )
  2373. {
  2374. // After 5 seconds, moving while zoomed will pop you back out
  2375. // This is to fix people who accidentally switch into zoom mode, but don't know how to get back out...
  2376. // Should give plenty of time to players who want to move while zoomed
  2377. ZoomOut();
  2378. }
  2379. }
  2380. }
  2381. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  2382. }
  2383. //-----------------------------------------------------------------------------
  2384. // Purpose: Deal with command coming in from the client-side
  2385. //-----------------------------------------------------------------------------
  2386. bool CPortal_Player::ClientCommand( const CCommand &args )
  2387. {
  2388. const char *pcmd = args[0];
  2389. if ( FStrEq( pcmd, "taunt" ) )
  2390. {
  2391. if ( args.ArgC() > 1 )
  2392. {
  2393. Taunt( args[1] );
  2394. }
  2395. else
  2396. {
  2397. Taunt();
  2398. }
  2399. return true;
  2400. }
  2401. else if ( FStrEq( pcmd, "taunt_auto" ) )
  2402. {
  2403. if ( args.ArgC() > 1 )
  2404. {
  2405. Taunt( args[1], true );
  2406. }
  2407. return true;
  2408. }
  2409. else if ( FStrEq( pcmd, "spectate" ) )
  2410. {
  2411. if( CommandLine()->FindParm( "-allowspectators" ) == NULL )
  2412. {
  2413. // do nothing
  2414. return true;
  2415. }
  2416. }
  2417. else if ( FStrEq( pcmd, "end_movie" ) )
  2418. {
  2419. if ( args.ArgC() == 2 )
  2420. {
  2421. const char *target = STRING( AllocPooledString( args[1] ) );
  2422. const char *action = "__MovieFinished";
  2423. variant_t value;
  2424. g_EventQueue.AddEvent( target, action, value, 0, this, this );
  2425. }
  2426. }
  2427. else if ( FStrEq( pcmd, "signify" ) )
  2428. {
  2429. // Verify our argument count
  2430. if ( args.ArgC() != 10 )
  2431. {
  2432. Assert(args.ArgC() != 10);
  2433. Msg("Ill-formed signify command from client!\n");
  2434. return true;
  2435. }
  2436. // Now, pull the pieces out of the string
  2437. const char *lpszCommand = args[1];
  2438. int nIndex = V_atoi( args[2] );
  2439. float fDelay = V_atof( args[3] );
  2440. Vector vPosition;
  2441. vPosition.x = V_atof( args[4] );
  2442. vPosition.y = V_atof( args[5] );
  2443. vPosition.z = V_atof( args[6] );
  2444. Vector vNormal;
  2445. vNormal.x = V_atof( args[7] );
  2446. vNormal.y = V_atof( args[8] );
  2447. vNormal.z = V_atof( args[9] );
  2448. // Now send the message on to the client
  2449. CReliableBroadcastRecipientFilter player;
  2450. player.AddAllPlayers();
  2451. if ( fDelay <= 0.0f )
  2452. {
  2453. player.RemoveRecipient( this ); // Remove us because we already predicted the results
  2454. }
  2455. CBaseEntity *pTargetEnt = ( nIndex != -1 ) ? UTIL_EntityByIndex( nIndex ) : NULL;
  2456. UserMessageBegin( player, "AddLocator" );
  2457. WRITE_SHORT( entindex() );
  2458. WRITE_EHANDLE( pTargetEnt );
  2459. WRITE_FLOAT( gpGlobals->curtime + fDelay );
  2460. WRITE_VEC3COORD( vPosition );
  2461. WRITE_VEC3NORMAL( vNormal );
  2462. WRITE_STRING( lpszCommand );
  2463. MessageEnd();
  2464. IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_ping" );
  2465. if ( event )
  2466. {
  2467. Vector vecSpot = vPosition;
  2468. if ( pTargetEnt )
  2469. vecSpot = pTargetEnt->GetAbsOrigin();
  2470. event->SetInt("userid", GetUserID() );
  2471. event->SetFloat("ping_x", vecSpot.x );
  2472. event->SetFloat("ping_y", vecSpot.y );
  2473. event->SetFloat("ping_z", vecSpot.z );
  2474. gameeventmanager->FireEvent( event );
  2475. }
  2476. // Denote that we're pointing
  2477. m_Shared.AddCond( PORTAL_COND_POINTING, 1.0f );
  2478. return true;
  2479. }
  2480. else if ( StringHasPrefix( pcmd, "CoopPingTool" ) )
  2481. {
  2482. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
  2483. if ( pEntity )
  2484. {
  2485. pEntity->RunScript( args.GetCommandString(), "PingToolCommand" );
  2486. }
  2487. return true;
  2488. }
  2489. else if ( FStrEq( pcmd, "survey_done" ) )
  2490. {
  2491. int nIndex = V_atoi( args[1] );
  2492. CPointSurvey *pPointSurveyEnt = ( nIndex != -1 ) ? (CPointSurvey*)UTIL_EntityByIndex( nIndex ) : NULL;
  2493. if ( pPointSurveyEnt )
  2494. {
  2495. pPointSurveyEnt->OnSurveyCompleted();
  2496. }
  2497. return true;
  2498. }
  2499. else if ( FStrEq( pcmd, "load_recent_checkpoint" ) )
  2500. {
  2501. if ( !PortalMPGameRules() )
  2502. {
  2503. RespawnPlayer();
  2504. }
  2505. }
  2506. else if ( FStrEq( pcmd, "pre_go_to_hub" ) )
  2507. {
  2508. // Play transition video
  2509. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  2510. {
  2511. CBasePlayer *pToPlayer = UTIL_PlayerByIndex( i );
  2512. if ( pToPlayer && !pToPlayer->IsSplitScreenPlayer() )
  2513. {
  2514. engine->ClientCommand( pToPlayer->edict(), "stopvideos" );
  2515. engine->ClientCommand( pToPlayer->edict(), "playvideo_end_level_transition coop_bots_load 1" );
  2516. }
  2517. }
  2518. return true;
  2519. }
  2520. else if ( FStrEq( pcmd, "go_to_hub" ) )
  2521. {
  2522. if ( PortalMPGameRules() )
  2523. {
  2524. PortalMPGameRules()->SaveMPStats();
  2525. }
  2526. bool bBothPlayersHaveDLC = true;
  2527. if ( IsGameConsole() )
  2528. {
  2529. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  2530. if ( pIMatchSession )
  2531. {
  2532. KeyValues *pFullSettings = pIMatchSession->GetSessionSettings();
  2533. if ( pFullSettings )
  2534. {
  2535. if ( !( ( pFullSettings->GetUint64( "members/machine0/dlcmask" ) & PORTAL2_DLCID_RETAIL_DLC1 ) &&
  2536. ( XBX_GetNumGameUsers() > 1 ||
  2537. ( pFullSettings->GetUint64( "members/machine1/dlcmask" ) & PORTAL2_DLCID_RETAIL_DLC1 ) ) ) )
  2538. {
  2539. bBothPlayersHaveDLC = false;
  2540. }
  2541. }
  2542. }
  2543. }
  2544. // clear out any outstanding UI for both players
  2545. ClearClientUI();
  2546. if ( bBothPlayersHaveDLC )
  2547. {
  2548. // They have the DLC!
  2549. engine->ChangeLevel( "mp_coop_lobby_3", NULL );
  2550. }
  2551. else
  2552. {
  2553. // One is missing the DLC! Go to the old hub
  2554. engine->ChangeLevel( "mp_coop_lobby_2", NULL );
  2555. }
  2556. return true;
  2557. }
  2558. else if ( FStrEq( pcmd, "mp_restart_level" ) )
  2559. {
  2560. if ( PortalMPGameRules() )
  2561. {
  2562. PortalMPGameRules()->SaveMPStats();
  2563. }
  2564. // clear out any outstanding UI for both players
  2565. ClearClientUI();
  2566. engine->ChangeLevel( gpGlobals->mapname.ToCStr(), NULL );
  2567. }
  2568. else if ( FStrEq( pcmd, "mp_select_level" ) )
  2569. {
  2570. if ( PortalMPGameRules() )
  2571. {
  2572. PortalMPGameRules()->SaveMPStats();
  2573. }
  2574. if ( args.ArgC() > 1 )
  2575. {
  2576. // clear out any outstanding UI for both players
  2577. ClearClientUI();
  2578. engine->ChangeLevel( args[1], NULL );
  2579. }
  2580. }
  2581. else if ( FStrEq( pcmd, "restart_level" ) )
  2582. {
  2583. sv_bonus_challenge.SetValue( GetBonusChallenge() );
  2584. #if !defined( _GAMECONSOLE )
  2585. g_Portal2ResearchDataTracker.Event_PlayerGaveUp();
  2586. #endif // !defined( _GAMECONSOLE )
  2587. // clear out any outstanding UI for both players
  2588. ClearClientUI();
  2589. engine->ChangeLevel( gpGlobals->mapname.ToCStr(), NULL );
  2590. }
  2591. else if ( FStrEq( pcmd, "erase_mp_progress" ) )
  2592. {
  2593. if ( PortalMPGameRules() )
  2594. {
  2595. PortalMPGameRules()->SetAllMapsComplete( false, atoi(args[1]) );
  2596. }
  2597. return true;
  2598. }
  2599. else if ( FStrEq( pcmd, "transition_map" ) )
  2600. {
  2601. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@transition_script" );
  2602. if ( !pEntity )
  2603. {
  2604. pEntity = gEntList.FindEntityByName( NULL, "script_check_finish_game" ); // Final level in coop
  2605. if ( !pEntity )
  2606. {
  2607. CBaseEntity *pTempEntity = gEntList.FindEntityByClassname( NULL, "logic_script" );
  2608. while ( pTempEntity )
  2609. {
  2610. if ( V_stristr( pTempEntity->GetEntityName().ToCStr(), "transition_script" ) ) // DLC levels
  2611. {
  2612. pEntity = pTempEntity;
  2613. break;
  2614. }
  2615. pTempEntity = gEntList.FindEntityByClassname( pTempEntity, "logic_script" );
  2616. }
  2617. }
  2618. }
  2619. // clear out any outstanding UI for both players
  2620. ClearClientUI();
  2621. if ( pEntity )
  2622. {
  2623. pEntity->RunScript( "RealTransitionFromMap()" );
  2624. }
  2625. }
  2626. else if ( FStrEq( pcmd, "select_map" ) )
  2627. {
  2628. // get the map to run
  2629. if ( args.ArgC() > 1 )
  2630. {
  2631. // clear out any outstanding UI for both players
  2632. ClearClientUI();
  2633. const char *pMapName = args[1];
  2634. sv_bonus_challenge.SetValue( GetBonusChallenge() );
  2635. engine->ChangeLevel( pMapName, NULL );
  2636. }
  2637. }
  2638. else if ( FStrEq( pcmd, "pre_go_to_calibration" ) )
  2639. {
  2640. // Play transition video
  2641. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  2642. {
  2643. CBasePlayer *pToPlayer = UTIL_PlayerByIndex( i );
  2644. if ( pToPlayer && !pToPlayer->IsSplitScreenPlayer() )
  2645. {
  2646. engine->ClientCommand( pToPlayer->edict(), "stopvideos" );
  2647. engine->ClientCommand( pToPlayer->edict(), "playvideo_end_level_transition coop_bots_load_wave 1" );
  2648. }
  2649. }
  2650. return true;
  2651. }
  2652. else if ( FStrEq( pcmd, "go_to_calibration" ) )
  2653. {
  2654. if ( PortalMPGameRules() )
  2655. {
  2656. PortalMPGameRules()->SaveMPStats();
  2657. }
  2658. // clear out any outstanding UI for both players
  2659. ClearClientUI();
  2660. engine->ChangeLevel( "mp_coop_start", NULL );
  2661. return true;
  2662. }
  2663. else if ( FStrEq( pcmd, "level_complete_data" ) )
  2664. {
  2665. CPortalMPGameRules *pRules = PortalMPGameRules();
  2666. if ( pRules )
  2667. {
  2668. pRules->SetMapCompleteData( atoi(args[1]) );
  2669. }
  2670. return true;
  2671. }
  2672. else if ( FStrEq( pcmd, "mp_stats_data" ) )
  2673. {
  2674. CPortalMPStats *pStats = GetPortalMPStats();
  2675. if ( pStats )
  2676. {
  2677. pStats->SetStats( atoi(args[1]), atoi(args[2]), atoi(args[3]), atoi(args[4]) );
  2678. }
  2679. return true;
  2680. }
  2681. return BaseClass::ClientCommand( args );
  2682. }
  2683. //-----------------------------------------------------------------------------
  2684. //-----------------------------------------------------------------------------
  2685. int CPortal_Player::FlashlightIsOn( void )
  2686. {
  2687. return IsEffectActive( EF_DIMLIGHT );
  2688. }
  2689. //-----------------------------------------------------------------------------
  2690. // Cheats only, this is for helping developers choose lighting for scenes
  2691. //-----------------------------------------------------------------------------
  2692. bool CPortal_Player::FlashlightTurnOn( bool playSound /*= false*/ )
  2693. {
  2694. if ( sv_cheats->GetBool() == false )
  2695. return false;
  2696. AddEffects( EF_DIMLIGHT );
  2697. return true;
  2698. }
  2699. //-----------------------------------------------------------------------------
  2700. // Cheats only, this is for helping developers choose lighting for scenes
  2701. //-----------------------------------------------------------------------------
  2702. void CPortal_Player::FlashlightTurnOff( bool playSound /*= false*/ )
  2703. {
  2704. if ( sv_cheats->GetBool() == false )
  2705. return;
  2706. RemoveEffects( EF_DIMLIGHT );
  2707. }
  2708. //-----------------------------------------------------------------------------
  2709. // Purpose:
  2710. //-----------------------------------------------------------------------------
  2711. void CPortal_Player::CheatImpulseCommands( int iImpulse )
  2712. {
  2713. switch ( iImpulse )
  2714. {
  2715. case 101:
  2716. {
  2717. if( sv_cheats->GetBool() )
  2718. {
  2719. //GiveAllItems();
  2720. // FIXME: Bring this back for DLC2
  2721. //sv_can_carry_both_guns.SetValue( 1 );
  2722. //GivePlayerPaintGun( true, false );
  2723. GivePlayerPortalGun( true, true );
  2724. }
  2725. }
  2726. break;
  2727. default:
  2728. BaseClass::CheatImpulseCommands( iImpulse );
  2729. }
  2730. }
  2731. void CPortal_Player::CreateViewModel( int index /*=0*/ )
  2732. {
  2733. if( !g_pGameRules->IsMultiplayer() )
  2734. return BaseClass::CreateViewModel( index );
  2735. Assert( index >= 0 && index < MAX_VIEWMODELS );
  2736. if ( GetViewModel( index ) )
  2737. return;
  2738. CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
  2739. if ( vm )
  2740. {
  2741. vm->SetAbsOrigin( GetAbsOrigin() );
  2742. vm->SetOwner( this );
  2743. vm->SetIndex( index );
  2744. DispatchSpawn( vm );
  2745. vm->FollowEntity( this, false );
  2746. m_hViewModel.Set( index, vm );
  2747. }
  2748. }
  2749. bool CPortal_Player::BecomeRagdollOnClient( const Vector &force )
  2750. {
  2751. return true;//BaseClass::BecomeRagdollOnClient( force );
  2752. }
  2753. void CPortal_Player::CreateRagdollEntity( const CTakeDamageInfo &info )
  2754. {
  2755. if ( m_hRagdoll )
  2756. {
  2757. UTIL_RemoveImmediate( m_hRagdoll );
  2758. m_hRagdoll = NULL;
  2759. }
  2760. #if defined PORTAL_HIDE_PLAYER_RAGDOLL
  2761. AddSolidFlags( FSOLID_NOT_SOLID );
  2762. AddEffects( EF_NODRAW | EF_NOSHADOW );
  2763. AddEFlags( EFL_NO_DISSOLVE );
  2764. #endif // PORTAL_HIDE_PLAYER_RAGDOLL
  2765. CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  2766. pRagdoll->m_takedamage = DAMAGE_NO;
  2767. m_hRagdoll = pRagdoll;
  2768. /*
  2769. // If we already have a ragdoll destroy it.
  2770. CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
  2771. if( pRagdoll )
  2772. {
  2773. UTIL_Remove( pRagdoll );
  2774. pRagdoll = NULL;
  2775. }
  2776. Assert( pRagdoll == NULL );
  2777. // Create a ragdoll.
  2778. pRagdoll = dynamic_cast<CPortalRagdoll*>( CreateEntityByName( "portal_ragdoll" ) );
  2779. if ( pRagdoll )
  2780. {
  2781. pRagdoll->m_hPlayer = this;
  2782. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  2783. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  2784. pRagdoll->m_nModelIndex = m_nModelIndex;
  2785. pRagdoll->m_nForceBone = m_nForceBone;
  2786. pRagdoll->CopyAnimationDataFrom( this );
  2787. pRagdoll->SetOwnerEntity( this );
  2788. pRagdoll->m_flAnimTime = gpGlobals->curtime;
  2789. pRagdoll->m_flPlaybackRate = 0.0;
  2790. pRagdoll->SetCycle( 0 );
  2791. pRagdoll->ResetSequence( 0 );
  2792. float fSequenceDuration = SequenceDuration( GetSequence() );
  2793. float fPreviousCycle = clamp(GetCycle()-( 0.1 * ( 1 / fSequenceDuration ) ),0.f,1.f);
  2794. float fCurCycle = GetCycle();
  2795. matrix3x4a_t bonetoworldnext[MAXSTUDIOBONES];
  2796. SetupBones( bonetoworldnext, BONE_USED_BY_ANYTHING );
  2797. SetCycle( fPreviousCycle );
  2798. matrix3x4a_t bonetoworld[MAXSTUDIOBONES];
  2799. SetupBones( bonetoworld, BONE_USED_BY_ANYTHING );
  2800. SetCycle( fCurCycle );
  2801. //pRagdoll->InitRagdoll( info.GetDamageForce(), m_nForceBone, info.GetDamagePosition(), bonetoworld, bonetoworldnext, 0.1f, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  2802. pRagdoll->SetMoveType( MOVETYPE_VPHYSICS );
  2803. pRagdoll->SetSolid( SOLID_VPHYSICS );
  2804. if ( IsDissolving() )
  2805. {
  2806. pRagdoll->TransferDissolveFrom( this );
  2807. }
  2808. Vector mins, maxs;
  2809. mins = CollisionProp()->OBBMins();
  2810. maxs = CollisionProp()->OBBMaxs();
  2811. pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
  2812. pRagdoll->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  2813. }
  2814. // Turn off the player.
  2815. AddSolidFlags( FSOLID_NOT_SOLID );
  2816. AddEffects( EF_NODRAW | EF_NOSHADOW );
  2817. SetMoveType( MOVETYPE_NONE );
  2818. // Save ragdoll handle.
  2819. m_hRagdoll = pRagdoll;
  2820. */
  2821. }
  2822. void CPortal_Player::Jump( void )
  2823. {
  2824. #if !defined ( _GAMECONSOLE ) && !defined( NO_STEAM )
  2825. g_PortalGameStats.Event_PlayerJump( GetAbsOrigin(), GetAbsVelocity() );
  2826. #endif
  2827. BaseClass::Jump();
  2828. }
  2829. //-----------------------------------------------------------------------------
  2830. // Purpose:
  2831. //-----------------------------------------------------------------------------
  2832. void CPortal_Player::ZoomIn()
  2833. {
  2834. SetFOV( this, 45.0f, 0.15f );
  2835. m_PortalLocal.m_bZoomedIn = true;
  2836. IGameEvent * event = gameeventmanager->CreateEvent( "player_zoomed" );
  2837. if ( event )
  2838. {
  2839. event->SetInt( "userid", GetUserID() );
  2840. gameeventmanager->FireEvent( event );
  2841. }
  2842. }
  2843. void CPortal_Player::ZoomOut()
  2844. {
  2845. SetFOV( this, 0.0f, 0.15f );
  2846. m_PortalLocal.m_bZoomedIn = false;
  2847. IGameEvent * event = gameeventmanager->CreateEvent( "player_unzoomed" );
  2848. if ( event )
  2849. {
  2850. event->SetInt( "userid", GetUserID() );
  2851. gameeventmanager->FireEvent( event );
  2852. }
  2853. }
  2854. bool CPortal_Player::IsZoomed( void )
  2855. {
  2856. return m_PortalLocal.m_bZoomedIn;
  2857. }
  2858. void CPortal_Player::Event_Killed( const CTakeDamageInfo &info )
  2859. {
  2860. m_PlayerGunTypeWhenDead = m_PlayerGunType;
  2861. //update damage info with our accumulated physics force
  2862. CTakeDamageInfo subinfo = info;
  2863. subinfo.SetDamageForce( m_vecTotalBulletForce );
  2864. // if we're burned by a laser, but we haven't been told to gib, that means that we had fractional damage done to us
  2865. // and we died before it was caught in OnTakeDamage. Gib them, plz.
  2866. if ( GameRules()->IsMultiplayer() && !m_Shared.InCond( PORTAL_COND_DROWNING ) )
  2867. {
  2868. if ( mp_should_gib_bots.GetBool() && !m_Shared.InCond( PORTAL_COND_DEATH_GIB ) )
  2869. {
  2870. m_Shared.AddCond( PORTAL_COND_DEATH_GIB );
  2871. m_Shared.m_damageInfo = info;
  2872. Break( info.GetAttacker(), info );
  2873. }
  2874. }
  2875. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  2876. g_PortalGameStats.Event_PlayerDeath( this );
  2877. #endif
  2878. #ifndef _GAMECONSOLE
  2879. g_Portal2ResearchDataTracker.IncrementDeath( this );
  2880. #endif // !_GAMECONSOLE
  2881. m_bTauntRemoteView = false;
  2882. // show killer in death cam mode
  2883. // chopped down version of SetObserverTarget without the team check
  2884. //if( info.GetAttacker() )
  2885. //{
  2886. // // set new target
  2887. // m_hObserverTarget.Set( info.GetAttacker() );
  2888. //}
  2889. //else
  2890. // m_hObserverTarget.Set( NULL );
  2891. // UpdateExpression();
  2892. #if !defined PORTAL_HIDE_PLAYER_RAGDOLL
  2893. CreateRagdollEntity( info );
  2894. #else
  2895. // Fizzle all portals so they don't see the player disappear
  2896. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  2897. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  2898. CWeaponPortalgun* pPortalgun = (CWeaponPortalgun*)GetActiveWeapon();
  2899. if( pPortalgun )
  2900. {
  2901. for( int i = 0; i != iPortalCount; ++i )
  2902. {
  2903. CProp_Portal *pTempPortal = pPortals[i];
  2904. //HACKISH: Do this before the chain to base... basecombatcharacer will dump the weapon
  2905. // and then we won't know what linkage ID to fizzle. This is relevant in multiplayer,
  2906. // where we want only the dying player's portals to fizzle.
  2907. if( pTempPortal && ( pPortalgun->GetLinkageGroupID() == pTempPortal->GetLinkageGroup() ) )
  2908. {
  2909. pTempPortal->DeactivatePortalOnThink();
  2910. }
  2911. }
  2912. }
  2913. if ( GameRules()->IsMultiplayer() && mp_should_gib_bots.GetBool() )
  2914. {
  2915. for ( int i = 0; i < WeaponCount(); ++i )
  2916. {
  2917. // remove the weapon, we don't need it anymore and it shows up attached to the non visuble player in third person
  2918. CBaseCombatWeapon *pWeapon = GetWeapon( i );
  2919. if ( pWeapon == NULL )
  2920. continue;
  2921. UTIL_Remove( pWeapon );
  2922. }
  2923. }
  2924. #endif // PORTAL_HIDE_PLAYER_RAGDOLL
  2925. BaseClass::Event_Killed( subinfo );
  2926. SetUseKeyCooldownTime( 3.0f );
  2927. if ( (info.GetDamageType() & DMG_DISSOLVE) && ( m_hRagdoll.Get() ) && !(m_hRagdoll.Get()->GetEFlags() & EFL_NO_DISSOLVE) )
  2928. {
  2929. m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
  2930. }
  2931. m_lifeState = LIFE_DYING;
  2932. if ( GetObserverTarget() )
  2933. {
  2934. //StartReplayMode( 3, 3, GetObserverTarget()->entindex() );
  2935. //StartObserverMode( OBS_MODE_DEATHCAM );
  2936. }
  2937. if ( GameRules() && GameRules()->IsMultiplayer() )
  2938. {
  2939. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
  2940. if ( pEntity )
  2941. {
  2942. char szScriptCommand[ 64 ];
  2943. V_snprintf( szScriptCommand, sizeof( szScriptCommand ), "BotDeath(%i,%i)", ( GetTeamNumber() == TEAM_BLUE ? 2 : 1 ), info.GetDamageType() );
  2944. pEntity->RunScript( szScriptCommand, "PlayerDied" );
  2945. }
  2946. CPortalMPStats *pStats = GetPortalMPStats();
  2947. if ( pStats )
  2948. {
  2949. pStats->IncrementPlayerDeathsMap( this );
  2950. }
  2951. }
  2952. FireConcept( TLK_PLAYER_KILLED );
  2953. }
  2954. int CPortal_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  2955. {
  2956. CTakeDamageInfo inputInfoCopy( inputInfo );
  2957. // If you shoot yourself, make it hurt but push you less
  2958. if ( inputInfoCopy.GetAttacker() == this && inputInfoCopy.GetDamageType() == DMG_BULLET )
  2959. {
  2960. inputInfoCopy.ScaleDamage( 5.0f );
  2961. inputInfoCopy.ScaleDamageForce( 0.05f );
  2962. }
  2963. CBaseEntity *pAttacker = inputInfoCopy.GetAttacker();
  2964. CBaseEntity *pInflictor = inputInfoCopy.GetInflictor();
  2965. bool bIsTurret = false;
  2966. if ( pAttacker && ( FClassnameIs( pAttacker, "npc_portal_turret_floor" ) ||
  2967. FClassnameIs( pAttacker, "npc_hover_turret" ) ) )
  2968. {
  2969. bIsTurret = true;
  2970. FireConcept( TLK_PLAYER_SHOT );
  2971. }
  2972. // Refuse damage from prop_glados_core.
  2973. if ( (pAttacker && FClassnameIs( pAttacker, "prop_glados_core" )) ||
  2974. (pInflictor && FClassnameIs( pInflictor, "prop_glados_core" )) )
  2975. {
  2976. inputInfoCopy.SetDamage(0.0f);
  2977. }
  2978. if ( bIsTurret && ( inputInfoCopy.GetDamageType() & ( DMG_BULLET | DMG_ENERGYBEAM ) ) )
  2979. {
  2980. Vector vLateralForce = inputInfoCopy.GetDamageForce();
  2981. vLateralForce.z = 0.0f;
  2982. // Push if the player is moving against the force direction
  2983. if ( GetAbsVelocity().Dot( vLateralForce ) < 0.0f )
  2984. ApplyAbsVelocityImpulse( vLateralForce );
  2985. }
  2986. else if ( ( inputInfoCopy.GetDamageType() & DMG_CRUSH ) )
  2987. {
  2988. if ( bIsTurret )
  2989. {
  2990. inputInfoCopy.SetDamage( inputInfoCopy.GetDamage() * 0.5f );
  2991. }
  2992. if ( inputInfoCopy.GetDamage() >= 10.0f )
  2993. {
  2994. EmitSound( "PortalPlayer.BonkYelp" );
  2995. }
  2996. }
  2997. else if ( ( inputInfoCopy.GetDamageType() & DMG_SHOCK ) || ( inputInfoCopy.GetDamageType() & DMG_BURN ) )
  2998. {
  2999. EmitSound( "Player.PainSmall" );
  3000. FireConcept( TLK_PLAYER_BURNED );
  3001. }
  3002. // FIXME: This is a hold-over from old Portal behavior -- we should adjust the health to compensate! -- jdw
  3003. inputInfoCopy.ScaleDamage( sk_dmg_take_scale1.GetFloat() );
  3004. if ( inputInfoCopy.GetDamage() >= m_iHealth && GameRules()->IsMultiplayer() &&
  3005. !m_Shared.InCond( PORTAL_COND_DROWNING ) ) // don't gib after drowning
  3006. {
  3007. if ( bIsTurret || (mp_should_gib_bots.GetBool() && !m_Shared.InCond( PORTAL_COND_DEATH_GIB )) )
  3008. {
  3009. m_Shared.AddCond( PORTAL_COND_DEATH_GIB );
  3010. m_Shared.m_damageInfo = inputInfoCopy;
  3011. Break( pAttacker, inputInfo );
  3012. }
  3013. else if ( !mp_should_gib_bots.GetBool() && !m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) )
  3014. {
  3015. m_Shared.AddCond( PORTAL_COND_DEATH_CRUSH );
  3016. m_Shared.m_damageInfo = inputInfoCopy;
  3017. }
  3018. }
  3019. if ( bIsTurret )
  3020. {
  3021. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  3022. g_PortalGameStats.Event_TurretDamage( inputInfoCopy.GetDamage() );
  3023. #endif
  3024. }
  3025. int ret = BaseClass::OnTakeDamage( inputInfoCopy );
  3026. // Copy the multidamage damage origin over what the base class wrote, because
  3027. // that gets translated correctly though portals.
  3028. m_DmgOrigin = inputInfo.GetDamagePosition();
  3029. // check if we are drowning in goo
  3030. bool bGooDamage = ( inputInfoCopy.GetDamageType() & DMG_RADIATION ) > 0;
  3031. if ( !( GameRules()->IsMultiplayer() && ( bGooDamage || m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) ) ) )
  3032. {
  3033. // Play a flinch!
  3034. DoAnimationEvent( PLAYERANIMEVENT_FLINCH_CHEST, 0 );
  3035. }
  3036. return ret;
  3037. }
  3038. int CPortal_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  3039. {
  3040. // set damage type sustained
  3041. m_bitsDamageType |= info.GetDamageType();
  3042. if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
  3043. return 0;
  3044. CBaseEntity * attacker = info.GetAttacker();
  3045. if ( !attacker )
  3046. return 0;
  3047. Vector vecDir = vec3_origin;
  3048. if ( info.GetInflictor() )
  3049. {
  3050. vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter();
  3051. VectorNormalize( vecDir );
  3052. }
  3053. if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK) &&
  3054. ( !attacker->IsSolidFlagSet(FSOLID_TRIGGER)) )
  3055. {
  3056. Vector force = vecDir;// * -DamageForce( WorldAlignSize(), info.GetBaseDamage() );
  3057. if ( force.z > 250.0f )
  3058. {
  3059. force.z = 250.0f;
  3060. }
  3061. ApplyAbsVelocityImpulse( force );
  3062. }
  3063. // fire global game event
  3064. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  3065. if ( event )
  3066. {
  3067. event->SetInt("userid", GetUserID() );
  3068. event->SetInt("health", MAX(0, m_iHealth) );
  3069. event->SetInt("priority", 5 ); // HLTV event priority, not transmitted
  3070. if ( attacker->IsPlayer() )
  3071. {
  3072. CBasePlayer *player = ToBasePlayer( attacker );
  3073. event->SetInt("attacker", player->GetUserID() ); // hurt by other player
  3074. }
  3075. else
  3076. {
  3077. event->SetInt("attacker", 0 ); // hurt by "world"
  3078. }
  3079. gameeventmanager->FireEvent( event );
  3080. }
  3081. // Insert a combat sound so that nearby NPCs hear battle
  3082. if ( attacker->IsNPC() )
  3083. {
  3084. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 512, 0.5, this );//<<TODO>>//magic number
  3085. }
  3086. return 1;
  3087. }
  3088. void CPortal_Player::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info )
  3089. {
  3090. // don't ever break unless we're in multiplayer
  3091. if ( !GameRules()->IsMultiplayer() )
  3092. return;
  3093. // do a screen shake
  3094. UTIL_ScreenShake( GetAbsOrigin(), 7.0f, 100.0, 1.5, 500.0f, SHAKE_START, true );
  3095. EmitSound( "CoopBot.Explode_Gib" );
  3096. //m_takedamage = DAMAGE_NO;
  3097. //m_OnBreak.FireOutput( pBreaker, this );
  3098. Vector velocity;
  3099. AngularImpulse angVelocity;
  3100. IPhysicsObject *pPhysics = VPhysicsGetObject();
  3101. Vector origin;
  3102. QAngle angles;
  3103. //AddSolidFlags( FSOLID_NOT_SOLID );
  3104. if ( pPhysics )
  3105. {
  3106. pPhysics->GetVelocity( &velocity, &angVelocity );
  3107. pPhysics->GetPosition( &origin, &angles );
  3108. pPhysics->RecheckCollisionFilter();
  3109. }
  3110. else
  3111. {
  3112. velocity = GetAbsVelocity();
  3113. QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity );
  3114. origin = GetAbsOrigin();
  3115. angles = GetAbsAngles();
  3116. }
  3117. //PhysBreakSound( this, VPhysicsGetObject(), GetAbsOrigin() );
  3118. // Allow derived classes to emit special things
  3119. //OnBreak( velocity, angVelocity, pBreaker );
  3120. breakablepropparams_t params( origin, angles, velocity, angVelocity );
  3121. params.impactEnergyScale = m_impactEnergyScale;
  3122. params.defCollisionGroup = GetCollisionGroup();
  3123. if ( params.defCollisionGroup == COLLISION_GROUP_NONE )
  3124. {
  3125. // don't automatically make anything COLLISION_GROUP_NONE or it will
  3126. // collide with debris being ejected by breaking
  3127. params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE;
  3128. }
  3129. params.defBurstScale = 100;
  3130. // in multiplayer spawn break models as clientside temp ents
  3131. CPASFilter filter( WorldSpaceCenter() );
  3132. //Vector velocity; velocity.Init();
  3133. if ( pPhysics )
  3134. pPhysics->GetVelocity( &velocity, NULL );
  3135. /*
  3136. switch ( GetMultiplayerBreakMode() )
  3137. {
  3138. case MULTIPLAYER_BREAK_DEFAULT: // default is to break client-side
  3139. case MULTIPLAYER_BREAK_CLIENTSIDE:
  3140. te->PhysicsProp( filter, -1, GetModelIndex(), m_nSkin, GetAbsOrigin(), GetAbsAngles(), velocity, true, GetEffects() );
  3141. break;
  3142. case MULTIPLAYER_BREAK_SERVERSIDE: // server-side break
  3143. if ( m_PerformanceMode != PM_NO_GIBS || breakable_disable_gib_limit.GetBool() )
  3144. {
  3145. PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, ( m_PerformanceMode == PM_FULL_GIBS ), false );
  3146. }
  3147. break;
  3148. case MULTIPLAYER_BREAK_BOTH: // pieces break from both dlls
  3149. te->PhysicsProp( filter, -1, GetModelIndex(), m_nSkin, GetAbsOrigin(), GetAbsAngles(), velocity, true, GetEffects() );
  3150. if ( m_PerformanceMode != PM_NO_GIBS || breakable_disable_gib_limit.GetBool() )
  3151. {
  3152. PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, ( m_PerformanceMode == PM_FULL_GIBS ), false );
  3153. }
  3154. break;
  3155. }
  3156. // no damage/damage force? set a burst of 100 for some movement
  3157. else if ( m_PerformanceMode != PM_NO_GIBS || breakable_disable_gib_limit.GetBool() )
  3158. {
  3159. PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, ( m_PerformanceMode == PM_FULL_GIBS ) );
  3160. }
  3161. */
  3162. te->PhysicsProp( filter, -1, GetModelIndex(), m_nSkin, GetAbsOrigin(), GetAbsAngles(), velocity, true, GetEffects() );
  3163. //UTIL_Remove( this );
  3164. }
  3165. //
  3166. //void CPortal_Player::UnDuck( void )
  3167. //{
  3168. // if( m_Local.m_bDucked != false )
  3169. // {
  3170. // m_Local.m_bDucked = false;
  3171. // UnforceButtons( IN_DUCK );
  3172. // RemoveFlag( FL_DUCKING );
  3173. // SetVCollisionState( GetAbsOrigin(), GetAbsVelocity(), VPHYS_WALK );
  3174. // }
  3175. //}
  3176. //--------------------------------------------------------------------------------------------------
  3177. // Disable the use key for the specified time (in seconds)
  3178. //--------------------------------------------------------------------------------------------------
  3179. void CPortal_Player::SetUseKeyCooldownTime( float flCooldownDuration )
  3180. {
  3181. const float flMaxCooldownDuration = 120.0f;
  3182. Assert( flCooldownDuration < flMaxCooldownDuration );
  3183. flCooldownDuration = clamp ( flCooldownDuration, 0.0f, flMaxCooldownDuration );
  3184. m_flUseKeyCooldownTime = gpGlobals->curtime + flCooldownDuration;
  3185. }
  3186. void CPortal_Player::SetForcedGrabControllerType( ForcedGrabControllerType type )
  3187. {
  3188. m_ForcedGrabController = type;
  3189. }
  3190. void CPortal_Player::UpdateVMGrab( CBaseEntity *pEntity )
  3191. {
  3192. if ( !pEntity )
  3193. return;
  3194. switch( m_ForcedGrabController )
  3195. {
  3196. case FORCE_GRAB_CONTROLLER_VM:
  3197. {
  3198. m_bUseVMGrab = true;
  3199. }
  3200. break;
  3201. case FORCE_GRAB_CONTROLLER_PHYSICS:
  3202. {
  3203. m_bUseVMGrab = false;
  3204. }
  3205. break;
  3206. case FORCE_GRAB_CONTROLLER_DEFAULT:
  3207. default:
  3208. {
  3209. #if 0 // Turning this off for now... the FOV differences cause a visual pop that people are bugging
  3210. // and we might not be getting that much for having it not collide while flinging.
  3211. if ( GetAbsVelocity().Length() > 350.0f )
  3212. {
  3213. // Going fast! Use VM
  3214. m_bUseVMGrab = true;
  3215. return;
  3216. }
  3217. #endif
  3218. if ( FClassnameIs( pEntity, "npc_portal_turret_floor" ) )
  3219. {
  3220. // It's a turret, use physics
  3221. m_bUseVMGrab = false;
  3222. return;
  3223. }
  3224. if ( GameRules() && GameRules()->IsMultiplayer() )
  3225. {
  3226. // In MP our reflective cubes need to go physics when touching or near a laser
  3227. if ( UTIL_IsReflectiveCube( pEntity ) || UTIL_IsSchrodinger( pEntity ) )
  3228. {
  3229. CPropWeightedCube *pCube = (CPropWeightedCube *)pEntity;
  3230. if ( pCube->HasLaser() )
  3231. {
  3232. // VMGrabController'd laser cubes that were grabbed through portals still think they are being
  3233. // held through portals. Clear that out here, so when it comes time to ComputeError, we won't get
  3234. // a bogus error and force drop the cube.
  3235. SetHeldObjectOnOppositeSideOfPortal( false );
  3236. SetHeldObjectPortal( NULL );
  3237. // It's redirecting a laser, use physics
  3238. m_bUseVMGrab = false;
  3239. return;
  3240. }
  3241. else
  3242. {
  3243. // Check it's distance to each laser line so the depth renders properly when the laser is between the player and cube
  3244. for ( int i = 0; i < IPortalLaserAutoList::AutoList().Count(); ++i )
  3245. {
  3246. CPortalLaser *pLaser = static_cast< CPortalLaser* >( IPortalLaserAutoList::AutoList()[ i ] );
  3247. if ( pLaser )
  3248. {
  3249. Vector vClosest = pLaser->ClosestPointOnLineSegment( pCube->GetAbsOrigin() );
  3250. if ( vClosest.DistToSqr( pCube->GetAbsOrigin() ) < ( 80.0f * 80.0f ) )
  3251. {
  3252. // VMGrabController'd laser cubes that were grabbed through portals still think they are being
  3253. // held through portals. Clear that out here, so when it comes time to ComputeError, we won't get
  3254. // a bogus error and force drop the cube.
  3255. SetHeldObjectOnOppositeSideOfPortal( false );
  3256. SetHeldObjectPortal( NULL );
  3257. // It's close to the laser
  3258. m_bUseVMGrab = false;
  3259. return;
  3260. }
  3261. }
  3262. }
  3263. }
  3264. }
  3265. // Multiplayer uses VM otherwise
  3266. m_bUseVMGrab = true;
  3267. return;
  3268. }
  3269. if ( FClassnameIs( pEntity, "npc_personality_core" ) )
  3270. {
  3271. // It's a personality core, use VM mode
  3272. m_bUseVMGrab = true;
  3273. return;
  3274. }
  3275. m_bUseVMGrab = false;
  3276. }
  3277. break;
  3278. } //Switch forced grab controller
  3279. }
  3280. void CPortal_Player::IncrementPortalsPlaced( bool bSecondaryPortal )
  3281. {
  3282. if ( bSecondaryPortal )
  3283. {
  3284. FirePlayerProxyOutput( "OnSecondaryPortalPlaced", variant_t(), this, this );
  3285. }
  3286. else
  3287. {
  3288. FirePlayerProxyOutput( "OnPrimaryPortalPlaced", variant_t(), this, this );
  3289. }
  3290. m_StatsThisLevel.iNumPortalsPlaced++;
  3291. #if !defined( _GAMECONSOLE )
  3292. g_Portal2ResearchDataTracker.IncrementPortalFired( this );
  3293. #endif // !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  3294. if ( m_iBonusChallenge == PORTAL_CHALLENGE_PORTALS )
  3295. SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumPortalsPlaced ) );
  3296. }
  3297. void CPortal_Player::IncrementStepsTaken( void )
  3298. {
  3299. m_StatsThisLevel.iNumStepsTaken++;
  3300. if( GetPortalMPStats() )
  3301. {
  3302. GetPortalMPStats()->IncrementPlayerSteps( this );
  3303. }
  3304. #if !defined( _GAMECONSOLE )
  3305. g_Portal2ResearchDataTracker.IncrementStepsTaken( this );
  3306. #endif // !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  3307. if ( m_iBonusChallenge == PORTAL_CHALLENGE_STEPS )
  3308. SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumStepsTaken ) );
  3309. }
  3310. void CPortal_Player::IncrementDistanceTaken( void )
  3311. {
  3312. m_StatsThisLevel.fDistanceTaken += VectorLength( GetAbsOrigin() - m_vPrevPosition );
  3313. m_vPrevPosition = GetAbsOrigin();
  3314. }
  3315. void CPortal_Player::UpdateSecondsTaken( void )
  3316. {
  3317. float fSecondsSinceLastUpdate = ( gpGlobals->curtime - m_fTimeLastNumSecondsUpdate );
  3318. m_StatsThisLevel.fNumSecondsTaken += fSecondsSinceLastUpdate;
  3319. m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
  3320. if ( m_iBonusChallenge == PORTAL_CHALLENGE_TIME )
  3321. SetBonusProgress( static_cast<int>( m_StatsThisLevel.fNumSecondsTaken ) );
  3322. if ( m_fNeuroToxinDamageTime > 0.0f )
  3323. {
  3324. float fTimeRemaining = m_fNeuroToxinDamageTime - gpGlobals->curtime;
  3325. if ( fTimeRemaining < 0.0f )
  3326. {
  3327. CTakeDamageInfo info;
  3328. info.SetDamage( gpGlobals->frametime * 50.0f );
  3329. info.SetDamageType( DMG_NERVEGAS );
  3330. TakeDamage( info );
  3331. fTimeRemaining = 0.0f;
  3332. }
  3333. PauseBonusProgress( false );
  3334. SetBonusProgress( static_cast<int>( fTimeRemaining ) );
  3335. }
  3336. }
  3337. void CPortal_Player::ResetThisLevelStats( void )
  3338. {
  3339. m_StatsThisLevel.iNumPortalsPlaced = 0;
  3340. m_StatsThisLevel.iNumStepsTaken = 0;
  3341. m_StatsThisLevel.fNumSecondsTaken = 0.0f;
  3342. m_StatsThisLevel.fDistanceTaken = 0.0f;
  3343. if ( m_iBonusChallenge != PORTAL_CHALLENGE_NONE )
  3344. SetBonusProgress( 0 );
  3345. }
  3346. //-----------------------------------------------------------------------------
  3347. // Purpose: Update the area bits variable which is networked down to the client to determine
  3348. // which area portals should be closed based on visibility.
  3349. // Input : *pvs - pvs to be used to determine visibility of the portals
  3350. //-----------------------------------------------------------------------------
  3351. void CPortal_Player::UpdatePortalViewAreaBits( unsigned char *pvs, int pvssize )
  3352. {
  3353. Assert ( pvs );
  3354. int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
  3355. if( iPortalCount == 0 )
  3356. return;
  3357. CPortal_Base2D **pPortals = CPortal_Base2D_Shared::AllPortals.Base();
  3358. int *portalArea = (int *)stackalloc( sizeof( int ) * iPortalCount );
  3359. bool *bUsePortalForVis = (bool *)stackalloc( sizeof( bool ) * iPortalCount );
  3360. unsigned char *portalTempBits = (unsigned char *)stackalloc( sizeof( unsigned char ) * 32 * iPortalCount );
  3361. COMPILE_TIME_ASSERT( (sizeof( unsigned char ) * 32) >= sizeof( ((CPlayerLocalData*)0)->m_chAreaBits ) );
  3362. // setup area bits for these portals
  3363. for ( int i = 0; i < iPortalCount; ++i )
  3364. {
  3365. CPortal_Base2D* pLocalPortal = pPortals[ i ];
  3366. // Make sure this portal is active before adding it's location to the pvs
  3367. if ( pLocalPortal && pLocalPortal->IsActive() )
  3368. {
  3369. CPortal_Base2D* pRemotePortal = pLocalPortal->m_hLinkedPortal.Get();
  3370. // Make sure this portal's linked portal is in the PVS before we add what it can see
  3371. if ( pRemotePortal && pRemotePortal->IsActivedAndLinked() && pRemotePortal->NetworkProp() &&
  3372. pRemotePortal->NetworkProp()->IsInPVS( edict(), pvs, pvssize ) )
  3373. {
  3374. portalArea[ i ] = engine->GetArea( pPortals[ i ]->GetAbsOrigin() );
  3375. if ( portalArea [ i ] >= 0 )
  3376. {
  3377. bUsePortalForVis[ i ] = true;
  3378. }
  3379. engine->GetAreaBits( portalArea[ i ], &portalTempBits[ i * 32 ], sizeof( unsigned char ) * 32 );
  3380. }
  3381. }
  3382. }
  3383. // Use the union of player-view area bits and the portal-view area bits of each portal
  3384. for ( int i = 0; i < m_Local.m_chAreaBits.Count(); i++ )
  3385. {
  3386. for ( int j = 0; j < iPortalCount; ++j )
  3387. {
  3388. // If this portal is active, in PVS and it's location is valid
  3389. if ( bUsePortalForVis[ j ] )
  3390. {
  3391. m_Local.m_chAreaBits.Set( i, m_Local.m_chAreaBits[ i ] | portalTempBits[ (j * 32) + i ] );
  3392. }
  3393. }
  3394. }
  3395. }
  3396. //-----------------------------------------------------------------------------
  3397. // Purpose: Recursive function to add any areas seen by portals in the pViewEnt's pvs (non-portal effected pvs) to the engine's fat pvs and networked area list.
  3398. // Input : *pViewEnt - The viewing ent. All portals in this entity's pvs will have their areas networked.
  3399. // area - area of the pViewEnt
  3400. // *pvs - pvs bytes passed from the engine
  3401. // pvssize - size of pvs in bytes
  3402. // vec_AreasNetworked - List of areas already marked to prevent 1->2->1 re-enterancy.
  3403. //-----------------------------------------------------------------------------
  3404. void PortalSetupVisibility( CBaseEntity *pViewEnt, const Vector &vViewOrigin, unsigned char *pvs, int pvssize )
  3405. {
  3406. CPVS_Extender::ComputeExtendedPVS( pViewEnt, vViewOrigin, pvs, pvssize, 10 );
  3407. }
  3408. void CPortal_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  3409. {
  3410. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  3411. int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
  3412. // At this point the EyePosition has been added as a view origin, but if we are currently stuck
  3413. // in a portal, our EyePosition may return a point in solid. Find the reflected eye position
  3414. // and use that as a vis origin instead.
  3415. if ( m_hPortalEnvironment )
  3416. {
  3417. CPortal_Base2D *pPortal = NULL, *pRemotePortal = NULL;
  3418. pPortal = m_hPortalEnvironment;
  3419. pRemotePortal = pPortal->m_hLinkedPortal;
  3420. if ( pPortal && pRemotePortal && pPortal->IsActive() && pRemotePortal->IsActive() )
  3421. {
  3422. Vector ptPortalCenter = pPortal->GetAbsOrigin();
  3423. Vector vPortalForward;
  3424. pPortal->GetVectors( &vPortalForward, NULL, NULL );
  3425. Vector eyeOrigin = EyePosition();
  3426. Vector vEyeToPortalCenter = ptPortalCenter - eyeOrigin;
  3427. float fPortalDist = vPortalForward.Dot( vEyeToPortalCenter );
  3428. if( fPortalDist > 0.0f ) //eye point is behind portal
  3429. {
  3430. // Move eye origin to it's transformed position on the other side of the portal
  3431. UTIL_Portal_PointTransform( pPortal->MatrixThisToLinked(), eyeOrigin, eyeOrigin );
  3432. // Use this as our view origin (as this is where the client will be displaying from)
  3433. engine->AddOriginToPVS( eyeOrigin );
  3434. if ( !pViewEntity || pViewEntity->IsPlayer() )
  3435. {
  3436. area = engine->GetArea( eyeOrigin );
  3437. }
  3438. }
  3439. }
  3440. }
  3441. PointCameraSetupVisibility( this, area, pvs, pvssize );
  3442. PortalSetupVisibility( this, EyePosition(), pvs, pvssize );
  3443. if ( m_bClientCheckPVSDirty )
  3444. {
  3445. UTIL_SetClientCheckPVS( edict(), pvs, pvssize );
  3446. m_bClientCheckPVSDirty = false;
  3447. }
  3448. }
  3449. //-----------------------------------------------------------------------------
  3450. // Purpose:
  3451. //-----------------------------------------------------------------------------
  3452. bool CPortal_Player::FindRemoteTauntViewpoint( Vector *pOriginOut, QAngle *pAnglesOut )
  3453. {
  3454. const float flRadius = (38*12);
  3455. CBaseEntity *pRemoteViewer = gEntList.FindEntityByClassnameNearest( "npc_security_camera", EyePosition(), flRadius );
  3456. if ( pRemoteViewer == NULL )
  3457. return false;
  3458. CNPC_SecurityCamera *pNPC = assert_cast<CNPC_SecurityCamera *>(pRemoteViewer);
  3459. if ( pNPC == NULL || !pNPC->IsActive() )
  3460. return false;
  3461. if ( pOriginOut && pAnglesOut )
  3462. {
  3463. if ( !pNPC->GetAttachment( "lens", *pOriginOut, *pAnglesOut ) )
  3464. return false;
  3465. Vector vecCam = *pOriginOut;
  3466. // check if the camera is in line of sight
  3467. trace_t tr;
  3468. UTIL_TraceLine( vecCam, WorldSpaceCenter(), MASK_SHOT, pRemoteViewer, COLLISION_GROUP_NONE, &tr );
  3469. if ( tr.m_pEnt != this )
  3470. return false;
  3471. Vector vecDir;
  3472. VectorSubtract( WorldSpaceCenter(), vecCam, vecDir );
  3473. VectorNormalize( vecDir );
  3474. *pOriginOut = vecCam + (vecDir*32);
  3475. VectorAngles( vecDir, *pAnglesOut );
  3476. m_hRemoteTauntCamera = pNPC;
  3477. pNPC->TauntedByPlayer( this );
  3478. }
  3479. return true;
  3480. }
  3481. //-----------------------------------------------------------------------------
  3482. // Purpose:
  3483. //-----------------------------------------------------------------------------
  3484. void CPortal_Player::Taunt( const char *pchTauntForce /*=NULL*/, bool bAuto /*= false*/ )
  3485. {
  3486. // This doesn't work in singleplayer
  3487. if ( g_pGameRules->IsMultiplayer() == false )
  3488. return;
  3489. // No taunting while holding stuff because it looks dumb, and if we drop that stuff it might get stuck behind walls
  3490. if ( IsHoldingEntity( NULL ) )
  3491. return;
  3492. if ( !bAuto && m_bTauntDisabled )
  3493. {
  3494. return;
  3495. }
  3496. bool bTeamAccept = ( pchTauntForce && V_strcmp( pchTauntForce, "team_accept" ) == 0 );
  3497. bool bTease = ( pchTauntForce && V_strcmp( pchTauntForce, "teamtease" ) == 0 );
  3498. // Don't re-taunt!
  3499. if ( m_Shared.InCond( PORTAL_COND_TAUNTING ) && !bTeamAccept )
  3500. return;
  3501. if ( m_nTeamTauntState == TEAM_TAUNT_SUCCESS )
  3502. return;
  3503. if ( !pchTauntForce )
  3504. {
  3505. m_szTauntForce.GetForModify()[ 0 ] = '\0';
  3506. SetTeamTauntState( TEAM_TAUNT_NONE );
  3507. }
  3508. else
  3509. {
  3510. // Don't hold objects while doing non air taunts
  3511. ForceDropOfCarriedPhysObjects( NULL );
  3512. if ( V_strcmp( pchTauntForce, "item" ) == 0 )
  3513. {
  3514. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  3515. // FIXME: Use the item to decide what taunt to do
  3516. CEconItemView *pItem = GetItemInLoadoutSlot( LOADOUT_POSITION_GESTURE );
  3517. if ( pItem && pItem->IsValid() )
  3518. {
  3519. pchTauntForce = pItem->GetStaticData()->GetBrassModelOverride();
  3520. }
  3521. else
  3522. #endif
  3523. {
  3524. pchTauntForce = mp_taunt_item.GetString();
  3525. }
  3526. }
  3527. if ( bTeamAccept || bTease )
  3528. {
  3529. CPortal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherConnectedPlayer( this ) );
  3530. if ( pOtherPlayer )
  3531. {
  3532. if ( bTease )
  3533. {
  3534. if ( GetDistanceToEntity( pOtherPlayer ) < 150.0f )
  3535. {
  3536. V_strncpy( pOtherPlayer->m_szTauntForce.GetForModify(), GetTeamNumber() == TEAM_BLUE ? "TeamBallTease" : "TeamEggTease", PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH );
  3537. bTeamAccept = true;
  3538. }
  3539. else
  3540. {
  3541. V_strncpy( m_szTauntForce.GetForModify(), pchTauntForce, PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH );
  3542. SetTeamTauntState( TEAM_TAUNT_NONE );
  3543. }
  3544. }
  3545. if ( bTeamAccept )
  3546. {
  3547. // Copy their taunt
  3548. V_strncpy( m_szTauntForce.GetForModify(), pOtherPlayer->m_szTauntForce.Get(), PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH );
  3549. if ( PortalMPGameRules() )
  3550. {
  3551. PortalMPGameRules()->ShuffleRPSOutcome();
  3552. }
  3553. if ( SolveTeamTauntPositionAndAngles( pOtherPlayer ) )
  3554. {
  3555. SetTeamTauntState( TEAM_TAUNT_SUCCESS );
  3556. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
  3557. if ( pEntity )
  3558. {
  3559. char szScriptCommand[ 64 ];
  3560. V_snprintf( szScriptCommand, sizeof( szScriptCommand ), "CoopBotAnimation(%i,\"%s\")", ( GetTeamNumber() == TEAM_BLUE ? 2 : 1 ), m_szTauntForce.Get() );
  3561. pEntity->RunScript( szScriptCommand, "BotAnimationCommand" );
  3562. }
  3563. // On server notify all players that team taunt happened
  3564. KeyValues *kvNotifyTaunt = new KeyValues( "OnCoopBotTaunt" );
  3565. kvNotifyTaunt->SetString( "taunt", m_szTauntForce.Get() );
  3566. UTIL_SendClientCommandKVToPlayer( kvNotifyTaunt );
  3567. // Track multiplayer stats for successful team taunts
  3568. if( GetPortalMPStats() )
  3569. {
  3570. GetPortalMPStats()->TeamTauntSuccess( pOtherPlayer->m_szTauntForce.Get() );
  3571. }
  3572. }
  3573. else
  3574. {
  3575. // Final positions weren't valid
  3576. return;
  3577. }
  3578. }
  3579. }
  3580. }
  3581. else
  3582. {
  3583. V_strncpy( m_szTauntForce.GetForModify(), pchTauntForce, PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH );
  3584. SetTeamTauntState( TEAM_TAUNT_NONE );
  3585. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
  3586. if ( pEntity )
  3587. {
  3588. char szScriptCommand[ 64 ];
  3589. V_snprintf( szScriptCommand, sizeof( szScriptCommand ), "CoopBotAnimation(%i,\"%s\")", ( GetTeamNumber() == TEAM_BLUE ? 2 : 1 ), m_szTauntForce.Get() );
  3590. pEntity->RunScript( szScriptCommand, "BotAnimationCommand" );
  3591. }
  3592. }
  3593. }
  3594. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  3595. // Record taunts used in gamestats (jeep says null means airtaunt)
  3596. g_PortalGameStats.Event_PlayerTaunt( this, pchTauntForce ? pchTauntForce : "airtaunt" );
  3597. #endif //!defined( _GAMECONSOLE )
  3598. StartTaunt();
  3599. }
  3600. bool CPortal_Player::SolveTeamTauntPositionAndAngles( CPortal_Player *pInitiator )
  3601. {
  3602. if ( ValidateTeamTaunt( pInitiator, pInitiator->m_vTauntPosition.GetForModify(), pInitiator->m_vTauntAngles.GetForModify(),
  3603. m_vTauntPosition.GetForModify(), m_vTauntAngles.GetForModify() ) )
  3604. {
  3605. // Start initiator's taunt
  3606. pInitiator->SetTeamTauntState( TEAM_TAUNT_SUCCESS );
  3607. pInitiator->StartTaunt();
  3608. return true;
  3609. }
  3610. return false;
  3611. }
  3612. bool CPortal_Player::ValidateTeamTaunt( CPortal_Player *pInitiator, Vector &vInitiatorPos, QAngle &angInitiatorAng, Vector &vAcceptorPos, QAngle &angAcceptorAng, bool bRecursed /*= false*/ )
  3613. {
  3614. if ( !pInitiator )
  3615. return false;
  3616. if ( !pInitiator->GetGroundEntity() )
  3617. return false;
  3618. if ( !GetGroundEntity() )
  3619. return false;
  3620. if ( GetAbsOrigin().DistTo( pInitiator->GetAbsOrigin() ) > 250.0f )
  3621. {
  3622. return false;
  3623. }
  3624. // Don't validate this team taunt if the two bots' z position varies by too much, even if they are close.
  3625. float flDeltaZ = fabs( GetAbsOrigin().z - pInitiator->GetAbsOrigin().z );
  3626. if ( flDeltaZ > ALLOWED_TEAM_TAUNT_Z_DIST )
  3627. {
  3628. return false;
  3629. }
  3630. // Don't validate this team taunt if something solid is in the way.
  3631. trace_t wall_check_tr;
  3632. UTIL_TraceLine( WorldSpaceCenter(), pInitiator->WorldSpaceCenter(), MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &wall_check_tr );
  3633. if ( wall_check_tr.fraction < 1.0f && wall_check_tr.m_pEnt != pInitiator )
  3634. {
  3635. return false;
  3636. }
  3637. bool bValid = true;
  3638. // Get the interaction params from ballbot
  3639. CUtlVector<ScriptedNPCInteraction_t> *pScriptedInteractions = NULL;
  3640. if ( GetTeamNumber() == TEAM_BLUE )
  3641. {
  3642. pScriptedInteractions = GetScriptedInteractions();
  3643. }
  3644. else
  3645. {
  3646. pScriptedInteractions = pInitiator->GetScriptedInteractions();
  3647. }
  3648. ScriptedNPCInteraction_t *pInteraction = NULL;
  3649. for ( int nInteraction = 0; nInteraction < pScriptedInteractions->Count(); nInteraction++ )
  3650. {
  3651. ScriptedNPCInteraction_t *pTempInteraction = &((*pScriptedInteractions)[ nInteraction ]);
  3652. if ( Q_stricmp( pTempInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence.ToCStr(), pInitiator->m_szTauntForce.Get() ) == 0 )
  3653. {
  3654. pInteraction = pTempInteraction;
  3655. break;
  3656. }
  3657. }
  3658. // Reference forward is initiator to acceptor
  3659. Vector vForward = GetAbsOrigin() - pInitiator->GetAbsOrigin();
  3660. vForward.z = 0.0f;
  3661. VectorNormalize( vForward );
  3662. // Rotate initiator, leave him in position
  3663. QAngle angNew;
  3664. VectorAngles( vForward, angNew );
  3665. angInitiatorAng = angNew;
  3666. vInitiatorPos = pInitiator->GetAbsOrigin();
  3667. if ( !pInteraction )
  3668. {
  3669. // Couldn't find an interaction! Make them overlap so we know there's a bug!
  3670. VectorAngles( vForward, angNew );
  3671. angAcceptorAng = angNew;
  3672. vAcceptorPos = vInitiatorPos;
  3673. }
  3674. else
  3675. {
  3676. // Rot matrix for forward to initiator
  3677. matrix3x4_t matToInitiator;
  3678. AngleMatrix( angInitiatorAng, matToInitiator );
  3679. // Build acceptor taunt angles
  3680. Vector vRelativeForward;
  3681. QAngle ang = pInteraction->angRelativeAngles;
  3682. ang[ YAW ] *= -1.0f;
  3683. if ( GetTeamNumber() == TEAM_BLUE )
  3684. {
  3685. // Invert the angles
  3686. ang = -ang;
  3687. }
  3688. AngleVectors( ang, &vRelativeForward );
  3689. Vector vRelativeForwardOffset;
  3690. VectorTransform( vRelativeForward, matToInitiator, vRelativeForwardOffset );
  3691. VectorAngles( vRelativeForwardOffset, angAcceptorAng );
  3692. if ( GetTeamNumber() == TEAM_BLUE )
  3693. {
  3694. // Rot matrix for forward to acceptor
  3695. AngleMatrix( angAcceptorAng, matToInitiator );
  3696. }
  3697. // Build acceptor taunt position
  3698. Vector vAcceptorRelativeOriginOffset;
  3699. Vector vec = pInteraction->vecRelativeOrigin;
  3700. vec.y *= -1.0f;
  3701. VectorTransform( vec, matToInitiator, vAcceptorRelativeOriginOffset );
  3702. if ( GetTeamNumber() == TEAM_BLUE )
  3703. {
  3704. // Opposite offset
  3705. vAcceptorPos = vInitiatorPos - vAcceptorRelativeOriginOffset;
  3706. }
  3707. else
  3708. {
  3709. vAcceptorPos = vInitiatorPos + vAcceptorRelativeOriginOffset;
  3710. }
  3711. // Make sure position touches floor
  3712. trace_t tr;
  3713. CTraceFilterSkipTwoEntities filter( this, pInitiator );
  3714. Ray_t ray;
  3715. ray.Init( vAcceptorPos + Vector( 0.0f, 0.0f, 1.0f ), vAcceptorPos + Vector( 0.0f, 0.0f, -ALLOWED_TEAM_TAUNT_Z_DIST ), GetPlayerMins(), GetPlayerMaxs() );
  3716. UTIL_TraceRay( ray, MASK_PLAYERSOLID, &filter, &tr );
  3717. if ( tr.fraction == 1.f )
  3718. return false; // No ground beneath this target location, we would be floating in the air!
  3719. if ( tr.startsolid || tr.fraction <= 0.0f )
  3720. {
  3721. // Scoot it up if needed
  3722. ray.Init( vAcceptorPos + Vector( 0.0f, 0.0f, 50.0f ), vAcceptorPos, GetPlayerMins(), GetPlayerMaxs() );
  3723. UTIL_TraceRay( ray, MASK_PLAYERSOLID, &filter, &tr );
  3724. if ( tr.startsolid || tr.fraction <= 0.0f )
  3725. {
  3726. bValid = false;
  3727. }
  3728. }
  3729. if ( bValid )
  3730. {
  3731. vAcceptorPos = tr.endpos;
  3732. UTIL_TraceLine( vAcceptorPos + ( GetPlayerMins() + GetPlayerMaxs() ) * 0.5f, pInitiator->WorldSpaceCenter(), MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &tr );
  3733. if ( tr.fraction < 1.0f && tr.m_pEnt != pInitiator )
  3734. {
  3735. bValid = false;
  3736. }
  3737. }
  3738. }
  3739. if ( !bValid && !bRecursed )
  3740. {
  3741. // Try the inversion where the initiator moves and the acceptor holds still
  3742. bValid = pInitiator->ValidateTeamTaunt( this, vAcceptorPos, angAcceptorAng, vInitiatorPos, angInitiatorAng, true );
  3743. }
  3744. return bValid;
  3745. }
  3746. void CPortal_Player::StartTaunt( void )
  3747. {
  3748. CBaseEntity *pOldRemoteTauntCamera = m_hRemoteTauntCamera.Get();
  3749. char szResponse[AI_Response::MAX_RESPONSE_NAME];
  3750. if ( SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_TAUNT, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME ) )
  3751. {
  3752. // Get the duration of the scene.
  3753. float flDuration = GetSceneDuration( szResponse );
  3754. m_vPreTauntAngles = EyeAngles();
  3755. // See if there's a security camera around, and if so, use it as our viewing target
  3756. m_bTauntRemoteView = FindRemoteTauntViewpoint( &m_vecRemoteViewOrigin.GetForModify(), &m_vecRemoteViewAngles.GetForModify() );
  3757. if ( m_bTauntRemoteView )
  3758. {
  3759. // Go into camera view
  3760. m_bTauntRemoteViewFOVFixup = true;
  3761. if ( !pOldRemoteTauntCamera )
  3762. {
  3763. SetFOV( this, 125, 0.0f ); // previous value was 110
  3764. SetFOV( this, 105, 1.0f ); // previous value was 90
  3765. }
  3766. // let Glados know that the player is taunting a camera
  3767. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
  3768. if ( pEntity )
  3769. {
  3770. int nTeam = GetTeamNumber();
  3771. pEntity->RunScript( UTIL_VarArgs( "PlayerTauntCamera(%i,\"%s\")", (nTeam == TEAM_BLUE) ? 2 : 1, m_szTauntForce.Get() ), "StartTaunt" );
  3772. }
  3773. UTIL_RecordAchievementEvent( UTIL_VarArgs( "ACH.TAUNT_CAMERA[%i]", PortalMPGameRules()->GetCoopSection() ), this );
  3774. }
  3775. else
  3776. {
  3777. // Taunt Defaults
  3778. m_fTauntCameraDistance = portal_tauntcam_dist.GetFloat();
  3779. m_vecRemoteViewAngles = QAngle( 20.0f, 160.0f, 0.0f );
  3780. if ( m_szTauntForce[ 0 ] != '\0' )
  3781. {
  3782. CUtlVector<ScriptedNPCInteraction_t> *pScriptedInteractions = GetScriptedInteractions();
  3783. for ( int nInteraction = 0; nInteraction < pScriptedInteractions->Count(); nInteraction++ )
  3784. {
  3785. ScriptedNPCInteraction_t *pInteraction = &((*pScriptedInteractions)[ nInteraction ]);
  3786. if ( Q_stricmp( pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence.ToCStr(), m_szTauntForce.Get() ) == 0 )
  3787. {
  3788. // Custom camera angles
  3789. m_fTauntCameraDistance = pInteraction->flCameraDistance;
  3790. m_vecRemoteViewAngles = pInteraction->angCameraAngles;
  3791. break;
  3792. }
  3793. }
  3794. }
  3795. }
  3796. IGameEvent *event = gameeventmanager->CreateEvent( "player_gesture" );
  3797. if ( event )
  3798. {
  3799. event->SetInt( "userid", GetUserID() );
  3800. event->SetBool( "air", false );
  3801. gameeventmanager->FireEvent( event );
  3802. }
  3803. m_Shared.m_flTauntRemoveTime = gpGlobals->curtime + flDuration + 0.5f;
  3804. m_Shared.AddCond( PORTAL_COND_TAUNTING );
  3805. // Check for circumstances to award the 'You monster' achievement.
  3806. CBaseEntity* pEnt = NULL;
  3807. while ( ( pEnt = gEntList.FindEntityByClassname( pEnt, "npc_portal_turret_floor" ) ) != NULL )
  3808. {
  3809. if ( PointWithinViewAngle( EyePosition(), pEnt->WorldSpaceCenter(), EyeDirection3D(), 0.85f ) )
  3810. {
  3811. CNPC_Portal_FloorTurret *pTurret = (CNPC_Portal_FloorTurret*)pEnt;
  3812. Assert ( pTurret );
  3813. if ( pTurret && pTurret->IsProjectedWallBlockingTurretFromPlayer( this ) )
  3814. {
  3815. UTIL_RecordAchievementEvent( "ACH.YOU_MONSTER", this );
  3816. }
  3817. }
  3818. }
  3819. }
  3820. }
  3821. //-----------------------------------------------------------------------------
  3822. // Purpose:
  3823. //-----------------------------------------------------------------------------
  3824. bool CPortal_Player::IsHoldingEntity( CBaseEntity *pEnt )
  3825. {
  3826. return PlayerPickupControllerIsHoldingEntity( m_hUseEntity, pEnt );
  3827. }
  3828. //-----------------------------------------------------------------------------
  3829. // Purpose: Queues up a use deny sound, played in ItemPostFrame.
  3830. //-----------------------------------------------------------------------------
  3831. void CPortal_Player::PlayUseDenySound()
  3832. {
  3833. m_bPlayUseDenySound = true;
  3834. }
  3835. //---------------------------------------------------------
  3836. // Purpose:
  3837. //---------------------------------------------------------
  3838. Vector CPortal_Player::EyeDirection2D( void )
  3839. {
  3840. Vector vecReturn = EyeDirection3D();
  3841. vecReturn.z = 0;
  3842. vecReturn.AsVector2D().NormalizeInPlace();
  3843. return vecReturn;
  3844. }
  3845. //---------------------------------------------------------
  3846. // Purpose:
  3847. //---------------------------------------------------------
  3848. Vector CPortal_Player::EyeDirection3D( void )
  3849. {
  3850. Vector vecForward;
  3851. AngleVectors( EyeAngles(), &vecForward );
  3852. return vecForward;
  3853. }
  3854. //-----------------------------------------------------------------------------
  3855. // Purpose:
  3856. //-----------------------------------------------------------------------------
  3857. float CPortal_Player::GetHeldObjectMass( IPhysicsObject *pHeldObject )
  3858. {
  3859. float mass = PlayerPickupGetHeldObjectMass( m_hUseEntity, pHeldObject );
  3860. if ( mass == 0.0f )
  3861. {
  3862. mass = PhysCannonGetHeldObjectMass( GetActiveWeapon(), pHeldObject );
  3863. }
  3864. return mass;
  3865. }
  3866. extern CBaseEntity *GetCoopSpawnLocation( int iTeam ); //in info_coop_spawn.cpp
  3867. CBaseEntity* CPortal_Player::EntSelectSpawnPoint( void )
  3868. {
  3869. if ( !g_pGameRules->IsMultiplayer() )
  3870. {
  3871. return BaseClass::EntSelectSpawnPoint();
  3872. }
  3873. if( g_pGameRules->IsCoOp() )
  3874. {
  3875. switch( GetTeamNumber() )
  3876. {
  3877. case TEAM_UNASSIGNED:
  3878. //case TEAM_SPECTATOR:
  3879. PickTeam();
  3880. }
  3881. CBaseEntity *pSpawnLocation = GetCoopSpawnLocation( GetTeamNumber() );
  3882. if( pSpawnLocation )
  3883. return pSpawnLocation;
  3884. }
  3885. CBaseEntity *pSpot = NULL;
  3886. CBaseEntity *pLastSpawnPoint = g_pLastSpawn;
  3887. const char *pSpawnpointName = "info_player_start";
  3888. pSpot = pLastSpawnPoint;
  3889. // Randomize the start spot
  3890. for ( int i = random->RandomInt(1,5); i > 0; i-- )
  3891. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  3892. if ( !pSpot ) // skip over the null point
  3893. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  3894. CBaseEntity *pFirstSpot = pSpot;
  3895. do
  3896. {
  3897. if ( pSpot )
  3898. {
  3899. // check if pSpot is valid
  3900. if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
  3901. {
  3902. if ( pSpot->GetLocalOrigin() == vec3_origin )
  3903. {
  3904. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  3905. continue;
  3906. }
  3907. // if so, go to pSpot
  3908. goto ReturnSpot;
  3909. }
  3910. }
  3911. // increment pSpot
  3912. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  3913. } while ( pSpot != pFirstSpot ); // loop if we're not back to the start
  3914. #if 0
  3915. // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
  3916. edict_t *player = edict();
  3917. if ( pSpot )
  3918. {
  3919. CBaseEntity *ent = NULL;
  3920. for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  3921. {
  3922. // if ent is a client, kill em (unless they are ourselves)
  3923. if ( ent->IsPlayer() && !(ent->edict() == player) )
  3924. ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) );
  3925. }
  3926. goto ReturnSpot;
  3927. }
  3928. #endif // 0
  3929. if ( !pSpot )
  3930. {
  3931. pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" );
  3932. if ( pSpot )
  3933. goto ReturnSpot;
  3934. }
  3935. ReturnSpot:
  3936. g_pLastSpawn = pSpot;
  3937. //m_flSlamProtectTime = gpGlobals->curtime + 0.5;
  3938. return pSpot;
  3939. }
  3940. static int s_CoopTeamAssignments[MAX_PLAYERS] = { 0 };
  3941. void CPortal_Player::PickTeam( void )
  3942. {
  3943. int iIndex = ENTINDEX( this ) - 1;
  3944. if( g_pGameRules->IsCoOp() && (s_CoopTeamAssignments[iIndex] != 0) )
  3945. {
  3946. ChangeTeam( s_CoopTeamAssignments[iIndex] );
  3947. return;
  3948. }
  3949. //picks lowest or random
  3950. CTeam *pRed = g_Teams[TEAM_RED];
  3951. CTeam *pBlue = g_Teams[TEAM_BLUE];
  3952. if ( pRed->GetNumPlayers() > pBlue->GetNumPlayers() )
  3953. {
  3954. ChangeTeam( TEAM_BLUE );
  3955. }
  3956. else if ( pRed->GetNumPlayers() < pBlue->GetNumPlayers() )
  3957. {
  3958. ChangeTeam( TEAM_RED );
  3959. }
  3960. else
  3961. {
  3962. if ( mp_server_player_team.GetInt() == 0 )
  3963. {
  3964. ChangeTeam( TEAM_BLUE );
  3965. }
  3966. else if ( mp_server_player_team.GetInt() == 1 )
  3967. {
  3968. ChangeTeam( TEAM_RED );
  3969. }
  3970. else
  3971. {
  3972. ChangeTeam( random->RandomInt( TEAM_RED, TEAM_BLUE ) );
  3973. }
  3974. }
  3975. if( g_pGameRules->IsCoOp() )
  3976. {
  3977. s_CoopTeamAssignments[iIndex] = GetTeamNumber();
  3978. }
  3979. }
  3980. void CPortal_Player::ClientDisconnected( edict_t *pPlayer )
  3981. {
  3982. s_CoopTeamAssignments[ENTINDEX( pPlayer ) - 1] = 0;
  3983. }
  3984. void CPortal_Player::ChangeTeam( int iTeamNum )
  3985. {
  3986. BaseClass::ChangeTeam( iTeamNum );
  3987. if ( g_pGameRules->IsMultiplayer() == false )
  3988. {
  3989. SetName( MAKE_STRING( "player" ) );
  3990. return;
  3991. }
  3992. // Change our model at this point
  3993. if ( iTeamNum == TEAM_BLUE )
  3994. {
  3995. SetName( MAKE_STRING( "blue" ) );
  3996. }
  3997. else if ( iTeamNum == TEAM_RED )
  3998. {
  3999. SetName( MAKE_STRING( "red" ) );
  4000. }
  4001. SetPlayerModel();
  4002. ClearScriptedInteractions();
  4003. ParseScriptedInteractions();
  4004. }
  4005. void CPortal_Player::ApplyPortalTeleportation( const CPortal_Base2D *pEnteredPortal, CMoveData *pMove )
  4006. {
  4007. #if PLAYERPORTALDEBUGSPEW == 1
  4008. Warning( "SERVER CPortal_Player::ApplyPortalTeleportation( %f %i )\n", gpGlobals->curtime, m_pCurrentCommand->command_number );
  4009. #endif
  4010. //catalog the pending transform
  4011. {
  4012. RecentPortalTransform_t temp;
  4013. temp.command_number = GetCurrentUserCommand()->command_number;
  4014. temp.Portal = pEnteredPortal;
  4015. temp.matTransform = pEnteredPortal->m_matrixThisToLinked.As3x4();
  4016. m_PendingPortalTransforms.AddToTail( temp );
  4017. //prune the pending transforms so it doesn't get ridiculously huge if the client stops responding while in an infinite fall or something
  4018. while( m_PendingPortalTransforms.Count() > 64 )
  4019. {
  4020. m_PendingPortalTransforms.Remove( 0 );
  4021. }
  4022. }
  4023. CBaseEntity *pHeldEntity = GetPlayerHeldEntity( this );
  4024. if ( pHeldEntity && !IsUsingVMGrab() )
  4025. {
  4026. ToggleHeldObjectOnOppositeSideOfPortal();
  4027. SetHeldObjectPortal( IsHeldObjectOnOppositeSideOfPortal() ? pEnteredPortal->m_hLinkedPortal.Get() : NULL );
  4028. }
  4029. //transform m_PlayerAnimState yaws
  4030. m_PlayerAnimState->TransformYAWs( pEnteredPortal->m_matrixThisToLinked.As3x4() );
  4031. for ( int i = 0; i < MAX_VIEWMODELS; i++ )
  4032. {
  4033. CBaseViewModel *pViewModel = GetViewModel( i );
  4034. if ( !pViewModel )
  4035. continue;
  4036. pViewModel->m_vecLastFacing = pEnteredPortal->m_matrixThisToLinked.ApplyRotation( pViewModel->m_vecLastFacing );
  4037. }
  4038. //physics transform
  4039. {
  4040. SetVCollisionState( pMove->GetAbsOrigin(), pMove->m_vecVelocity, IsDucked() ? VPHYS_CROUCH : VPHYS_WALK );
  4041. }
  4042. //transform local velocity
  4043. {
  4044. //Vector vTransformedLocalVelocity;
  4045. //VectorRotate( GetAbsVelocity(), pEnteredPortal->m_matrixThisToLinked.As3x4(), vTransformedLocalVelocity );
  4046. //SetAbsVelocity( vTransformedLocalVelocity );
  4047. SetAbsVelocity( pMove->m_vecVelocity );
  4048. }
  4049. //transform base velocity
  4050. {
  4051. Vector vTransformedBaseVelocity;
  4052. VectorRotate( GetBaseVelocity(), pEnteredPortal->m_matrixThisToLinked.As3x4(), vTransformedBaseVelocity );
  4053. SetBaseVelocity( vTransformedBaseVelocity );
  4054. }
  4055. //transform previous position
  4056. {
  4057. UTIL_Portal_PointTransform( pEnteredPortal->m_matrixThisToLinked, m_vPrevPosition, m_vPrevPosition );
  4058. }
  4059. CollisionRulesChanged();
  4060. // straighten out velocity if going nearly straight up/down out of a floor/ceiling portal
  4061. {
  4062. const CPortal_Base2D *pOtherPortal = pEnteredPortal->m_hLinkedPortal.Get();
  4063. if( sv_player_funnel_into_portals.GetBool() && pOtherPortal )
  4064. {
  4065. // Make sure this portal is nearly facing straight up/down
  4066. const Vector vNormal = pOtherPortal->m_PortalSimulator.GetInternalData().Placement.vForward;
  4067. if( (1.f - fabs(vNormal.z)) < 0.001f )
  4068. {
  4069. const Vector vUp(0.f,0.f,1.f);
  4070. const Vector vVel = pMove->m_vecVelocity;
  4071. const float flVelDotUp = DotProduct( vVel.Normalized(), vUp );
  4072. // We're going mostly straight up/down
  4073. if( fabs( flVelDotUp ) > sv_player_funnel_gimme_dot.GetFloat() )
  4074. {
  4075. // Make us go exactly sraight up/down
  4076. pMove->m_vecVelocity = ( DotProduct(vUp, vVel) * vUp );
  4077. }
  4078. }
  4079. }
  4080. }
  4081. PostTeleportationCameraFixup( pEnteredPortal );
  4082. // Use a slightly expanded box to search for stick surfaces as the player leaves the portal.
  4083. m_flUsePostTeleportationBoxTime = sv_post_teleportation_box_time.GetFloat();
  4084. m_nPortalsEnteredInAirFlags |= pEnteredPortal->m_nPortalColor;
  4085. int nAllPortals = ( PORTAL_COLOR_FLAG_BLUE | PORTAL_COLOR_FLAG_PURPLE | PORTAL_COLOR_FLAG_ORANGE | PORTAL_COLOR_FLAG_RED );
  4086. if ( ( m_nPortalsEnteredInAirFlags & nAllPortals ) == nAllPortals )
  4087. {
  4088. UTIL_RecordAchievementEvent( "ACH.FOUR_PORTALS", this );
  4089. }
  4090. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  4091. g_PortalGameStats.Event_PortalTeleport( this );
  4092. #endif
  4093. // Increment portal uses in MP stats
  4094. if( GetPortalMPStats() )
  4095. {
  4096. GetPortalMPStats()->IncrementPlayerPortalsTraveled( this );
  4097. }
  4098. const CUtlVector<ITriggerCatapultAutoList*>& catapults = CTriggerCatapult::AutoList();
  4099. for( int i = 0; i < catapults.Count(); ++i )
  4100. {
  4101. assert_cast<CTriggerCatapult*>( catapults[i] )->EndTouch( this );
  4102. }
  4103. }
  4104. CON_COMMAND( displayportalplayerstats, "Displays current level stats for portals placed, steps taken, and seconds taken." )
  4105. {
  4106. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  4107. if( pPlayer == NULL )
  4108. pPlayer = GetPortalPlayer( 1 ); //last ditch effort
  4109. if( pPlayer )
  4110. {
  4111. int iMinutes = static_cast<int>( pPlayer->NumSecondsTaken() / 60.0f );
  4112. int iSeconds = static_cast<int>( pPlayer->NumSecondsTaken() ) % 60;
  4113. CFmtStr msg;
  4114. NDebugOverlay::ScreenText( 0.5f, 0.5f, msg.sprintf( "Portals Placed: %d\nSteps Taken: %d\nTime: %d:%d", pPlayer->NumPortalsPlaced(), pPlayer->NumStepsTaken(), iMinutes, iSeconds ), 255, 255, 255, 150, 5.0f );
  4115. }
  4116. }
  4117. CON_COMMAND( startneurotoxins, "Starts the nerve gas timer." )
  4118. {
  4119. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  4120. if( pPlayer == NULL )
  4121. pPlayer = GetPortalPlayer( 1 ); //last ditch effort
  4122. float fCoundownTime = 180.0f;
  4123. if ( args.ArgC() > 1 )
  4124. fCoundownTime = atof( args[ 1 ] );
  4125. if( pPlayer )
  4126. pPlayer->SetNeuroToxinDamageTime( fCoundownTime );
  4127. }
  4128. void CPortal_Player::InitialSpawn( void )
  4129. {
  4130. BaseClass::InitialSpawn();
  4131. #if USE_SLOWTIME
  4132. // Reset our slow timers
  4133. m_PortalLocal.m_bSlowingTime = false;
  4134. m_PortalLocal.m_flSlowTimeRemaining = m_PortalLocal.m_flSlowTimeMaximum = slowtime_max.GetFloat();
  4135. #endif // USE_SLOWTIME
  4136. m_bReadyForDLCItemUpdates = false;
  4137. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  4138. UpdateInventory( true );
  4139. #endif
  4140. }
  4141. extern ConVar *sv_cheats;
  4142. void CC_give_me_a_point( void )
  4143. {
  4144. //static ConVarRef sv_cheatsref( "sv_cheats" );
  4145. if( sv_cheats->GetBool() )
  4146. {
  4147. UTIL_GetCommandClient()->IncrementFragCount( 1 );
  4148. }
  4149. }
  4150. static ConCommand give_a_point( "give_me_a_point", CC_give_me_a_point, "Give yourself a point", 0 );
  4151. #if USE_SLOWTIME
  4152. //-----------------------------------------------------------------------------
  4153. // Purpose:
  4154. //-----------------------------------------------------------------------------
  4155. class CLogicPlayerSlowTime : public CLogicalEntity
  4156. {
  4157. public:
  4158. DECLARE_CLASS( CLogicPlayerSlowTime, CLogicalEntity );
  4159. DECLARE_DATADESC();
  4160. private:
  4161. void InputStartSlowTime( inputdata_t &data )
  4162. {
  4163. CPortal_Player *pPlayer = (CPortal_Player *) UTIL_GetLocalPlayer();
  4164. if ( pPlayer )
  4165. {
  4166. pPlayer->StartSlowingTime( data.value.Float() );
  4167. }
  4168. }
  4169. void InputStopSlowTime( inputdata_t &data )
  4170. {
  4171. CPortal_Player *pPlayer = (CPortal_Player *) UTIL_GetLocalPlayer();
  4172. if ( pPlayer )
  4173. {
  4174. pPlayer->StopSlowingTime();
  4175. }
  4176. }
  4177. };
  4178. BEGIN_DATADESC( CLogicPlayerSlowTime )
  4179. DEFINE_INPUTFUNC( FIELD_FLOAT, "StartSlowingTime", InputStartSlowTime ),
  4180. DEFINE_INPUTFUNC( FIELD_VOID, "StopSlowingTime", InputStopSlowTime ),
  4181. END_DATADESC()
  4182. LINK_ENTITY_TO_CLASS( logic_player_slowtime, CLogicPlayerSlowTime );
  4183. #endif // USE_SLOWTIME
  4184. //-----------------------------------------------------------------------------
  4185. // Purpose:
  4186. //-----------------------------------------------------------------------------
  4187. class CLogicPlayerViewFinder : public CPointEntity
  4188. {
  4189. public:
  4190. DECLARE_CLASS( CLogicPlayerViewFinder, CPointEntity );
  4191. DECLARE_DATADESC();
  4192. private:
  4193. void InputShowViewFinder( inputdata_t &data )
  4194. {
  4195. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  4196. {
  4197. CPortal_Player *pPlayer = (CPortal_Player *) UTIL_PlayerByIndex( i );
  4198. if ( pPlayer )
  4199. {
  4200. pPlayer->ShowViewFinder();
  4201. }
  4202. }
  4203. }
  4204. void InputHideViewFinder( inputdata_t &data )
  4205. {
  4206. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  4207. {
  4208. CPortal_Player *pPlayer = (CPortal_Player *) UTIL_PlayerByIndex( i );
  4209. if ( pPlayer )
  4210. {
  4211. pPlayer->HideViewFinder();
  4212. }
  4213. }
  4214. }
  4215. };
  4216. BEGIN_DATADESC( CLogicPlayerViewFinder )
  4217. DEFINE_INPUTFUNC( FIELD_VOID, "ShowViewFinder", InputShowViewFinder ),
  4218. DEFINE_INPUTFUNC( FIELD_VOID, "HideViewFinder", InputHideViewFinder ),
  4219. END_DATADESC()
  4220. LINK_ENTITY_TO_CLASS( env_player_viewfinder, CLogicPlayerViewFinder );
  4221. //-----------------------------------------------------------------------------
  4222. // Purpose:
  4223. //-----------------------------------------------------------------------------
  4224. void CPortal_Player::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
  4225. {
  4226. BaseClass::ModifyOrAppendCriteria( criteriaSet );
  4227. // Determine if we're in the air
  4228. criteriaSet.AppendCriteria( "in_air", ( GetGroundEntity() == NULL || m_PortalLocal.m_hTractorBeam.Get() ) ? 1 : 0 );
  4229. // Determine if we're standing on something special
  4230. CBaseEntity *pGroundEnt = GetGroundEntity();
  4231. if ( pGroundEnt != NULL )
  4232. {
  4233. criteriaSet.AppendCriteria( "ground_entity", pGroundEnt->GetClassname() );
  4234. }
  4235. // Looking at various things
  4236. trace_t tr;
  4237. Vector vecOurForward;
  4238. EyeVectors( &vecOurForward, NULL, NULL );
  4239. UTIL_TraceLine( EyePosition(), EyePosition() + ( vecOurForward * MAX_TRACE_LENGTH ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  4240. if ( tr.DidHitNonWorldEntity() )
  4241. {
  4242. criteriaSet.AppendCriteria( "look_entity", tr.m_pEnt->GetClassname() );
  4243. }
  4244. // We need to find the other player
  4245. CPortal_Player *pPartner = ToPortalPlayer( UTIL_OtherPlayer( this ) );
  4246. // Any criteria after this point require a partner!
  4247. if ( pPartner != NULL )
  4248. {
  4249. Vector vecPartnerForward;
  4250. pPartner->GetVectors( &vecPartnerForward, NULL, NULL );
  4251. vecOurForward.z = 0.0f;
  4252. vecPartnerForward.z = 0.0f;
  4253. float flDot = DotProduct( vecOurForward, vecPartnerForward );
  4254. bool bFacingPartner = ( flDot < -DOT_20DEGREE );
  4255. criteriaSet.AppendCriteria( "facing_partner", ( bFacingPartner ) ? 1 : 0 );
  4256. float flDistToPartner = ( GetAbsOrigin() - pPartner->GetAbsOrigin() ).Length();
  4257. criteriaSet.AppendCriteria( "dist_to_partner", flDistToPartner );
  4258. }
  4259. else
  4260. {
  4261. criteriaSet.AppendCriteria( "dist_to_partner", 0.0f );
  4262. }
  4263. criteriaSet.AppendCriteria( "rps_outcome", PortalMPGameRules() ? PortalMPGameRules()->GetRPSOutcome() : 0 );
  4264. if ( m_szTauntForce[ 0 ] == '\0' )
  4265. {
  4266. criteriaSet.AppendCriteria( "force_taunt", "empty" );
  4267. }
  4268. else
  4269. {
  4270. criteriaSet.AppendCriteria( "force_taunt", m_szTauntForce.Get() );
  4271. }
  4272. criteriaSet.AppendCriteria( "taunt_partner", ( m_nTeamTauntState == TEAM_TAUNT_SUCCESS ) ? 1 : 0 );
  4273. criteriaSet.AppendCriteria( "no_portalgun", ( !Weapon_OwnsThisType( "weapon_portalgun" ) ) ? 1 : 0 );
  4274. // Check for if the player is ballbot or eggbot
  4275. if ( g_pGameRules->IsMultiplayer() )
  4276. {
  4277. switch ( GetTeamNumber() )
  4278. {
  4279. case TEAM_RED:
  4280. criteriaSet.AppendCriteria( "is_eggbot", 1 );
  4281. break;
  4282. case TEAM_BLUE:
  4283. criteriaSet.AppendCriteria( "is_ballbot", 1 );
  4284. break;
  4285. }
  4286. }
  4287. }
  4288. ConVar portal_use_player_avoidance( "portal_use_player_avoidance", "0", FCVAR_REPLICATED );
  4289. //-----------------------------------------------------------------------------
  4290. // Purpose: Don't collide with other players, we'll just push away from them
  4291. //-----------------------------------------------------------------------------
  4292. bool CPortal_Player::ShouldCollide( int collisionGroup, int contentsMask ) const
  4293. {
  4294. // Don't hit other players
  4295. if ( portal_use_player_avoidance.GetBool() && ( ( collisionGroup == COLLISION_GROUP_PLAYER || collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) ) )
  4296. return false;
  4297. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  4298. }
  4299. //-----------------------------------------------------------------------------
  4300. // Purpose: Change a player from blue to orange and vice versa
  4301. //-----------------------------------------------------------------------------
  4302. void PlayerSwitchTeams( void )
  4303. {
  4304. // Find all players and swap them around
  4305. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  4306. {
  4307. CPortal_Player *pPlayer = static_cast<CPortal_Player *>(UTIL_PlayerByIndex( i ));
  4308. if ( pPlayer )
  4309. {
  4310. if ( pPlayer->GetTeamNumber() == TEAM_RED )
  4311. {
  4312. pPlayer->ChangeTeam( TEAM_BLUE );
  4313. }
  4314. else
  4315. {
  4316. pPlayer->ChangeTeam( TEAM_RED );
  4317. }
  4318. }
  4319. }
  4320. }
  4321. // REMOVED FOR PORTAL2: ConCommand switch_teams( "switch_teams", PlayerSwitchTeams, "Change a player from blue to orange and vice versa.", FCVAR_NONE );
  4322. //////////////////////////////////////////////////////////////////////////
  4323. // PAINT SECTION
  4324. //////////////////////////////////////////////////////////////////////////
  4325. // This is a glorious hack to find free space when you've crouched into some solid space
  4326. // Our crouching collisions do not work correctly for some reason and this is easier
  4327. // than fixing the problem :(
  4328. // Note: This is a nasty copy/paste job from player.cpp, which replaces VEC_DUCK_HULL_MIN/MAX
  4329. // with the player's local hulls.
  4330. CEG_NOINLINE void FixPlayerCrouchStuck( CPortal_Player *pPlayer )
  4331. {
  4332. trace_t trace;
  4333. const Vector& duckHullMin = pPlayer->GetDuckHullMins();
  4334. const Vector& duckHullMax = pPlayer->GetDuckHullMaxs();
  4335. // Move up as many as 18 pixels if the player is stuck.
  4336. Vector org = pPlayer->GetAbsOrigin();;
  4337. for( int i = 0; i < 18; i++ )
  4338. {
  4339. UTIL_TraceHull( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(),
  4340. duckHullMin, duckHullMax, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  4341. if ( trace.startsolid )
  4342. {
  4343. Vector origin = pPlayer->GetAbsOrigin();
  4344. origin.z += 1.0f;
  4345. pPlayer->SetLocalOrigin( origin );
  4346. }
  4347. else
  4348. return;
  4349. }
  4350. pPlayer->SetAbsOrigin( org );
  4351. for( int i = 0; i < 18; i++ )
  4352. {
  4353. UTIL_TraceHull( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(),
  4354. duckHullMin, duckHullMax, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  4355. if ( trace.startsolid )
  4356. {
  4357. Vector origin = pPlayer->GetAbsOrigin();
  4358. origin.z -= 1.0f;
  4359. pPlayer->SetLocalOrigin( origin );
  4360. }
  4361. else
  4362. return;
  4363. }
  4364. }
  4365. CEG_PROTECT_FUNCTION( FixPlayerCrouchStuck );
  4366. int CPortal_Player::Restore( IRestore &restore )
  4367. {
  4368. int status = 0;
  4369. if( BaseClass::Restore( restore ) )
  4370. {
  4371. status = 1;
  4372. if( GetFlags() & FL_DUCKING )
  4373. {
  4374. // Use the crouch HACK
  4375. FixPlayerCrouchStuck( this );
  4376. UTIL_SetSize( this, GetDuckHullMins(), GetDuckHullMaxs() );
  4377. m_Local.m_bDucked = true;
  4378. }
  4379. else
  4380. {
  4381. m_Local.m_bDucked = false;
  4382. UTIL_SetSize( this, GetStandHullMins(), GetStandHullMaxs() );
  4383. }
  4384. }
  4385. return status;
  4386. }
  4387. void CPortal_Player::GivePortalPlayerItems( void )
  4388. {
  4389. bool bSpawnWithPaintGun = false;
  4390. bool bSpawnWithPortalGun = false;
  4391. bool bMultiplayer = g_pGameRules->IsMultiplayer();
  4392. bool bHadPaintGunOnDeath = m_PlayerGunTypeWhenDead == PLAYER_PAINT_GUN;
  4393. bool bHadPortalGunOnDeath = m_PlayerGunTypeWhenDead == PLAYER_PORTAL_GUN;
  4394. bool bIs2GunsMap = ( V_stristr( gpGlobals->mapname.ToCStr(), "2guns" ) != NULL ) || ( GlobalEntity_GetState( "paintgun_map" ) == GLOBAL_ON );
  4395. //If this map has 2 guns in it
  4396. if( bMultiplayer && bIs2GunsMap )
  4397. {
  4398. //If the player should spawn with a paint gun in this map
  4399. if( bHadPaintGunOnDeath || //Spawn with paintgun in multiplayer if the player had paintgun on death
  4400. ( !m_bSpawnFromDeath && m_PlayerGunType == PLAYER_PAINT_GUN ) || //Spawn with paintgun if player is not spawning from death and had paintgun
  4401. ( m_PlayerGunType == PLAYER_NO_GUN && GetTeamNumber() != g_iPortalGunPlayerTeam ) ) //Red player gets paintgun if no gun is assigned
  4402. {
  4403. bSpawnWithPaintGun = true;
  4404. }
  4405. //Else If the player should spawn with a portal gun in this map
  4406. else if( bHadPortalGunOnDeath || //Spawn with portalgun in multiplayer if the player had portalgun on death
  4407. ( !m_bSpawnFromDeath && m_PlayerGunType == PLAYER_PORTAL_GUN ) || //Spawn with portalgun if player is not spawning from death and had portalgun
  4408. ( m_PlayerGunType == PLAYER_NO_GUN && GetTeamNumber() == g_iPortalGunPlayerTeam ) ) //Blue player gets portalgun
  4409. {
  4410. bSpawnWithPortalGun = true;
  4411. }
  4412. }
  4413. else
  4414. {
  4415. GiveDefaultItems();
  4416. }
  4417. //Check for the can carry both guns cheat
  4418. if( sv_can_carry_both_guns )
  4419. {
  4420. bSpawnWithPaintGun = true;
  4421. bSpawnWithPortalGun = true;
  4422. }
  4423. if( bSpawnWithPortalGun )
  4424. {
  4425. bool bSwitchToPortalGun = !bIs2GunsMap;
  4426. GivePlayerPortalGun( true, bSwitchToPortalGun );
  4427. }
  4428. if( bSpawnWithPaintGun )
  4429. {
  4430. GivePlayerPaintGun( false, false );
  4431. }
  4432. m_bSpawnFromDeath = false;
  4433. m_PlayerGunTypeWhenDead = PLAYER_NO_GUN;
  4434. }
  4435. void CPortal_Player::GivePlayerPaintGun( bool bActivatePaintPowers, bool bSwitchTo )
  4436. {
  4437. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( "weapon_paintgun", 0 );
  4438. CWeaponPaintGun *pPaintGun = NULL;
  4439. //If the player already has a paint gun
  4440. if( pWeapon )
  4441. {
  4442. pPaintGun = static_cast<CWeaponPaintGun*>( pWeapon );
  4443. }
  4444. else //Give the player a paint gun
  4445. {
  4446. pPaintGun = (CWeaponPaintGun*)CreateEntityByName( "weapon_paintgun" );
  4447. if ( pPaintGun )
  4448. {
  4449. DispatchSpawn( pPaintGun );
  4450. if ( !pPaintGun->IsMarkedForDeletion() )
  4451. {
  4452. Weapon_Equip( pPaintGun );
  4453. }
  4454. }
  4455. }
  4456. //Activate all the paint powers if needed
  4457. if( pPaintGun && bActivatePaintPowers )
  4458. {
  4459. pPaintGun->ActivatePaint(BOUNCE_POWER);
  4460. pPaintGun->ActivatePaint(SPEED_POWER);
  4461. //pPaintGun->ActivatePaint(REFLECT_POWER);
  4462. pPaintGun->ActivatePaint(PORTAL_POWER);
  4463. PaintPowerPickup( BOUNCE_POWER, this );
  4464. PaintPowerPickup( SPEED_POWER, this );
  4465. //PaintPowerPickup( REFLECT_POWER, this );
  4466. PaintPowerPickup( PORTAL_POWER, this );
  4467. }
  4468. //Switch to the paint gun
  4469. if( pPaintGun && bSwitchTo )
  4470. {
  4471. Weapon_Switch( pPaintGun );
  4472. }
  4473. }
  4474. void CPortal_Player::GivePlayerPortalGun( bool bUpgraded, bool bSwitchTo )
  4475. {
  4476. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( "weapon_portalgun", 0 );
  4477. CWeaponPortalgun *pPortalGun = NULL;
  4478. //If the player already has a portal gun
  4479. if( pWeapon )
  4480. {
  4481. pPortalGun = static_cast<CWeaponPortalgun*>( pWeapon );
  4482. }
  4483. else //Give the player a portal gun
  4484. {
  4485. pPortalGun = (CWeaponPortalgun*)CreateEntityByName( "weapon_portalgun" );
  4486. if ( pPortalGun )
  4487. {
  4488. pPortalGun->SetLocalOrigin( GetLocalOrigin() );
  4489. pPortalGun->AddSpawnFlags( SF_NORESPAWN );
  4490. pPortalGun->SetSubType( 0 );
  4491. DispatchSpawn( pPortalGun );
  4492. if ( !pPortalGun->IsMarkedForDeletion() )
  4493. {
  4494. pPortalGun->SetCanFirePortal1();
  4495. //Upgrade the portal gun if needed
  4496. if( bUpgraded )
  4497. {
  4498. pPortalGun->SetCanFirePortal2();
  4499. }
  4500. //pPortalGun->Touch( this );
  4501. Weapon_Equip( pPortalGun );
  4502. }
  4503. }
  4504. }
  4505. //Switch to the portal gun
  4506. if( pPortalGun && bSwitchTo )
  4507. {
  4508. Weapon_Switch( pPortalGun );
  4509. }
  4510. }
  4511. void CPortal_Player::RemovePlayerWearable( const char *pItemName )
  4512. {
  4513. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( pItemName, 0 );
  4514. if ( pWeapon )
  4515. {
  4516. if ( Weapon_Detach(pWeapon) )
  4517. {
  4518. UTIL_Remove( pWeapon );
  4519. }
  4520. }
  4521. }
  4522. void CPortal_Player::GivePlayerWearable( const char *pItemName )
  4523. {
  4524. CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( pItemName, 0 );
  4525. if ( !pWeapon )
  4526. {
  4527. pWeapon = (CBaseCombatWeapon *)CreateEntityByName( pItemName );
  4528. if ( pWeapon )
  4529. {
  4530. pWeapon->SetLocalOrigin( GetLocalOrigin() );
  4531. pWeapon->AddSpawnFlags( SF_NORESPAWN );
  4532. pWeapon->SetSubType( 0 );
  4533. DispatchSpawn( pWeapon );
  4534. if ( !pWeapon->IsMarkedForDeletion() )
  4535. {
  4536. Weapon_Equip( pWeapon );
  4537. }
  4538. }
  4539. }
  4540. if ( pWeapon )
  4541. {
  4542. // DON'T SWITCH TO THIS! We still draw it even when it's not the active weapon
  4543. //Weapon_Switch( pWeapon );
  4544. pWeapon->SetWeaponVisible( true );
  4545. }
  4546. }
  4547. void CPortal_Player::Weapon_Equip( CBaseCombatWeapon *pWeapon )
  4548. {
  4549. if( pWeapon && FClassnameIs( pWeapon, "weapon_paintgun" ) )
  4550. {
  4551. m_PlayerGunType = PLAYER_PAINT_GUN;
  4552. }
  4553. else if( pWeapon && FClassnameIs( pWeapon, "weapon_portalgun" ) )
  4554. {
  4555. m_PlayerGunType = PLAYER_PORTAL_GUN;
  4556. }
  4557. //If the player is equipping the paint gun
  4558. //CWeaponPaintGun *pPaintGun = dynamic_cast<CWeaponPaintGun*>( pWeapon );
  4559. //if( pPaintGun )
  4560. //{
  4561. // IGameEvent *event = gameeventmanager->CreateEvent( "equipped_paintgun" );
  4562. // if ( event )
  4563. // {
  4564. // event->SetInt("userid", GetUserID() );
  4565. // gameeventmanager->FireEvent( event );
  4566. // }
  4567. // m_PlayerGunType = PLAYER_PAINT_GUN;
  4568. //}
  4569. //// set portals owner here because picking up non trigger weapon doesn't call BumpWeapon
  4570. //CWeaponPortalgun *pPortalGun = dynamic_cast< CWeaponPortalgun* >( pWeapon );
  4571. ////store old linkageID
  4572. //unsigned int linkageID = 0;
  4573. //if ( pPortalGun )
  4574. //{
  4575. // linkageID = pPortalGun->m_iPortalLinkageGroupID;
  4576. // m_PlayerGunType = PLAYER_PORTAL_GUN;
  4577. //}
  4578. BaseClass::Weapon_Equip( pWeapon );
  4579. //if ( pPortalGun )
  4580. //{
  4581. // //reset the linkage ID because Weapon_Equip calls Deploy which changes the id
  4582. // pPortalGun->m_iPortalLinkageGroupID = linkageID;
  4583. // //Set the existing portals to have been fired by the new player holding the portal gun
  4584. // CProp_Portal *pPortal1 = CProp_Portal::FindPortal( linkageID, false );
  4585. // if( pPortal1 )
  4586. // {
  4587. // pPortal1->SetFiredByPlayer( this );
  4588. // }
  4589. // CProp_Portal *pPortal2 = CProp_Portal::FindPortal( linkageID, true );
  4590. // if( pPortal2 )
  4591. // {
  4592. // pPortal2->SetFiredByPlayer( this );
  4593. // }
  4594. //}
  4595. }
  4596. void CPortal_Player::SetWantsToSwapGuns( bool bWantsToSwap )
  4597. {
  4598. m_bWantsToSwapGuns = bWantsToSwap;
  4599. m_bSendSwapProximityFailEvent = true;
  4600. }
  4601. void CPortal_Player::SwapThink()
  4602. {
  4603. bool bIsMultiplayer = gpGlobals->maxClients > 1;
  4604. //Check if this player wants to swap guns
  4605. if( m_afButtonPressed & IN_ALT1 )
  4606. {
  4607. CBaseCombatWeapon *pPaintGun = Weapon_OwnsThisType( "weapon_paintgun" );
  4608. CBaseCombatWeapon *pPortalGun = Weapon_OwnsThisType( "weapon_portalgun" );
  4609. bool bHasBothGuns = !!pPaintGun && !!pPortalGun;
  4610. if( ( !bIsMultiplayer && bHasBothGuns ) || sv_can_carry_both_guns )
  4611. {
  4612. //engine->ClientCommand( edict(), "lastinv" );
  4613. if ( pPaintGun == GetActiveWeapon() )
  4614. {
  4615. Weapon_Switch( pPortalGun );
  4616. }
  4617. else
  4618. {
  4619. Weapon_Switch( pPaintGun );
  4620. }
  4621. }
  4622. else
  4623. {
  4624. if( bIsMultiplayer && sv_can_swap_guns_anytime )
  4625. {
  4626. IGameEvent *event = gameeventmanager->CreateEvent( "wants_to_swap_guns" );
  4627. if ( event )
  4628. {
  4629. event->SetInt( "userid", GetUserID() );
  4630. gameeventmanager->FireEvent( event );
  4631. }
  4632. SetWantsToSwapGuns( true );
  4633. }
  4634. }
  4635. }
  4636. else if( m_afButtonReleased & IN_ALT1 && bIsMultiplayer && !sv_can_carry_both_guns && sv_can_swap_guns_anytime )
  4637. {
  4638. IGameEvent *event = gameeventmanager->CreateEvent( "doesnt_want_to_swap_guns" );
  4639. if ( event )
  4640. {
  4641. event->SetInt( "userid", GetUserID() );
  4642. gameeventmanager->FireEvent( event );
  4643. }
  4644. SetWantsToSwapGuns( false );
  4645. }
  4646. bool bSwap = false;
  4647. if( WantsToSwapGuns() && sv_can_swap_guns )
  4648. {
  4649. CPortal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherConnectedPlayer( this ) );
  4650. if( pOtherPlayer && pOtherPlayer->WantsToSwapGuns() )
  4651. {
  4652. if( sv_can_swap_guns_anytime )
  4653. {
  4654. //Check if the players are close enough to swap
  4655. bSwap = CheckSwapProximity( this, pOtherPlayer );
  4656. }
  4657. else
  4658. {
  4659. bSwap = true;
  4660. }
  4661. if( bSwap )
  4662. {
  4663. //Reset the wants to swap flag for both players
  4664. SetWantsToSwapGuns( false );
  4665. pOtherPlayer->SetWantsToSwapGuns( false );
  4666. SwapPaintAndPortalGuns( this, pOtherPlayer );
  4667. //Send the event that the players swapped guns
  4668. IGameEvent *event = gameeventmanager->CreateEvent( "swapped_guns" );
  4669. if ( event )
  4670. {
  4671. gameeventmanager->FireEvent( event );
  4672. }
  4673. }
  4674. else
  4675. {
  4676. //Check if the proximity check failed event should be send
  4677. //We only want to send this event the first time the proximity check fails
  4678. if( m_bSendSwapProximityFailEvent )
  4679. {
  4680. m_bSendSwapProximityFailEvent = false;
  4681. IGameEvent *event = gameeventmanager->CreateEvent( "swap_guns_proximity_fail" );
  4682. if( event )
  4683. {
  4684. gameeventmanager->FireEvent( event );
  4685. }
  4686. }
  4687. }
  4688. } //If other player wants to swap
  4689. } //If this player wants to swap
  4690. }
  4691. Vector CPortal_Player::BodyTarget( const Vector &posSrc, bool bNoisy )
  4692. {
  4693. if (bNoisy)
  4694. {
  4695. return WorldSpaceCenter() /*+ (GetViewOffset() * random->RandomFloat( 0.7, 1.0 ))*/;
  4696. }
  4697. else
  4698. {
  4699. return WorldSpaceCenter();
  4700. }
  4701. }
  4702. void CPortal_Player::SetFogController( CFogController *pFogController )
  4703. {
  4704. BaseClass::SetFogController( pFogController );
  4705. // In portal multiplayer we need to for the master to be whatever the player was last set to
  4706. // so when they respawn they still obey the master
  4707. if ( GameRules() && GameRules()->IsMultiplayer() )
  4708. {
  4709. FogSystem()->SetMasterController( pFogController );
  4710. }
  4711. }
  4712. //-----------------------------------------------------------------------------
  4713. // Purpose:
  4714. //-----------------------------------------------------------------------------
  4715. bool CPortal_Player::PlayGesture( const char *pGestureName )
  4716. {
  4717. Activity nActivity = (Activity)LookupActivity( pGestureName );
  4718. if ( nActivity != ACT_INVALID )
  4719. {
  4720. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, nActivity );
  4721. return true;
  4722. }
  4723. int nSequence = LookupSequence( pGestureName );
  4724. if ( nSequence != -1 )
  4725. {
  4726. DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE, nSequence );
  4727. return true;
  4728. }
  4729. return false;
  4730. }
  4731. void CPortal_Player::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
  4732. {
  4733. // Cleanup any old vphysics stuff.
  4734. VPhysicsDestroyObject();
  4735. // in turbo physics players dont have a physics shadow
  4736. if ( sv_turbophysics.GetBool() )
  4737. return;
  4738. CPhysCollide *pModel = PhysCreateBbox( GetStandHullMins(), GetStandHullMaxs() );
  4739. CPhysCollide *pCrouchModel = PhysCreateBbox( GetDuckHullMins(), GetDuckHullMaxs() );
  4740. SetupVPhysicsShadow( vecAbsOrigin, vecAbsVelocity, pModel, "player_stand", pCrouchModel, "player_crouch" );
  4741. }
  4742. void CPortal_Player::OnPlayerLanded()
  4743. {
  4744. // make sure the player don't land on the floor at the bottom of the goo
  4745. if ( GetWaterLevel() == WL_NotInWater )
  4746. {
  4747. m_bWasDroppedByOtherPlayerWhileTaunting = false;
  4748. }
  4749. }
  4750. void CPortal_Player::NetworkPortalTeleportation( CBaseEntity *pOther, CPortal_Base2D *pPortal, float fTime, bool bForcedDuck )
  4751. {
  4752. CEntityPortalledNetworkMessage &writeTo = m_EntityPortalledNetworkMessages[m_iEntityPortalledNetworkMessageCount % MAX_ENTITY_PORTALLED_NETWORK_MESSAGES];
  4753. writeTo.m_hEntity = pOther;
  4754. writeTo.m_hPortal = pPortal;
  4755. writeTo.m_fTime = fTime;
  4756. writeTo.m_bForcedDuck = bForcedDuck;
  4757. writeTo.m_iMessageCount = m_iEntityPortalledNetworkMessageCount;
  4758. ++m_iEntityPortalledNetworkMessageCount;
  4759. //NetworkProp()->NetworkStateChanged( offsetof( CPortal_Player, m_EntityPortalledNetworkMessages ) );
  4760. NetworkProp()->NetworkStateChanged();
  4761. }
  4762. void cc_can_carry_both_guns( const CCommand &args )
  4763. {
  4764. //sv_can_carry_both_guns.SetValue( args[1] );
  4765. //if ( sv_can_carry_both_guns.GetBool() )
  4766. {
  4767. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  4768. {
  4769. CPortal_Player *pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
  4770. if ( pPlayer )
  4771. {
  4772. pPlayer->GivePlayerPaintGun( true, false );
  4773. pPlayer->GivePlayerPortalGun( true, true );
  4774. }
  4775. }
  4776. }
  4777. }
  4778. // FIXME: Bring this back for DLC2
  4779. //ConCommand can_carry_both_guns( "can_carry_both_guns", cc_can_carry_both_guns );