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.

816 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "beam_shared.h"
  9. #include "ndebugoverlay.h"
  10. #include "filters.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. // Keeps us from doing strcmps in the tracefilter.
  14. string_t g_iszPhysicsPropClassname;
  15. enum Touch_t
  16. {
  17. touch_none = 0,
  18. touch_player_only,
  19. touch_npc_only,
  20. touch_player_or_npc,
  21. touch_player_or_npc_or_physicsprop,
  22. };
  23. class CEnvBeam : public CBeam
  24. {
  25. public:
  26. DECLARE_CLASS( CEnvBeam, CBeam );
  27. void Spawn( void );
  28. void Precache( void );
  29. void Activate( void );
  30. void StrikeThink( void );
  31. void UpdateThink( void );
  32. void RandomArea( void );
  33. void RandomPoint( const Vector &vecSrc );
  34. void Zap( const Vector &vecSrc, const Vector &vecDest );
  35. void Strike( void );
  36. bool PassesTouchFilters(CBaseEntity *pOther);
  37. void InputTurnOn( inputdata_t &inputdata );
  38. void InputTurnOff( inputdata_t &inputdata );
  39. void InputToggle( inputdata_t &inputdata );
  40. void InputStrikeOnce( inputdata_t &inputdata );
  41. void TurnOn( void );
  42. void TurnOff( void );
  43. void Toggle( void );
  44. const char *GetDecalName( void ){ return STRING( m_iszDecal );}
  45. inline bool ServerSide( void )
  46. {
  47. if ( m_life == 0 && !HasSpawnFlags(SF_BEAM_RING) )
  48. return true;
  49. return false;
  50. }
  51. DECLARE_DATADESC();
  52. void BeamUpdateVars( void );
  53. protected:
  54. // true if the end point vecline was specified in hammer
  55. inline bool HasEndPointHandle() { return !m_vEndPointRelative.IsZero(); }
  56. int m_active;
  57. int m_spriteTexture;
  58. string_t m_iszStartEntity;
  59. string_t m_iszEndEntity;
  60. float m_life;
  61. float m_boltWidth;
  62. float m_noiseAmplitude;
  63. int m_speed;
  64. float m_restrike;
  65. string_t m_iszSpriteName;
  66. int m_frameStart;
  67. // endpoint may be optionally specified as a vecline instead of a target entity.
  68. // note: this mechanism seems rather roundabout, because the parent CBeam has
  69. // the m_vecEndPos data member; however, the CEnvBeam is programmed to overwrite it
  70. // each frame with the position of a target entity, so the easiest way to
  71. // implement this behavior was to put this bit of redundant data in the child
  72. // class and have it get written back every frame. If this bothers you,
  73. // please fix it.
  74. Vector m_vEndPointWorld; // this is the point as read from the level spec; however it's not used, because
  75. Vector m_vEndPointRelative; // on spawn, endpoint is transformed into local space here.
  76. float m_radius;
  77. Touch_t m_TouchType;
  78. string_t m_iFilterName;
  79. EHANDLE m_hFilter;
  80. string_t m_iszDecal;
  81. COutputEvent m_OnTouchedByEntity;
  82. };
  83. LINK_ENTITY_TO_CLASS( env_beam, CEnvBeam );
  84. BEGIN_DATADESC( CEnvBeam )
  85. DEFINE_FIELD( m_active, FIELD_INTEGER ),
  86. DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ),
  87. DEFINE_KEYFIELD( m_iszStartEntity, FIELD_STRING, "LightningStart" ),
  88. DEFINE_KEYFIELD( m_iszEndEntity, FIELD_STRING, "LightningEnd" ),
  89. DEFINE_KEYFIELD( m_vEndPointWorld, FIELD_VECTOR, "targetpoint" ),
  90. DEFINE_KEYFIELD( m_life, FIELD_FLOAT, "life" ),
  91. DEFINE_KEYFIELD( m_boltWidth, FIELD_FLOAT, "BoltWidth" ),
  92. DEFINE_KEYFIELD( m_noiseAmplitude, FIELD_FLOAT, "NoiseAmplitude" ),
  93. DEFINE_KEYFIELD( m_speed, FIELD_INTEGER, "TextureScroll" ),
  94. DEFINE_KEYFIELD( m_restrike, FIELD_FLOAT, "StrikeTime" ),
  95. DEFINE_KEYFIELD( m_iszSpriteName, FIELD_STRING, "texture" ),
  96. DEFINE_KEYFIELD( m_frameStart, FIELD_INTEGER, "framestart" ),
  97. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "Radius" ),
  98. DEFINE_KEYFIELD( m_TouchType, FIELD_INTEGER, "TouchType" ),
  99. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  100. DEFINE_KEYFIELD( m_iszDecal, FIELD_STRING, "decalname" ),
  101. DEFINE_KEYFIELD( m_nClipStyle, FIELD_INTEGER, "ClipStyle" ),
  102. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  103. // Function Pointers
  104. DEFINE_FUNCTION( StrikeThink ),
  105. DEFINE_FUNCTION( UpdateThink ),
  106. // Input functions
  107. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  108. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  109. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  110. DEFINE_INPUTFUNC( FIELD_VOID, "StrikeOnce", InputStrikeOnce ),
  111. DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ),
  112. END_DATADESC()
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. //-----------------------------------------------------------------------------
  116. void CEnvBeam::Spawn( void )
  117. {
  118. if ( !m_iszSpriteName )
  119. {
  120. SetThink( &CEnvBeam::SUB_Remove );
  121. return;
  122. }
  123. BaseClass::Spawn();
  124. m_noiseAmplitude = MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude);
  125. // Check for tapering
  126. if ( HasSpawnFlags( SF_BEAM_TAPEROUT ) )
  127. {
  128. SetWidth( m_boltWidth );
  129. SetEndWidth( 0 );
  130. }
  131. else
  132. {
  133. SetWidth( m_boltWidth );
  134. SetEndWidth( GetWidth() ); // Note: EndWidth is not scaled
  135. }
  136. // if a non-targetentity endpoint was specified, transform it into local relative space
  137. // so it can move along with the base
  138. if (!m_vEndPointWorld.IsZero())
  139. {
  140. WorldToEntitySpace( m_vEndPointWorld, &m_vEndPointRelative );
  141. }
  142. else
  143. {
  144. m_vEndPointRelative.Zero();
  145. }
  146. if ( ServerSide() )
  147. {
  148. SetThink( &CEnvBeam::UpdateThink );
  149. SetNextThink( gpGlobals->curtime );
  150. SetFireTime( gpGlobals->curtime );
  151. if ( GetEntityName() != NULL_STRING )
  152. {
  153. if ( !(m_spawnflags & SF_BEAM_STARTON) )
  154. {
  155. AddEffects( EF_NODRAW );
  156. m_active = 0;
  157. SetNextThink( TICK_NEVER_THINK );
  158. }
  159. else
  160. {
  161. m_active = 1;
  162. }
  163. }
  164. }
  165. else
  166. {
  167. m_active = 0;
  168. if ( !GetEntityName() || FBitSet(m_spawnflags, SF_BEAM_STARTON) )
  169. {
  170. SetThink( &CEnvBeam::StrikeThink );
  171. SetNextThink( gpGlobals->curtime + 1.0f );
  172. }
  173. }
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose:
  177. //-----------------------------------------------------------------------------
  178. void CEnvBeam::Precache( void )
  179. {
  180. if ( !Q_stristr( STRING(m_iszSpriteName), ".vmt" ) )
  181. {
  182. // HACK/YWB: This was almost always the laserbeam.spr, so alloc'ing the name a second time with the proper extension isn't going to
  183. // kill us on memrory.
  184. //Warning( "Level Design Error: %s (%i:%s) Sprite name (%s) missing .vmt extension!\n",
  185. // STRING( m_iClassname ), entindex(), GetEntityName(), STRING(m_iszSpriteName) );
  186. char fixedname[ 512 ];
  187. Q_strncpy( fixedname, STRING( m_iszSpriteName ), sizeof( fixedname ) );
  188. Q_SetExtension( fixedname, ".vmt", sizeof( fixedname ) );
  189. m_iszSpriteName = AllocPooledString( fixedname );
  190. }
  191. g_iszPhysicsPropClassname = AllocPooledString( "prop_physics" );
  192. m_spriteTexture = PrecacheModel( STRING(m_iszSpriteName) );
  193. BaseClass::Precache();
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void CEnvBeam::Activate( void )
  199. {
  200. // Get a handle to my filter entity if there is one
  201. if (m_iFilterName != NULL_STRING)
  202. {
  203. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  204. }
  205. BaseClass::Activate();
  206. if ( ServerSide() )
  207. BeamUpdateVars();
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Input handler to turn the lightning on either continually or for
  211. // interval refiring.
  212. //-----------------------------------------------------------------------------
  213. void CEnvBeam::InputTurnOn( inputdata_t &inputdata )
  214. {
  215. if ( !m_active )
  216. {
  217. TurnOn();
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Input handler to turn the lightning off.
  222. //-----------------------------------------------------------------------------
  223. void CEnvBeam::InputTurnOff( inputdata_t &inputdata )
  224. {
  225. if ( m_active )
  226. {
  227. TurnOff();
  228. }
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Input handler to toggle the lightning on/off.
  232. //-----------------------------------------------------------------------------
  233. void CEnvBeam::InputToggle( inputdata_t &inputdata )
  234. {
  235. if ( m_active )
  236. {
  237. TurnOff();
  238. }
  239. else
  240. {
  241. TurnOn();
  242. }
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose: Input handler for making the beam strike once. This will not affect
  246. // any interval refiring that might be going on. If the lifetime is set
  247. // to zero (infinite) it will turn on and stay on.
  248. //-----------------------------------------------------------------------------
  249. void CEnvBeam::InputStrikeOnce( inputdata_t &inputdata )
  250. {
  251. Strike();
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Turns the lightning on. If it is set for interval refiring, it will
  255. // begin doing so. If it is set to be continually on, it will do so.
  256. //-----------------------------------------------------------------------------
  257. void CEnvBeam::TurnOn( void )
  258. {
  259. m_active = 1;
  260. if ( ServerSide() )
  261. {
  262. RemoveEffects( EF_NODRAW );
  263. DoSparks( GetAbsStartPos(), GetAbsEndPos() );
  264. SetThink( &CEnvBeam::UpdateThink );
  265. SetNextThink( gpGlobals->curtime );
  266. SetFireTime( gpGlobals->curtime );
  267. }
  268. else
  269. {
  270. SetThink( &CEnvBeam::StrikeThink );
  271. SetNextThink( gpGlobals->curtime );
  272. }
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. //-----------------------------------------------------------------------------
  277. void CEnvBeam::TurnOff( void )
  278. {
  279. m_active = 0;
  280. if ( ServerSide() )
  281. {
  282. AddEffects( EF_NODRAW );
  283. }
  284. SetNextThink( TICK_NEVER_THINK );
  285. SetThink( NULL );
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose: Think function for striking at intervals.
  289. //-----------------------------------------------------------------------------
  290. void CEnvBeam::StrikeThink( void )
  291. {
  292. if ( m_life != 0 )
  293. {
  294. if ( m_spawnflags & SF_BEAM_RANDOM )
  295. SetNextThink( gpGlobals->curtime + m_life + random->RandomFloat( 0, m_restrike ) );
  296. else
  297. SetNextThink( gpGlobals->curtime + m_life + m_restrike );
  298. }
  299. m_active = 1;
  300. if (!m_iszEndEntity && !HasEndPointHandle())
  301. {
  302. if (!m_iszStartEntity)
  303. {
  304. RandomArea( );
  305. }
  306. else
  307. {
  308. CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
  309. if (pStart != NULL)
  310. {
  311. RandomPoint( pStart->GetAbsOrigin() );
  312. }
  313. else
  314. {
  315. Msg( "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) );
  316. }
  317. }
  318. return;
  319. }
  320. Strike();
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Strikes once for its configured lifetime.
  324. //-----------------------------------------------------------------------------
  325. void CEnvBeam::Strike( void )
  326. {
  327. CBroadcastRecipientFilter filter;
  328. CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
  329. CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) );
  330. // if the end entity is missing, we use the Hammer-specified vector offset instead.
  331. bool bEndPointFromEntity = pEnd != NULL;
  332. if ( pStart == NULL || ( !bEndPointFromEntity && !HasEndPointHandle() ) )
  333. return;
  334. Vector vEndPointLocation;
  335. if ( bEndPointFromEntity )
  336. {
  337. vEndPointLocation = pEnd->GetAbsOrigin() ;
  338. }
  339. else
  340. {
  341. EntityToWorldSpace( m_vEndPointRelative, &vEndPointLocation );
  342. }
  343. m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED );
  344. bool pointStart = IsStaticPointEntity( pStart );
  345. bool pointEnd = !bEndPointFromEntity || IsStaticPointEntity( pEnd );
  346. if ( pointStart || pointEnd )
  347. {
  348. if ( m_spawnflags & SF_BEAM_RING )
  349. {
  350. // don't work
  351. return;
  352. }
  353. te->BeamEntPoint( filter, 0.0,
  354. pointStart ? 0 : pStart->entindex(),
  355. pointStart ? &pStart->GetAbsOrigin() : NULL,
  356. pointEnd ? 0 : pEnd->entindex(),
  357. pointEnd ? &vEndPointLocation : NULL,
  358. m_spriteTexture,
  359. 0, // No halo
  360. m_frameStart,
  361. (int)m_flFrameRate,
  362. m_life,
  363. m_boltWidth,
  364. m_boltWidth, // End width
  365. 0, // No fade
  366. m_noiseAmplitude,
  367. m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a,
  368. m_speed );
  369. }
  370. else
  371. {
  372. if ( m_spawnflags & SF_BEAM_RING)
  373. {
  374. te->BeamRing( filter, 0.0,
  375. pStart->entindex(),
  376. pEnd->entindex(),
  377. m_spriteTexture,
  378. 0, // No halo
  379. m_frameStart,
  380. (int)m_flFrameRate,
  381. m_life,
  382. m_boltWidth,
  383. 0, // No spread
  384. m_noiseAmplitude,
  385. m_clrRender->r,
  386. m_clrRender->g,
  387. m_clrRender->b,
  388. m_clrRender->a,
  389. m_speed );
  390. }
  391. else
  392. {
  393. te->BeamEnts( filter, 0.0,
  394. pStart->entindex(),
  395. pEnd->entindex(),
  396. m_spriteTexture,
  397. 0, // No halo
  398. m_frameStart,
  399. (int)m_flFrameRate,
  400. m_life,
  401. m_boltWidth,
  402. m_boltWidth, // End width
  403. 0, // No fade
  404. m_noiseAmplitude,
  405. m_clrRender->r,
  406. m_clrRender->g,
  407. m_clrRender->b,
  408. m_clrRender->a,
  409. m_speed );
  410. }
  411. }
  412. DoSparks( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin() );
  413. if ( m_flDamage > 0 )
  414. {
  415. trace_t tr;
  416. UTIL_TraceLine( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  417. BeamDamageInstant( &tr, m_flDamage );
  418. }
  419. }
  420. class CTraceFilterPlayersNPCs : public ITraceFilter
  421. {
  422. public:
  423. bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  424. {
  425. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  426. if ( pEntity )
  427. {
  428. if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() )
  429. return true;
  430. }
  431. return false;
  432. }
  433. virtual TraceType_t GetTraceType() const
  434. {
  435. return TRACE_ENTITIES_ONLY;
  436. }
  437. };
  438. class CTraceFilterPlayersNPCsPhysicsProps : public ITraceFilter
  439. {
  440. public:
  441. bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  442. {
  443. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  444. if ( pEntity )
  445. {
  446. if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() || pEntity->m_iClassname == g_iszPhysicsPropClassname )
  447. return true;
  448. }
  449. return false;
  450. }
  451. virtual TraceType_t GetTraceType() const
  452. {
  453. return TRACE_ENTITIES_ONLY;
  454. }
  455. };
  456. //-----------------------------------------------------------------------------
  457. //-----------------------------------------------------------------------------
  458. bool CEnvBeam::PassesTouchFilters(CBaseEntity *pOther)
  459. {
  460. bool fPassedSoFar = false;
  461. // Touched some player or NPC!
  462. if( m_TouchType != touch_npc_only )
  463. {
  464. if( pOther->IsPlayer() )
  465. {
  466. fPassedSoFar = true;
  467. }
  468. }
  469. if( m_TouchType != touch_player_only )
  470. {
  471. if( pOther->IsNPC() )
  472. {
  473. fPassedSoFar = true;
  474. }
  475. }
  476. if( m_TouchType == touch_player_or_npc_or_physicsprop )
  477. {
  478. if( pOther->m_iClassname == g_iszPhysicsPropClassname )
  479. {
  480. fPassedSoFar = true;
  481. }
  482. }
  483. if( fPassedSoFar )
  484. {
  485. CBaseFilter* pFilter = (CBaseFilter*)(m_hFilter.Get());
  486. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  487. }
  488. return false;
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose:
  492. //-----------------------------------------------------------------------------
  493. void CEnvBeam::UpdateThink( void )
  494. {
  495. // Apply damage every 1/10th of a second.
  496. if ( ( m_flDamage > 0 ) && ( gpGlobals->curtime >= m_flFireTime + 0.1 ) )
  497. {
  498. trace_t tr;
  499. UTIL_TraceLine( GetAbsStartPos(), GetAbsEndPos(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  500. BeamDamage( &tr );
  501. // BeamDamage calls RelinkBeam, so no need to call it again.
  502. }
  503. else
  504. {
  505. RelinkBeam();
  506. }
  507. if( m_TouchType != touch_none )
  508. {
  509. trace_t tr;
  510. Ray_t ray;
  511. ray.Init( GetAbsStartPos(), GetAbsEndPos() );
  512. if( m_TouchType == touch_player_or_npc_or_physicsprop )
  513. {
  514. CTraceFilterPlayersNPCsPhysicsProps traceFilter;
  515. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  516. }
  517. else
  518. {
  519. CTraceFilterPlayersNPCs traceFilter;
  520. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  521. }
  522. if( tr.fraction != 1.0 && PassesTouchFilters( tr.m_pEnt ) )
  523. {
  524. m_OnTouchedByEntity.FireOutput( tr.m_pEnt, this, 0 );
  525. return;
  526. }
  527. }
  528. SetNextThink( gpGlobals->curtime );
  529. }
  530. //-----------------------------------------------------------------------------
  531. // Purpose:
  532. // Input : &vecSrc -
  533. // &vecDest -
  534. //-----------------------------------------------------------------------------
  535. void CEnvBeam::Zap( const Vector &vecSrc, const Vector &vecDest )
  536. {
  537. CBroadcastRecipientFilter filter;
  538. te->BeamPoints( filter, 0.0,
  539. &vecSrc,
  540. &vecDest,
  541. m_spriteTexture,
  542. 0, // No halo
  543. m_frameStart,
  544. (int)m_flFrameRate,
  545. m_life,
  546. m_boltWidth,
  547. m_boltWidth, // End width
  548. 0, // No fade
  549. m_noiseAmplitude,
  550. m_clrRender->r,
  551. m_clrRender->g,
  552. m_clrRender->b,
  553. m_clrRender->a,
  554. m_speed );
  555. DoSparks( vecSrc, vecDest );
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose:
  559. //-----------------------------------------------------------------------------
  560. void CEnvBeam::RandomArea( void )
  561. {
  562. int iLoops = 0;
  563. for (iLoops = 0; iLoops < 10; iLoops++)
  564. {
  565. Vector vecSrc = GetAbsOrigin();
  566. Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  567. VectorNormalize( vecDir1 );
  568. trace_t tr1;
  569. UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
  570. if (tr1.fraction == 1.0)
  571. continue;
  572. Vector vecDir2;
  573. do {
  574. vecDir2 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  575. } while (DotProduct(vecDir1, vecDir2 ) > 0);
  576. VectorNormalize( vecDir2 );
  577. trace_t tr2;
  578. UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
  579. if (tr2.fraction == 1.0)
  580. continue;
  581. if ((tr1.endpos - tr2.endpos).Length() < m_radius * 0.1)
  582. continue;
  583. UTIL_TraceLine( tr1.endpos, tr2.endpos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
  584. if (tr2.fraction != 1.0)
  585. continue;
  586. Zap( tr1.endpos, tr2.endpos );
  587. break;
  588. }
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose:
  592. // Input : vecSrc -
  593. //-----------------------------------------------------------------------------
  594. void CEnvBeam::RandomPoint( const Vector &vecSrc )
  595. {
  596. int iLoops = 0;
  597. for (iLoops = 0; iLoops < 10; iLoops++)
  598. {
  599. Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  600. VectorNormalize( vecDir1 );
  601. trace_t tr1;
  602. UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
  603. if ((tr1.endpos - vecSrc).Length() < m_radius * 0.1)
  604. continue;
  605. if (tr1.fraction == 1.0)
  606. continue;
  607. Zap( vecSrc, tr1.endpos );
  608. break;
  609. }
  610. }
  611. //-----------------------------------------------------------------------------
  612. // Purpose:
  613. //-----------------------------------------------------------------------------
  614. void CEnvBeam::BeamUpdateVars( void )
  615. {
  616. CBaseEntity *pStart = gEntList.FindEntityByName( NULL, m_iszStartEntity );
  617. CBaseEntity *pEnd = gEntList.FindEntityByName( NULL, m_iszEndEntity );
  618. // if the end entity is missing, we use the Hammer-specified vector offset instead.
  619. bool bEndPointFromEntity = pEnd != NULL;
  620. if (( pStart == NULL ) || ( !bEndPointFromEntity && !HasEndPointHandle() ))
  621. {
  622. return;
  623. }
  624. Vector vEndPointPos;
  625. if ( bEndPointFromEntity )
  626. {
  627. vEndPointPos = pEnd->GetAbsOrigin();
  628. }
  629. else
  630. {
  631. EntityToWorldSpace( m_vEndPointRelative, &vEndPointPos );
  632. }
  633. m_nNumBeamEnts = 2;
  634. m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED );
  635. // NOTE: If the end entity is the beam itself (and the start entity
  636. // isn't *also* the beam itself, we've got problems. This is a problem
  637. // because SetAbsStartPos actually sets the entity's origin.
  638. if ( ( pEnd == this ) && ( pStart != this ) )
  639. {
  640. DevMsg("env_beams cannot have the end entity be the beam itself\n"
  641. "unless the start entity is also the beam itself!\n" );
  642. Assert(0);
  643. }
  644. SetModelName( m_iszSpriteName );
  645. SetTexture( m_spriteTexture );
  646. SetType( BEAM_ENTPOINT );
  647. if ( IsStaticPointEntity( pStart ) )
  648. {
  649. SetAbsStartPos( pStart->GetAbsOrigin() );
  650. }
  651. else
  652. {
  653. SetStartEntity( pStart );
  654. }
  655. if ( !bEndPointFromEntity || IsStaticPointEntity( pEnd ) )
  656. {
  657. SetAbsEndPos( vEndPointPos );
  658. }
  659. else
  660. {
  661. SetEndEntity( pEnd );
  662. }
  663. RelinkBeam();
  664. SetWidth( MIN(MAX_BEAM_WIDTH, m_boltWidth) );
  665. SetNoise( MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude) );
  666. SetFrame( m_frameStart );
  667. SetScrollRate( m_speed );
  668. if ( m_spawnflags & SF_BEAM_SHADEIN )
  669. {
  670. SetBeamFlags( FBEAM_SHADEIN );
  671. }
  672. else if ( m_spawnflags & SF_BEAM_SHADEOUT )
  673. {
  674. SetBeamFlags( FBEAM_SHADEOUT );
  675. }
  676. }