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.

442 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "c_weapon__stubs.h"
  8. #include "c_basehlcombatweapon.h"
  9. #include "fx.h"
  10. #include "particles_localspace.h"
  11. #include "view.h"
  12. #include "particles_attractor.h"
  13. class C_WeaponPhysCannon: public C_BaseHLCombatWeapon
  14. {
  15. DECLARE_CLASS( C_WeaponPhysCannon, C_BaseHLCombatWeapon );
  16. public:
  17. C_WeaponPhysCannon( void );
  18. DECLARE_CLIENTCLASS();
  19. DECLARE_PREDICTABLE();
  20. virtual void OnDataChanged( DataUpdateType_t updateType );
  21. virtual int DrawModel( int flags );
  22. virtual void ClientThink( void );
  23. virtual bool ShouldUseLargeViewModelVROverride() OVERRIDE { return true; }
  24. private:
  25. bool SetupEmitter( void );
  26. bool m_bIsCurrentlyUpgrading;
  27. float m_flTimeForceView;
  28. float m_flTimeIgnoreForceView;
  29. bool m_bWasUpgraded;
  30. CSmartPtr<CLocalSpaceEmitter> m_pLocalEmitter;
  31. CSmartPtr<CSimpleEmitter> m_pEmitter;
  32. CSmartPtr<CParticleAttractor> m_pAttractor;
  33. };
  34. STUB_WEAPON_CLASS_IMPLEMENT( weapon_physcannon, C_WeaponPhysCannon );
  35. IMPLEMENT_CLIENTCLASS_DT( C_WeaponPhysCannon, DT_WeaponPhysCannon, CWeaponPhysCannon )
  36. RecvPropBool( RECVINFO( m_bIsCurrentlyUpgrading ) ),
  37. RecvPropFloat( RECVINFO( m_flTimeForceView) ),
  38. END_RECV_TABLE()
  39. //-----------------------------------------------------------------------------
  40. // Constructor
  41. //-----------------------------------------------------------------------------
  42. C_WeaponPhysCannon::C_WeaponPhysCannon( void )
  43. {
  44. m_bWasUpgraded = false;
  45. m_flTimeIgnoreForceView = -1;
  46. }
  47. //-----------------------------------------------------------------------------
  48. //-----------------------------------------------------------------------------
  49. void C_WeaponPhysCannon::OnDataChanged( DataUpdateType_t updateType )
  50. {
  51. BaseClass::OnDataChanged( updateType );
  52. SetNextClientThink( CLIENT_THINK_ALWAYS );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose:
  56. // Output : Returns true on success, false on failure.
  57. //-----------------------------------------------------------------------------
  58. bool C_WeaponPhysCannon::SetupEmitter( void )
  59. {
  60. if ( !m_pLocalEmitter.IsValid() )
  61. {
  62. m_pLocalEmitter = CLocalSpaceEmitter::Create( "physpowerup", GetRefEHandle(), LookupAttachment( "core" ) );
  63. if ( m_pLocalEmitter.IsValid() == false )
  64. return false;
  65. }
  66. if ( !m_pAttractor.IsValid() )
  67. {
  68. m_pAttractor = CParticleAttractor::Create( vec3_origin, "physpowerup_att" );
  69. if ( m_pAttractor.IsValid() == false )
  70. return false;
  71. }
  72. if ( !m_pEmitter.IsValid() )
  73. {
  74. m_pEmitter = CSimpleEmitter::Create( "physpowerup_glow" );
  75. if ( m_pEmitter.IsValid() == false )
  76. return false;
  77. }
  78. return true;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Sorts the components of a vector
  82. //-----------------------------------------------------------------------------
  83. static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
  84. {
  85. Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );
  86. int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
  87. if (absVec[2] > absVec[maxIdx])
  88. {
  89. maxIdx = 2;
  90. }
  91. // always choose something right-handed....
  92. switch( maxIdx )
  93. {
  94. case 0:
  95. pVecIdx[0] = 1;
  96. pVecIdx[1] = 2;
  97. pVecIdx[2] = 0;
  98. break;
  99. case 1:
  100. pVecIdx[0] = 2;
  101. pVecIdx[1] = 0;
  102. pVecIdx[2] = 1;
  103. break;
  104. case 2:
  105. pVecIdx[0] = 0;
  106. pVecIdx[1] = 1;
  107. pVecIdx[2] = 2;
  108. break;
  109. }
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Compute the bounding box's center, size, and basis
  113. //-----------------------------------------------------------------------------
  114. void ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld,
  115. Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec )
  116. {
  117. // Compute the center of the hitbox in worldspace
  118. Vector vecHitboxCenter;
  119. VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter );
  120. vecHitboxCenter *= 0.5f;
  121. VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin );
  122. // Get the object's basis
  123. Vector vec[3];
  124. MatrixGetColumn( hitboxToWorld, 0, vec[0] );
  125. MatrixGetColumn( hitboxToWorld, 1, vec[1] );
  126. MatrixGetColumn( hitboxToWorld, 2, vec[2] );
  127. // vec[1] *= -1.0f;
  128. Vector vecViewDir;
  129. VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir );
  130. VectorNormalize( vecViewDir );
  131. // Project the shadow casting direction into the space of the hitbox
  132. Vector localViewDir;
  133. localViewDir[0] = DotProduct( vec[0], vecViewDir );
  134. localViewDir[1] = DotProduct( vec[1], vecViewDir );
  135. localViewDir[2] = DotProduct( vec[2], vecViewDir );
  136. // Figure out which vector has the largest component perpendicular
  137. // to the view direction...
  138. // Sort by how perpendicular it is
  139. int vecIdx[3];
  140. SortAbsVectorComponents( localViewDir, vecIdx );
  141. // Here's our hitbox basis vectors; namely the ones that are
  142. // most perpendicular to the view direction
  143. *pXVec = vec[vecIdx[0]];
  144. *pYVec = vec[vecIdx[1]];
  145. // Project them into a plane perpendicular to the view direction
  146. *pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec );
  147. *pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec );
  148. VectorNormalize( *pXVec );
  149. VectorNormalize( *pYVec );
  150. // Compute the hitbox size
  151. Vector boxSize;
  152. VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize );
  153. // We project the two longest sides into the vectors perpendicular
  154. // to the projection direction, then add in the projection of the perp direction
  155. Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
  156. size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) );
  157. size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) );
  158. // Add the third component into x and y
  159. size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) );
  160. size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) );
  161. // Bloat a bit, since the shadow wants to extend outside the model a bit
  162. size *= 2.0f;
  163. // Clamp the minimum size
  164. Vector2DMax( size, Vector2D(10.0f, 10.0f), size );
  165. // Factor the size into the xvec + yvec
  166. (*pXVec) *= size.x * 0.5f;
  167. (*pYVec) *= size.y * 0.5f;
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose:
  171. // Input : flags -
  172. // Output : int
  173. //-----------------------------------------------------------------------------
  174. int C_WeaponPhysCannon::DrawModel( int flags )
  175. {
  176. // If we're not ugrading, don't do anything special
  177. if ( m_bIsCurrentlyUpgrading == false && m_bWasUpgraded == false )
  178. return BaseClass::DrawModel( flags );
  179. if ( gpGlobals->frametime == 0 )
  180. return BaseClass::DrawModel( flags );
  181. if ( !m_bReadyToDraw )
  182. return 0;
  183. m_bWasUpgraded = true;
  184. // Create the particle emitter if it's not already
  185. if ( SetupEmitter() )
  186. {
  187. // Add the power-up particles
  188. // See if we should draw
  189. if ( m_bReadyToDraw == false )
  190. return 0;
  191. C_BaseAnimating *pAnimating = GetBaseAnimating();
  192. if (!pAnimating)
  193. return 0;
  194. matrix3x4_t *hitboxbones[MAXSTUDIOBONES];
  195. if ( !pAnimating->HitboxToWorldTransforms( hitboxbones ) )
  196. return 0;
  197. studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() );
  198. if (!pStudioHdr)
  199. return false;
  200. mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() );
  201. if ( !set )
  202. return false;
  203. int i;
  204. float fadePerc = 1.0f;
  205. if ( m_bIsCurrentlyUpgrading )
  206. {
  207. Vector vecSkew = vec3_origin;
  208. // Skew the particles in front or in back of their targets
  209. vecSkew = CurrentViewForward() * 4.0f;
  210. float spriteScale = 1.0f;
  211. spriteScale = clamp( spriteScale, 0.75f, 1.0f );
  212. SimpleParticle *sParticle;
  213. for ( i = 0; i < set->numhitboxes; ++i )
  214. {
  215. Vector vecAbsOrigin, xvec, yvec;
  216. mstudiobbox_t *pBox = set->pHitbox(i);
  217. ComputeRenderInfo( pBox, *hitboxbones[pBox->bone], &vecAbsOrigin, &xvec, &yvec );
  218. Vector offset;
  219. Vector xDir, yDir;
  220. xDir = xvec;
  221. float xScale = VectorNormalize( xDir ) * 0.75f;
  222. yDir = yvec;
  223. float yScale = VectorNormalize( yDir ) * 0.75f;
  224. int numParticles = clamp( 4.0f * fadePerc, 1, 3 );
  225. for ( int j = 0; j < numParticles; j++ )
  226. {
  227. offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f );
  228. offset += vecSkew;
  229. sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), m_pEmitter->GetPMaterial( "effects/combinemuzzle1" ), vecAbsOrigin + offset );
  230. if ( sParticle == NULL )
  231. return 1;
  232. sParticle->m_vecVelocity = vec3_origin;
  233. sParticle->m_uchStartSize = 16.0f * spriteScale;
  234. sParticle->m_flDieTime = 0.2f;
  235. sParticle->m_flLifetime = 0.0f;
  236. sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
  237. sParticle->m_flRollDelta = Helper_RandomFloat( -2.0f, 2.0f );
  238. float alpha = 40;
  239. sParticle->m_uchColor[0] = alpha;
  240. sParticle->m_uchColor[1] = alpha;
  241. sParticle->m_uchColor[2] = alpha;
  242. sParticle->m_uchStartAlpha = alpha;
  243. sParticle->m_uchEndAlpha = 0;
  244. sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2;
  245. }
  246. }
  247. }
  248. }
  249. int attachment = LookupAttachment( "core" );
  250. Vector coreOrigin;
  251. QAngle coreAngles;
  252. GetAttachment( attachment, coreOrigin, coreAngles );
  253. SimpleParticle *sParticle;
  254. // Do the core effects
  255. for ( int i = 0; i < 4; i++ )
  256. {
  257. sParticle = (SimpleParticle *) m_pLocalEmitter->AddParticle( sizeof(SimpleParticle), m_pLocalEmitter->GetPMaterial( "effects/strider_muzzle" ), vec3_origin );
  258. if ( sParticle == NULL )
  259. return 1;
  260. sParticle->m_vecVelocity = vec3_origin;
  261. sParticle->m_flDieTime = 0.1f;
  262. sParticle->m_flLifetime = 0.0f;
  263. sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
  264. sParticle->m_flRollDelta = 0.0f;
  265. float alpha = 255;
  266. sParticle->m_uchColor[0] = alpha;
  267. sParticle->m_uchColor[1] = alpha;
  268. sParticle->m_uchColor[2] = alpha;
  269. sParticle->m_uchStartAlpha = alpha;
  270. sParticle->m_uchEndAlpha = 0;
  271. if ( i < 2 )
  272. {
  273. sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
  274. sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
  275. }
  276. else
  277. {
  278. if ( random->RandomInt( 0, 20 ) == 0 )
  279. {
  280. sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
  281. sParticle->m_uchEndSize = sParticle->m_uchStartSize * 4.0f;
  282. sParticle->m_flDieTime = 0.25f;
  283. }
  284. else
  285. {
  286. sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
  287. sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
  288. }
  289. }
  290. }
  291. if ( m_bWasUpgraded && m_bIsCurrentlyUpgrading )
  292. {
  293. // Update our attractor point
  294. m_pAttractor->SetAttractorOrigin( coreOrigin );
  295. Vector offset;
  296. for ( int i = 0; i < 4; i++ )
  297. {
  298. offset = coreOrigin + RandomVector( -32.0f, 32.0f );
  299. sParticle = (SimpleParticle *) m_pAttractor->AddParticle( sizeof(SimpleParticle), m_pAttractor->GetPMaterial( "effects/strider_muzzle" ), offset );
  300. if ( sParticle == NULL )
  301. return 1;
  302. sParticle->m_vecVelocity = Vector(0,0,8);
  303. sParticle->m_flDieTime = 0.5f;
  304. sParticle->m_flLifetime = 0.0f;
  305. sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
  306. sParticle->m_flRollDelta = 0.0f;
  307. float alpha = 255;
  308. sParticle->m_uchColor[0] = alpha;
  309. sParticle->m_uchColor[1] = alpha;
  310. sParticle->m_uchColor[2] = alpha;
  311. sParticle->m_uchStartAlpha = alpha;
  312. sParticle->m_uchEndAlpha = 0;
  313. sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
  314. sParticle->m_uchEndSize = 0;
  315. }
  316. }
  317. return BaseClass::DrawModel( flags );
  318. }
  319. //---------------------------------------------------------
  320. // On 360, raise up the player's view if the server has
  321. // asked us to.
  322. //---------------------------------------------------------
  323. #define PHYSCANNON_RAISE_VIEW_GOAL 0.0f
  324. void C_WeaponPhysCannon::ClientThink( void )
  325. {
  326. if( m_flTimeIgnoreForceView > gpGlobals->curtime )
  327. return;
  328. float flTime = (m_flTimeForceView - gpGlobals->curtime);
  329. if( flTime < 0.0f )
  330. return;
  331. float flDT = 1.0f - flTime;
  332. if( flDT > 0.0f )
  333. {
  334. QAngle viewangles;
  335. engine->GetViewAngles( viewangles );
  336. if( viewangles.x > PHYSCANNON_RAISE_VIEW_GOAL + 1.0f )
  337. {
  338. float flDelta = PHYSCANNON_RAISE_VIEW_GOAL - viewangles.x;
  339. viewangles.x += (flDelta * flDT);
  340. engine->SetViewAngles(viewangles);
  341. }
  342. else
  343. {
  344. // We've reached our goal. Ignore the forced view angles for now.
  345. m_flTimeIgnoreForceView = m_flTimeForceView + 0.1f;
  346. }
  347. }
  348. return BaseClass::ClientThink();
  349. }