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.

956 lines
24 KiB

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