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.

648 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Crossbow
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "hl1mp_basecombatweapon_shared.h"
  10. //#include "basecombatcharacter.h"
  11. //#include "AI_BaseNPC.h"
  12. #ifdef CLIENT_DLL
  13. #include "c_baseplayer.h"
  14. #else
  15. #include "player.h"
  16. #endif
  17. //#include "player.h"
  18. #include "gamerules.h"
  19. #include "in_buttons.h"
  20. #ifdef CLIENT_DLL
  21. #else
  22. #include "soundent.h"
  23. #include "entitylist.h"
  24. #include "game.h"
  25. #endif
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "IEffects.h"
  29. #ifdef CLIENT_DLL
  30. #include "c_te_effect_dispatch.h"
  31. #else
  32. #include "te_effect_dispatch.h"
  33. #endif
  34. #define BOLT_MODEL "models/crossbow_bolt.mdl"
  35. #define BOLT_AIR_VELOCITY 2000
  36. #define BOLT_WATER_VELOCITY 1000
  37. extern ConVar sk_plr_dmg_xbow_bolt_plr;
  38. extern ConVar sk_plr_dmg_xbow_bolt_npc;
  39. extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the fireball
  40. #if !defined(CLIENT_DLL)
  41. //-----------------------------------------------------------------------------
  42. // Crossbow Bolt
  43. //-----------------------------------------------------------------------------
  44. class CCrossbowBolt : public CBaseCombatCharacter
  45. {
  46. DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter );
  47. public:
  48. CCrossbowBolt()
  49. {
  50. m_bExplode = true;
  51. }
  52. Class_T Classify( void ) { return CLASS_NONE; }
  53. public:
  54. void Spawn( void );
  55. void Precache( void );
  56. void BubbleThink( void );
  57. void BoltTouch( CBaseEntity *pOther );
  58. void ExplodeThink( void );
  59. void SetExplode( bool bVal ) { m_bExplode = bVal; }
  60. bool CreateVPhysics( void );
  61. unsigned int PhysicsSolidMaskForEntity() const;
  62. static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL );
  63. DECLARE_DATADESC();
  64. private:
  65. bool m_bExplode;
  66. };
  67. LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );
  68. BEGIN_DATADESC( CCrossbowBolt )
  69. // Function Pointers
  70. DEFINE_FUNCTION( BubbleThink ),
  71. DEFINE_FUNCTION( BoltTouch ),
  72. DEFINE_FUNCTION( ExplodeThink ),
  73. END_DATADESC()
  74. CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner )
  75. {
  76. // Create a new entity with CCrossbowBolt private data
  77. CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" );
  78. UTIL_SetOrigin( pBolt, vecOrigin );
  79. pBolt->SetAbsAngles( angAngles );
  80. pBolt->Spawn();
  81. pBolt->SetOwnerEntity( pentOwner );
  82. return pBolt;
  83. }
  84. void CCrossbowBolt::Spawn( )
  85. {
  86. Precache( );
  87. SetModel( BOLT_MODEL );
  88. UTIL_SetSize( this, -Vector(1, 1, 1), Vector(1, 1, 1) );
  89. SetSolid( SOLID_BBOX );
  90. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  91. SetGravity( UTIL_ScaleForGravity( 40 ) ); // use a really low gravity (40 in/s^2)
  92. SetTouch( &CCrossbowBolt::BoltTouch );
  93. SetThink( &CCrossbowBolt::BubbleThink );
  94. SetNextThink( gpGlobals->curtime + 0.2 );
  95. m_bExplode = true;
  96. }
  97. void CCrossbowBolt::Precache( )
  98. {
  99. PrecacheModel( BOLT_MODEL );
  100. PrecacheScriptSound( "BaseGrenade.Explode" );
  101. }
  102. void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
  103. {
  104. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
  105. return;
  106. SetTouch( NULL );
  107. SetThink( NULL );
  108. if ( pOther->m_takedamage != DAMAGE_NO )
  109. {
  110. trace_t tr, tr2;
  111. tr = BaseClass::GetTouchTrace( );
  112. Vector vecNormalizedVel = GetAbsVelocity();
  113. ClearMultiDamage( );
  114. VectorNormalize( vecNormalizedVel );
  115. if ( pOther->IsPlayer() )
  116. {
  117. float m_fDamage = sk_plr_dmg_xbow_bolt_plr.GetFloat() * g_pGameRules->GetDamageMultiplier();
  118. // If multiplayer sniper bolt, multiply damage by 4
  119. if ( g_pGameRules->IsMultiplayer() && !m_bExplode )
  120. m_fDamage *= 4;
  121. CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_fDamage,DMG_NEVERGIB );
  122. CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos );
  123. pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
  124. }
  125. else
  126. {
  127. CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_xbow_bolt_npc.GetFloat() * g_pGameRules->GetDamageMultiplier(), DMG_BULLET | DMG_NEVERGIB );
  128. CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos );
  129. pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
  130. }
  131. ApplyMultiDamage();
  132. SetAbsVelocity( Vector( 0, 0, 0 ) );
  133. // play body "thwack" sound
  134. EmitSound( "Weapon_Crossbow.BoltHitBody" );
  135. Vector vForward;
  136. AngleVectors( GetAbsAngles(), &vForward );
  137. VectorNormalize ( vForward );
  138. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_OPAQUE, pOther, COLLISION_GROUP_NONE, &tr2 );
  139. if ( tr2.fraction != 1.0f )
  140. {
  141. // NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
  142. // NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
  143. CEffectData data;
  144. data.m_vOrigin = tr2.endpos;
  145. data.m_vNormal = vForward;
  146. data.m_nEntIndex = tr2.fraction != 1.0f;
  147. DispatchEffect( "BoltImpact", data );
  148. }
  149. if ( !g_pGameRules->IsMultiplayer() || !m_bExplode )
  150. {
  151. UTIL_Remove( this );
  152. }
  153. }
  154. else
  155. {
  156. EmitSound( "Weapon_Crossbow.BoltHitWorld" );
  157. SetThink( &CCrossbowBolt::SUB_Remove );
  158. SetNextThink( gpGlobals->curtime );// this will get changed below if the bolt is allowed to stick in what it hit.
  159. if ( m_bExplode == false )
  160. {
  161. Vector vForward;
  162. AngleVectors( GetAbsAngles(), &vForward );
  163. VectorNormalize ( vForward );
  164. CEffectData data;
  165. data.m_vOrigin = GetAbsOrigin();
  166. data.m_vNormal = vForward;
  167. data.m_nEntIndex = 0;
  168. DispatchEffect( "BoltImpact", data );
  169. }
  170. if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER)
  171. {
  172. g_pEffects->Sparks( GetAbsOrigin() );
  173. }
  174. }
  175. // Set up an explosion in one tenth of a second
  176. if ( g_pGameRules->IsMultiplayer() && m_bExplode )
  177. {
  178. SetThink( &CCrossbowBolt::ExplodeThink );
  179. SetNextThink( gpGlobals->curtime + 0.1f );
  180. }
  181. }
  182. void CCrossbowBolt::ExplodeThink( void )
  183. {
  184. // int iContents = UTIL_PointContents( pev->origin );
  185. CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_xbow_bolt_npc.GetFloat() * g_pGameRules->GetDamageMultiplier(), DMG_BLAST );
  186. ::RadiusDamage( dmgInfo, GetAbsOrigin(), 128, CLASS_NONE, NULL );
  187. #if !defined( CLIENT_DLL )
  188. CPASFilter filter( GetAbsOrigin() );
  189. te->Explosion( filter, /* filter */
  190. 0.0, /* delay */
  191. &GetAbsOrigin(), /* pos */
  192. g_sModelIndexFireball, /* modelindex */
  193. 0.2, /* scale */
  194. 25, /* framerate */
  195. TE_EXPLFLAG_NONE, /* flags */
  196. 128, /* radius */
  197. 64, /* magnitude */
  198. NULL, /* normal */
  199. 'C' ); /* materialType */
  200. //CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 );
  201. EmitSound( "BaseGrenade.Explode" );
  202. #endif
  203. SetThink( &CCrossbowBolt::SUB_Remove );
  204. SetNextThink( gpGlobals->curtime );
  205. AddEffects( EF_NODRAW );
  206. }
  207. void CCrossbowBolt::BubbleThink( void )
  208. {
  209. QAngle angNewAngles;
  210. VectorAngles( GetAbsVelocity(), angNewAngles );
  211. SetAbsAngles( angNewAngles );
  212. SetNextThink( gpGlobals->curtime + 0.1f );
  213. if ( GetWaterLevel() == 0 )
  214. return;
  215. UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1, GetAbsOrigin(), 1 );
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. // Output : Returns true on success, false on failure.
  220. //-----------------------------------------------------------------------------
  221. bool CCrossbowBolt::CreateVPhysics( void )
  222. {
  223. // Create the object in the physics system
  224. VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
  225. return true;
  226. }
  227. //-----------------------------------------------------------------------------
  228. //-----------------------------------------------------------------------------
  229. unsigned int CCrossbowBolt::PhysicsSolidMaskForEntity() const
  230. {
  231. return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
  232. }
  233. #endif // crossbowbolt
  234. //-----------------------------------------------------------------------------
  235. // CWeaponCrossbow
  236. //-----------------------------------------------------------------------------
  237. #ifdef CLIENT_DLL
  238. #define CWeaponCrossbow C_WeaponCrossbow
  239. #endif
  240. class CWeaponCrossbow : public CBaseHL1MPCombatWeapon
  241. {
  242. DECLARE_CLASS( CWeaponCrossbow, CBaseHL1MPCombatWeapon );
  243. public:
  244. DECLARE_NETWORKCLASS();
  245. DECLARE_PREDICTABLE();
  246. CWeaponCrossbow( void );
  247. void Precache( void );
  248. void PrimaryAttack( void );
  249. void SecondaryAttack( void );
  250. bool Reload( void );
  251. void WeaponIdle( void );
  252. bool Deploy( void );
  253. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  254. // DECLARE_SERVERCLASS();
  255. DECLARE_DATADESC();
  256. #ifndef CLIENT_DLL
  257. // DECLARE_ACTTABLE();
  258. #endif
  259. private:
  260. void FireBolt( void );
  261. void ToggleZoom( void );
  262. private:
  263. // bool m_fInZoom;
  264. CNetworkVar( bool, m_fInZoom );
  265. };
  266. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrossbow, DT_WeaponCrossbow );
  267. BEGIN_NETWORK_TABLE( CWeaponCrossbow, DT_WeaponCrossbow )
  268. #ifdef CLIENT_DLL
  269. RecvPropBool( RECVINFO( m_fInZoom ) ),
  270. #else
  271. SendPropBool( SENDINFO( m_fInZoom ) ),
  272. #endif
  273. END_NETWORK_TABLE()
  274. BEGIN_PREDICTION_DATA( CWeaponCrossbow )
  275. #ifdef CLIENT_DLL
  276. DEFINE_PRED_FIELD( m_fInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  277. #endif
  278. END_PREDICTION_DATA()
  279. LINK_ENTITY_TO_CLASS( weapon_crossbow, CWeaponCrossbow );
  280. PRECACHE_WEAPON_REGISTER( weapon_crossbow );
  281. //IMPLEMENT_SERVERCLASS_ST( CWeaponCrossbow, DT_WeaponCrossbow )
  282. //END_SEND_TABLE()
  283. BEGIN_DATADESC( CWeaponCrossbow )
  284. END_DATADESC()
  285. #if 0
  286. #ifndef CLIENT_DLL
  287. acttable_t CWeaponCrossbow::m_acttable[] =
  288. {
  289. { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false },
  290. // { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false },
  291. // { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false },
  292. // { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false },
  293. // { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false },
  294. // { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false },
  295. // { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false },
  296. };
  297. IMPLEMENT_ACTTABLE(CWeaponCrossbow);
  298. #endif
  299. #endif
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Constructor
  302. //-----------------------------------------------------------------------------
  303. CWeaponCrossbow::CWeaponCrossbow( void )
  304. {
  305. m_bReloadsSingly = false;
  306. m_bFiresUnderwater = true;
  307. m_fInZoom = false;
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. //-----------------------------------------------------------------------------
  312. void CWeaponCrossbow::Precache( void )
  313. {
  314. #ifndef CLIENT_DLL
  315. UTIL_PrecacheOther( "crossbow_bolt" );
  316. #endif
  317. PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
  318. PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
  319. BaseClass::Precache();
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose:
  323. //-----------------------------------------------------------------------------
  324. void CWeaponCrossbow::PrimaryAttack( void )
  325. {
  326. if ( m_fInZoom && g_pGameRules->IsMultiplayer() )
  327. {
  328. // FireSniperBolt();
  329. FireBolt();
  330. }
  331. else
  332. {
  333. FireBolt();
  334. }
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose:
  338. //-----------------------------------------------------------------------------
  339. void CWeaponCrossbow::SecondaryAttack( void )
  340. {
  341. ToggleZoom();
  342. m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
  343. }
  344. void CWeaponCrossbow::FireBolt( void )
  345. {
  346. if ( m_iClip1 <= 0 )
  347. {
  348. if ( !m_bFireOnEmpty )
  349. {
  350. Reload();
  351. }
  352. else
  353. {
  354. WeaponSound( EMPTY );
  355. m_flNextPrimaryAttack = 0.15;
  356. }
  357. return;
  358. }
  359. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  360. if ( pOwner == NULL )
  361. return;
  362. Vector vecAiming = pOwner->GetAutoaimVector( AUTOAIM_2DEGREES );
  363. Vector vecSrc = pOwner->Weapon_ShootPosition();
  364. QAngle angAiming;
  365. VectorAngles( vecAiming, angAiming );
  366. #ifndef CLIENT_DLL
  367. CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, pOwner );
  368. // In multiplayer, secondary fire is instantaneous.
  369. if ( g_pGameRules->IsMultiplayer() && m_fInZoom )
  370. {
  371. Vector vecEnd = vecSrc + ( vecAiming * MAX_TRACE_LENGTH );
  372. trace_t trace;
  373. UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT, GetOwner(), COLLISION_GROUP_NONE, &trace );
  374. pBolt->SetAbsOrigin( trace.endpos );
  375. // We hit someone
  376. if ( trace.m_pEnt && trace.m_pEnt->m_takedamage )
  377. {
  378. pBolt->SetExplode( false );
  379. pBolt->BoltTouch( trace.m_pEnt );
  380. return;
  381. }
  382. }
  383. else
  384. {
  385. if ( pOwner->GetWaterLevel() == 3 )
  386. {
  387. pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
  388. }
  389. else
  390. {
  391. pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
  392. }
  393. }
  394. pBolt->SetLocalAngularVelocity( QAngle( 0, 0, 10 ) );
  395. if ( m_fInZoom || !g_pGameRules->IsMultiplayer() )
  396. {
  397. pBolt->SetExplode( false );
  398. }
  399. #endif
  400. m_iClip1--;
  401. pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
  402. WeaponSound( SINGLE );
  403. WeaponSound( RELOAD );
  404. #ifdef CLIENT_DLL
  405. #else
  406. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 );
  407. #endif
  408. if ( m_iClip1 > 0 )
  409. {
  410. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  411. }
  412. else if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) > 0 )
  413. {
  414. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  415. }
  416. if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  417. {
  418. // HEV suit - indicate out of ammo condition
  419. pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
  420. }
  421. m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
  422. m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
  423. if ( m_iClip1 > 0 )
  424. {
  425. SetWeaponIdleTime( gpGlobals->curtime + 5.0 );
  426. }
  427. else
  428. {
  429. SetWeaponIdleTime( gpGlobals->curtime + 0.75 );
  430. }
  431. }
  432. bool CWeaponCrossbow::Reload( void )
  433. {
  434. bool fRet;
  435. fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
  436. if ( fRet )
  437. {
  438. if ( m_fInZoom )
  439. {
  440. ToggleZoom();
  441. }
  442. WeaponSound( RELOAD );
  443. }
  444. return fRet;
  445. }
  446. void CWeaponCrossbow::WeaponIdle( void )
  447. {
  448. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  449. if ( pPlayer )
  450. {
  451. pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES );
  452. }
  453. if ( !HasWeaponIdleTimeElapsed() )
  454. return;
  455. int iAnim;
  456. float flRand = random->RandomFloat( 0, 1 );
  457. if ( flRand <= 0.75 )
  458. {
  459. if ( m_iClip1 <= 0 )
  460. iAnim = ACT_CROSSBOW_IDLE_UNLOADED;
  461. else
  462. iAnim = ACT_VM_IDLE;
  463. }
  464. else
  465. {
  466. if ( m_iClip1 <= 0 )
  467. iAnim = ACT_CROSSBOW_FIDGET_UNLOADED;
  468. else
  469. iAnim = ACT_VM_FIDGET;
  470. }
  471. SendWeaponAnim( iAnim );
  472. }
  473. bool CWeaponCrossbow::Deploy( void )
  474. {
  475. if ( m_iClip1 <= 0 )
  476. {
  477. return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
  478. }
  479. return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
  480. }
  481. bool CWeaponCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
  482. {
  483. if ( m_fInZoom )
  484. {
  485. ToggleZoom();
  486. }
  487. return BaseClass::Holster( pSwitchingTo );
  488. }
  489. void CWeaponCrossbow::ToggleZoom( void )
  490. {
  491. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  492. if ( !pPlayer )
  493. {
  494. return;
  495. }
  496. #if !defined(CLIENT_DLL)
  497. if ( m_fInZoom )
  498. {
  499. if ( pPlayer->SetFOV( this, 0 ) )
  500. {
  501. m_fInZoom = false;
  502. }
  503. }
  504. else
  505. {
  506. if ( pPlayer->SetFOV( this, 20 ) )
  507. {
  508. m_fInZoom = true;
  509. }
  510. }
  511. #endif
  512. }