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

982 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "npcevent.h"
  8. #include "basehlcombatweapon_shared.h"
  9. #include "basecombatcharacter.h"
  10. #include "ai_basenpc.h"
  11. #include "player.h"
  12. #include "gamerules.h"
  13. #include "in_buttons.h"
  14. #include "soundent.h"
  15. #include "game.h"
  16. #include "vstdlib/random.h"
  17. #include "engine/IEngineSound.h"
  18. #include "IEffects.h"
  19. #include "te_effect_dispatch.h"
  20. #include "Sprite.h"
  21. #include "SpriteTrail.h"
  22. #include "beam_shared.h"
  23. #include "rumble_shared.h"
  24. #include "gamestats.h"
  25. #include "decals.h"
  26. #ifdef PORTAL
  27. #include "portal_util_shared.h"
  28. #endif
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. //#define BOLT_MODEL "models/crossbow_bolt.mdl"
  32. #define BOLT_MODEL "models/weapons/w_missile_closed.mdl"
  33. #define BOLT_AIR_VELOCITY 2500
  34. #define BOLT_WATER_VELOCITY 1500
  35. extern ConVar sk_plr_dmg_crossbow;
  36. extern ConVar sk_npc_dmg_crossbow;
  37. void TE_StickyBolt( IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin );
  38. #define BOLT_SKIN_NORMAL 0
  39. #define BOLT_SKIN_GLOW 1
  40. //-----------------------------------------------------------------------------
  41. // Crossbow Bolt
  42. //-----------------------------------------------------------------------------
  43. class CCrossbowBolt : public CBaseCombatCharacter
  44. {
  45. DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter );
  46. public:
  47. CCrossbowBolt() { };
  48. ~CCrossbowBolt();
  49. Class_T Classify( void ) { return CLASS_NONE; }
  50. public:
  51. void Spawn( void );
  52. void Precache( void );
  53. void BubbleThink( void );
  54. void BoltTouch( CBaseEntity *pOther );
  55. bool CreateVPhysics( void );
  56. unsigned int PhysicsSolidMaskForEntity() const;
  57. static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL );
  58. protected:
  59. bool CreateSprites( void );
  60. CHandle<CSprite> m_pGlowSprite;
  61. //CHandle<CSpriteTrail> m_pGlowTrail;
  62. DECLARE_DATADESC();
  63. DECLARE_SERVERCLASS();
  64. };
  65. LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );
  66. BEGIN_DATADESC( CCrossbowBolt )
  67. // Function Pointers
  68. DEFINE_FUNCTION( BubbleThink ),
  69. DEFINE_FUNCTION( BoltTouch ),
  70. // These are recreated on reload, they don't need storage
  71. DEFINE_FIELD( m_pGlowSprite, FIELD_EHANDLE ),
  72. //DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
  73. END_DATADESC()
  74. IMPLEMENT_SERVERCLASS_ST( CCrossbowBolt, DT_CrossbowBolt )
  75. END_SEND_TABLE()
  76. CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner )
  77. {
  78. // Create a new entity with CCrossbowBolt private data
  79. CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" );
  80. UTIL_SetOrigin( pBolt, vecOrigin );
  81. pBolt->SetAbsAngles( angAngles );
  82. pBolt->Spawn();
  83. pBolt->SetOwnerEntity( pentOwner );
  84. return pBolt;
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. CCrossbowBolt::~CCrossbowBolt( void )
  90. {
  91. if ( m_pGlowSprite )
  92. {
  93. UTIL_Remove( m_pGlowSprite );
  94. }
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. // Output : Returns true on success, false on failure.
  99. //-----------------------------------------------------------------------------
  100. bool CCrossbowBolt::CreateVPhysics( void )
  101. {
  102. // Create the object in the physics system
  103. VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
  104. return true;
  105. }
  106. //-----------------------------------------------------------------------------
  107. //-----------------------------------------------------------------------------
  108. unsigned int CCrossbowBolt::PhysicsSolidMaskForEntity() const
  109. {
  110. return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose:
  114. // Output : Returns true on success, false on failure.
  115. //-----------------------------------------------------------------------------
  116. bool CCrossbowBolt::CreateSprites( void )
  117. {
  118. // Start up the eye glow
  119. m_pGlowSprite = CSprite::SpriteCreate( "sprites/light_glow02_noz.vmt", GetLocalOrigin(), false );
  120. if ( m_pGlowSprite != NULL )
  121. {
  122. m_pGlowSprite->FollowEntity( this );
  123. m_pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation );
  124. m_pGlowSprite->SetScale( 0.2f );
  125. m_pGlowSprite->TurnOff();
  126. }
  127. return true;
  128. }
  129. //-----------------------------------------------------------------------------
  130. // Purpose:
  131. //-----------------------------------------------------------------------------
  132. void CCrossbowBolt::Spawn( void )
  133. {
  134. Precache( );
  135. SetModel( "models/crossbow_bolt.mdl" );
  136. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
  137. UTIL_SetSize( this, -Vector(0.3f,0.3f,0.3f), Vector(0.3f,0.3f,0.3f) );
  138. SetSolid( SOLID_BBOX );
  139. SetGravity( 0.05f );
  140. // Make sure we're updated if we're underwater
  141. UpdateWaterState();
  142. SetTouch( &CCrossbowBolt::BoltTouch );
  143. SetThink( &CCrossbowBolt::BubbleThink );
  144. SetNextThink( gpGlobals->curtime + 0.1f );
  145. CreateSprites();
  146. // Make us glow until we've hit the wall
  147. m_nSkin = BOLT_SKIN_GLOW;
  148. }
  149. void CCrossbowBolt::Precache( void )
  150. {
  151. PrecacheModel( BOLT_MODEL );
  152. // This is used by C_TEStickyBolt, despte being different from above!!!
  153. PrecacheModel( "models/crossbow_bolt.mdl" );
  154. PrecacheModel( "sprites/light_glow02_noz.vmt" );
  155. }
  156. //-----------------------------------------------------------------------------
  157. //-----------------------------------------------------------------------------
  158. void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
  159. {
  160. if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) )
  161. {
  162. // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
  163. if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
  164. return;
  165. }
  166. if ( pOther->m_takedamage != DAMAGE_NO )
  167. {
  168. trace_t tr, tr2;
  169. tr = BaseClass::GetTouchTrace();
  170. Vector vecNormalizedVel = GetAbsVelocity();
  171. ClearMultiDamage();
  172. VectorNormalize( vecNormalizedVel );
  173. #if defined(HL2_EPISODIC)
  174. //!!!HACKHACK - specific hack for ep2_outland_10 to allow crossbow bolts to pass through her bounding box when she's crouched in front of the player
  175. // (the player thinks they have clear line of sight because Alyx is crouching, but her BBOx is still full-height and blocks crossbow bolts.
  176. if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->Classify() == CLASS_PLAYER_ALLY_VITAL && FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") )
  177. {
  178. // Change the owner to stop further collisions with Alyx. We do this by making her the owner.
  179. // The player won't get credit for this kill but at least the bolt won't magically disappear!
  180. SetOwnerEntity( pOther );
  181. return;
  182. }
  183. #endif//HL2_EPISODIC
  184. if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() )
  185. {
  186. CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_NEVERGIB );
  187. dmgInfo.AdjustPlayerDamageInflictedForSkillLevel();
  188. CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
  189. dmgInfo.SetDamagePosition( tr.endpos );
  190. pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
  191. CBasePlayer *pPlayer = ToBasePlayer( GetOwnerEntity() );
  192. if ( pPlayer )
  193. {
  194. gamestats->Event_WeaponHit( pPlayer, true, "weapon_crossbow", dmgInfo );
  195. }
  196. }
  197. else
  198. {
  199. CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_BULLET | DMG_NEVERGIB );
  200. CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
  201. dmgInfo.SetDamagePosition( tr.endpos );
  202. pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
  203. }
  204. ApplyMultiDamage();
  205. //Adrian: keep going through the glass.
  206. if ( pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS )
  207. return;
  208. if ( !pOther->IsAlive() )
  209. {
  210. // We killed it!
  211. const surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps );
  212. if ( pdata->game.material == CHAR_TEX_GLASS )
  213. {
  214. return;
  215. }
  216. }
  217. SetAbsVelocity( Vector( 0, 0, 0 ) );
  218. // play body "thwack" sound
  219. EmitSound( "Weapon_Crossbow.BoltHitBody" );
  220. Vector vForward;
  221. AngleVectors( GetAbsAngles(), &vForward );
  222. VectorNormalize ( vForward );
  223. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_BLOCKLOS, pOther, COLLISION_GROUP_NONE, &tr2 );
  224. if ( tr2.fraction != 1.0f )
  225. {
  226. // NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
  227. // NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
  228. if ( tr2.m_pEnt == NULL || ( tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE ) )
  229. {
  230. CEffectData data;
  231. data.m_vOrigin = tr2.endpos;
  232. data.m_vNormal = vForward;
  233. data.m_nEntIndex = tr2.fraction != 1.0f;
  234. DispatchEffect( "BoltImpact", data );
  235. }
  236. }
  237. SetTouch( NULL );
  238. SetThink( NULL );
  239. if ( !g_pGameRules->IsMultiplayer() )
  240. {
  241. UTIL_Remove( this );
  242. }
  243. }
  244. else
  245. {
  246. trace_t tr;
  247. tr = BaseClass::GetTouchTrace();
  248. // See if we struck the world
  249. if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
  250. {
  251. EmitSound( "Weapon_Crossbow.BoltHitWorld" );
  252. // if what we hit is static architecture, can stay around for a while.
  253. Vector vecDir = GetAbsVelocity();
  254. float speed = VectorNormalize( vecDir );
  255. // See if we should reflect off this surface
  256. float hitDot = DotProduct( tr.plane.normal, -vecDir );
  257. if ( ( hitDot < 0.5f ) && ( speed > 100 ) )
  258. {
  259. Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir;
  260. QAngle reflectAngles;
  261. VectorAngles( vReflection, reflectAngles );
  262. SetLocalAngles( reflectAngles );
  263. SetAbsVelocity( vReflection * speed * 0.75f );
  264. // Start to sink faster
  265. SetGravity( 1.0f );
  266. }
  267. else
  268. {
  269. SetThink( &CCrossbowBolt::SUB_Remove );
  270. SetNextThink( gpGlobals->curtime + 2.0f );
  271. //FIXME: We actually want to stick (with hierarchy) to what we've hit
  272. SetMoveType( MOVETYPE_NONE );
  273. Vector vForward;
  274. AngleVectors( GetAbsAngles(), &vForward );
  275. VectorNormalize ( vForward );
  276. CEffectData data;
  277. data.m_vOrigin = tr.endpos;
  278. data.m_vNormal = vForward;
  279. data.m_nEntIndex = 0;
  280. DispatchEffect( "BoltImpact", data );
  281. UTIL_ImpactTrace( &tr, DMG_BULLET );
  282. AddEffects( EF_NODRAW );
  283. SetTouch( NULL );
  284. SetThink( &CCrossbowBolt::SUB_Remove );
  285. SetNextThink( gpGlobals->curtime + 2.0f );
  286. if ( m_pGlowSprite != NULL )
  287. {
  288. m_pGlowSprite->TurnOn();
  289. m_pGlowSprite->FadeAndDie( 3.0f );
  290. }
  291. }
  292. // Shoot some sparks
  293. if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER)
  294. {
  295. g_pEffects->Sparks( GetAbsOrigin() );
  296. }
  297. }
  298. else
  299. {
  300. // Put a mark unless we've hit the sky
  301. if ( ( tr.surface.flags & SURF_SKY ) == false )
  302. {
  303. UTIL_ImpactTrace( &tr, DMG_BULLET );
  304. }
  305. UTIL_Remove( this );
  306. }
  307. }
  308. if ( g_pGameRules->IsMultiplayer() )
  309. {
  310. // SetThink( &CCrossbowBolt::ExplodeThink );
  311. // SetNextThink( gpGlobals->curtime + 0.1f );
  312. }
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. void CCrossbowBolt::BubbleThink( void )
  318. {
  319. QAngle angNewAngles;
  320. VectorAngles( GetAbsVelocity(), angNewAngles );
  321. SetAbsAngles( angNewAngles );
  322. SetNextThink( gpGlobals->curtime + 0.1f );
  323. // Make danger sounds out in front of me, to scare snipers back into their hole
  324. CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, GetAbsOrigin() + GetAbsVelocity() * 0.2, 120.0f, 0.5f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  325. if ( GetWaterLevel() == 0 )
  326. return;
  327. UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1f, GetAbsOrigin(), 5 );
  328. }
  329. //-----------------------------------------------------------------------------
  330. // CWeaponCrossbow
  331. //-----------------------------------------------------------------------------
  332. class CWeaponCrossbow : public CBaseHLCombatWeapon
  333. {
  334. DECLARE_CLASS( CWeaponCrossbow, CBaseHLCombatWeapon );
  335. public:
  336. CWeaponCrossbow( void );
  337. virtual void Precache( void );
  338. virtual void PrimaryAttack( void );
  339. virtual void SecondaryAttack( void );
  340. virtual bool Deploy( void );
  341. virtual void Drop( const Vector &vecVelocity );
  342. virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  343. virtual bool Reload( void );
  344. virtual void ItemPostFrame( void );
  345. virtual void ItemBusyFrame( void );
  346. virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  347. virtual bool SendWeaponAnim( int iActivity );
  348. virtual bool IsWeaponZoomed() { return m_bInZoom; }
  349. bool ShouldDisplayHUDHint() { return true; }
  350. DECLARE_SERVERCLASS();
  351. DECLARE_DATADESC();
  352. private:
  353. void StopEffects( void );
  354. void SetSkin( int skinNum );
  355. void CheckZoomToggle( void );
  356. void FireBolt( void );
  357. void ToggleZoom( void );
  358. // Various states for the crossbow's charger
  359. enum ChargerState_t
  360. {
  361. CHARGER_STATE_START_LOAD,
  362. CHARGER_STATE_START_CHARGE,
  363. CHARGER_STATE_READY,
  364. CHARGER_STATE_DISCHARGE,
  365. CHARGER_STATE_OFF,
  366. };
  367. void CreateChargerEffects( void );
  368. void SetChargerState( ChargerState_t state );
  369. void DoLoadEffect( void );
  370. private:
  371. // Charger effects
  372. ChargerState_t m_nChargeState;
  373. CHandle<CSprite> m_hChargerSprite;
  374. bool m_bInZoom;
  375. bool m_bMustReload;
  376. };
  377. LINK_ENTITY_TO_CLASS( weapon_crossbow, CWeaponCrossbow );
  378. PRECACHE_WEAPON_REGISTER( weapon_crossbow );
  379. IMPLEMENT_SERVERCLASS_ST( CWeaponCrossbow, DT_WeaponCrossbow )
  380. END_SEND_TABLE()
  381. BEGIN_DATADESC( CWeaponCrossbow )
  382. DEFINE_FIELD( m_bInZoom, FIELD_BOOLEAN ),
  383. DEFINE_FIELD( m_bMustReload, FIELD_BOOLEAN ),
  384. DEFINE_FIELD( m_nChargeState, FIELD_INTEGER ),
  385. DEFINE_FIELD( m_hChargerSprite, FIELD_EHANDLE ),
  386. END_DATADESC()
  387. //-----------------------------------------------------------------------------
  388. // Purpose: Constructor
  389. //-----------------------------------------------------------------------------
  390. CWeaponCrossbow::CWeaponCrossbow( void )
  391. {
  392. m_bReloadsSingly = true;
  393. m_bFiresUnderwater = true;
  394. m_bAltFiresUnderwater = true;
  395. m_bInZoom = false;
  396. m_bMustReload = false;
  397. }
  398. #define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt"
  399. #define CROSSBOW_GLOW_SPRITE2 "sprites/blueflare1.vmt"
  400. //-----------------------------------------------------------------------------
  401. // Purpose:
  402. //-----------------------------------------------------------------------------
  403. void CWeaponCrossbow::Precache( void )
  404. {
  405. UTIL_PrecacheOther( "crossbow_bolt" );
  406. PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
  407. PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
  408. PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );
  409. PrecacheModel( CROSSBOW_GLOW_SPRITE );
  410. PrecacheModel( CROSSBOW_GLOW_SPRITE2 );
  411. BaseClass::Precache();
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose:
  415. //-----------------------------------------------------------------------------
  416. void CWeaponCrossbow::PrimaryAttack( void )
  417. {
  418. if ( m_bInZoom && g_pGameRules->IsMultiplayer() )
  419. {
  420. // FireSniperBolt();
  421. FireBolt();
  422. }
  423. else
  424. {
  425. FireBolt();
  426. }
  427. // Signal a reload
  428. m_bMustReload = true;
  429. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
  430. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  431. if ( pPlayer )
  432. {
  433. m_iPrimaryAttacks++;
  434. gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose:
  439. //-----------------------------------------------------------------------------
  440. void CWeaponCrossbow::SecondaryAttack( void )
  441. {
  442. //NOTENOTE: The zooming is handled by the post/busy frames
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. // Output : Returns true on success, false on failure.
  447. //-----------------------------------------------------------------------------
  448. bool CWeaponCrossbow::Reload( void )
  449. {
  450. if ( BaseClass::Reload() )
  451. {
  452. m_bMustReload = false;
  453. return true;
  454. }
  455. return false;
  456. }
  457. //-----------------------------------------------------------------------------
  458. // Purpose:
  459. //-----------------------------------------------------------------------------
  460. void CWeaponCrossbow::CheckZoomToggle( void )
  461. {
  462. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  463. if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
  464. {
  465. ToggleZoom();
  466. }
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose:
  470. //-----------------------------------------------------------------------------
  471. void CWeaponCrossbow::ItemBusyFrame( void )
  472. {
  473. // Allow zoom toggling even when we're reloading
  474. CheckZoomToggle();
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose:
  478. //-----------------------------------------------------------------------------
  479. void CWeaponCrossbow::ItemPostFrame( void )
  480. {
  481. // Allow zoom toggling
  482. CheckZoomToggle();
  483. if ( m_bMustReload && HasWeaponIdleTimeElapsed() )
  484. {
  485. Reload();
  486. }
  487. BaseClass::ItemPostFrame();
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose:
  491. //-----------------------------------------------------------------------------
  492. void CWeaponCrossbow::FireBolt( void )
  493. {
  494. if ( m_iClip1 <= 0 )
  495. {
  496. if ( !m_bFireOnEmpty )
  497. {
  498. Reload();
  499. }
  500. else
  501. {
  502. WeaponSound( EMPTY );
  503. m_flNextPrimaryAttack = 0.15;
  504. }
  505. return;
  506. }
  507. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  508. if ( pOwner == NULL )
  509. return;
  510. pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART );
  511. Vector vecAiming = pOwner->GetAutoaimVector( 0 );
  512. Vector vecSrc = pOwner->Weapon_ShootPosition();
  513. QAngle angAiming;
  514. VectorAngles( vecAiming, angAiming );
  515. #if defined(HL2_EPISODIC)
  516. // !!!HACK - the other piece of the Alyx crossbow bolt hack for Outland_10 (see ::BoltTouch() for more detail)
  517. if( FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") )
  518. {
  519. trace_t tr;
  520. UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 24.0f, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
  521. if( tr.m_pEnt != NULL && tr.m_pEnt->Classify() == CLASS_PLAYER_ALLY_VITAL )
  522. {
  523. // If Alyx is right in front of the player, make sure the bolt starts outside of the player's BBOX, or the bolt
  524. // will instantly collide with the player after the owner of the bolt is switched to Alyx in ::BoltTouch(). We
  525. // avoid this altogether by making it impossible for the bolt to collide with the player.
  526. vecSrc += vecAiming * 24.0f;
  527. }
  528. }
  529. #endif
  530. CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, pOwner );
  531. if ( pOwner->GetWaterLevel() == 3 )
  532. {
  533. pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
  534. }
  535. else
  536. {
  537. pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
  538. }
  539. m_iClip1--;
  540. pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
  541. WeaponSound( SINGLE );
  542. WeaponSound( SPECIAL2 );
  543. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 );
  544. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  545. if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  546. {
  547. // HEV suit - indicate out of ammo condition
  548. pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
  549. }
  550. m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
  551. DoLoadEffect();
  552. SetChargerState( CHARGER_STATE_DISCHARGE );
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose:
  556. // Output : Returns true on success, false on failure.
  557. //-----------------------------------------------------------------------------
  558. bool CWeaponCrossbow::Deploy( void )
  559. {
  560. if ( m_iClip1 <= 0 )
  561. {
  562. return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
  563. }
  564. SetSkin( BOLT_SKIN_GLOW );
  565. return BaseClass::Deploy();
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose:
  569. // Input : *pSwitchingTo -
  570. // Output : Returns true on success, false on failure.
  571. //-----------------------------------------------------------------------------
  572. bool CWeaponCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
  573. {
  574. StopEffects();
  575. return BaseClass::Holster( pSwitchingTo );
  576. }
  577. //-----------------------------------------------------------------------------
  578. // Purpose:
  579. //-----------------------------------------------------------------------------
  580. void CWeaponCrossbow::ToggleZoom( void )
  581. {
  582. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  583. if ( pPlayer == NULL )
  584. return;
  585. if ( m_bInZoom )
  586. {
  587. if ( pPlayer->SetFOV( this, 0, 0.2f ) )
  588. {
  589. m_bInZoom = false;
  590. }
  591. }
  592. else
  593. {
  594. if ( pPlayer->SetFOV( this, 20, 0.1f ) )
  595. {
  596. m_bInZoom = true;
  597. }
  598. }
  599. }
  600. #define BOLT_TIP_ATTACHMENT 2
  601. //-----------------------------------------------------------------------------
  602. // Purpose:
  603. //-----------------------------------------------------------------------------
  604. void CWeaponCrossbow::CreateChargerEffects( void )
  605. {
  606. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  607. if ( m_hChargerSprite != NULL )
  608. return;
  609. m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false );
  610. if ( m_hChargerSprite )
  611. {
  612. m_hChargerSprite->SetAttachment( pOwner->GetViewModel(), BOLT_TIP_ATTACHMENT );
  613. m_hChargerSprite->SetTransparency( kRenderTransAdd, 255, 128, 0, 255, kRenderFxNoDissipation );
  614. m_hChargerSprite->SetBrightness( 0 );
  615. m_hChargerSprite->SetScale( 0.1f );
  616. m_hChargerSprite->TurnOff();
  617. }
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Purpose:
  621. // Input : skinNum -
  622. //-----------------------------------------------------------------------------
  623. void CWeaponCrossbow::SetSkin( int skinNum )
  624. {
  625. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  626. if ( pOwner == NULL )
  627. return;
  628. CBaseViewModel *pViewModel = pOwner->GetViewModel();
  629. if ( pViewModel == NULL )
  630. return;
  631. pViewModel->m_nSkin = skinNum;
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. void CWeaponCrossbow::DoLoadEffect( void )
  637. {
  638. SetSkin( BOLT_SKIN_GLOW );
  639. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  640. if ( pOwner == NULL )
  641. return;
  642. CBaseViewModel *pViewModel = pOwner->GetViewModel();
  643. if ( pViewModel == NULL )
  644. return;
  645. CEffectData data;
  646. data.m_nEntIndex = pViewModel->entindex();
  647. data.m_nAttachmentIndex = 1;
  648. DispatchEffect( "CrossbowLoad", data );
  649. CSprite *pBlast = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE2, GetAbsOrigin(), false );
  650. if ( pBlast )
  651. {
  652. pBlast->SetAttachment( pOwner->GetViewModel(), 1 );
  653. pBlast->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  654. pBlast->SetBrightness( 128 );
  655. pBlast->SetScale( 0.2f );
  656. pBlast->FadeOutFromSpawn();
  657. }
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. // Input : state -
  662. //-----------------------------------------------------------------------------
  663. void CWeaponCrossbow::SetChargerState( ChargerState_t state )
  664. {
  665. // Make sure we're setup
  666. CreateChargerEffects();
  667. // Don't do this twice
  668. if ( state == m_nChargeState )
  669. return;
  670. m_nChargeState = state;
  671. switch( m_nChargeState )
  672. {
  673. case CHARGER_STATE_START_LOAD:
  674. WeaponSound( SPECIAL1 );
  675. // Shoot some sparks and draw a beam between the two outer points
  676. DoLoadEffect();
  677. break;
  678. case CHARGER_STATE_START_CHARGE:
  679. {
  680. if ( m_hChargerSprite == NULL )
  681. break;
  682. m_hChargerSprite->SetBrightness( 32, 0.5f );
  683. m_hChargerSprite->SetScale( 0.025f, 0.5f );
  684. m_hChargerSprite->TurnOn();
  685. }
  686. break;
  687. case CHARGER_STATE_READY:
  688. {
  689. // Get fully charged
  690. if ( m_hChargerSprite == NULL )
  691. break;
  692. m_hChargerSprite->SetBrightness( 80, 1.0f );
  693. m_hChargerSprite->SetScale( 0.1f, 0.5f );
  694. m_hChargerSprite->TurnOn();
  695. }
  696. break;
  697. case CHARGER_STATE_DISCHARGE:
  698. {
  699. SetSkin( BOLT_SKIN_NORMAL );
  700. if ( m_hChargerSprite == NULL )
  701. break;
  702. m_hChargerSprite->SetBrightness( 0 );
  703. m_hChargerSprite->TurnOff();
  704. }
  705. break;
  706. case CHARGER_STATE_OFF:
  707. {
  708. SetSkin( BOLT_SKIN_NORMAL );
  709. if ( m_hChargerSprite == NULL )
  710. break;
  711. m_hChargerSprite->SetBrightness( 0 );
  712. m_hChargerSprite->TurnOff();
  713. }
  714. break;
  715. default:
  716. break;
  717. }
  718. }
  719. //-----------------------------------------------------------------------------
  720. // Purpose:
  721. // Input : *pEvent -
  722. // *pOperator -
  723. //-----------------------------------------------------------------------------
  724. void CWeaponCrossbow::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  725. {
  726. switch( pEvent->event )
  727. {
  728. case EVENT_WEAPON_THROW:
  729. SetChargerState( CHARGER_STATE_START_LOAD );
  730. break;
  731. case EVENT_WEAPON_THROW2:
  732. SetChargerState( CHARGER_STATE_START_CHARGE );
  733. break;
  734. case EVENT_WEAPON_THROW3:
  735. SetChargerState( CHARGER_STATE_READY );
  736. break;
  737. default:
  738. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  739. break;
  740. }
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose: Set the desired activity for the weapon and its viewmodel counterpart
  744. // Input : iActivity - activity to play
  745. //-----------------------------------------------------------------------------
  746. bool CWeaponCrossbow::SendWeaponAnim( int iActivity )
  747. {
  748. int newActivity = iActivity;
  749. // The last shot needs a non-loaded activity
  750. if ( ( newActivity == ACT_VM_IDLE ) && ( m_iClip1 <= 0 ) )
  751. {
  752. newActivity = ACT_VM_FIDGET;
  753. }
  754. //For now, just set the ideal activity and be done with it
  755. return BaseClass::SendWeaponAnim( newActivity );
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose: Stop all zooming and special effects on the viewmodel
  759. //-----------------------------------------------------------------------------
  760. void CWeaponCrossbow::StopEffects( void )
  761. {
  762. // Stop zooming
  763. if ( m_bInZoom )
  764. {
  765. ToggleZoom();
  766. }
  767. // Turn off our sprites
  768. SetChargerState( CHARGER_STATE_OFF );
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose:
  772. //-----------------------------------------------------------------------------
  773. void CWeaponCrossbow::Drop( const Vector &vecVelocity )
  774. {
  775. StopEffects();
  776. BaseClass::Drop( vecVelocity );
  777. }