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.

1379 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // npc_blob - experimental, cpu-intensive monster made of lots of smaller elements
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_default.h"
  8. #include "ai_task.h"
  9. #include "ai_schedule.h"
  10. #include "ai_hull.h"
  11. #include "soundent.h"
  12. #include "game.h"
  13. #include "npcevent.h"
  14. #include "entitylist.h"
  15. #include "activitylist.h"
  16. #include "ai_basenpc.h"
  17. #include "engine/IEngineSound.h"
  18. #include "vstdlib/jobthread.h"
  19. #include "saverestore_utlvector.h"
  20. #include "eventqueue.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. extern float MOVE_HEIGHT_EPSILON;
  24. #define BLOB_MAX_AVOID_ORIGINS 3
  25. ConVar blob_mindist( "blob_mindist", "120.0" );
  26. ConVar blob_element_speed( "blob_element_speed", "187" );
  27. ConVar npc_blob_idle_speed_factor( "npc_blob_idle_speed_factor", "0.5" );
  28. ConVar blob_numelements( "blob_numelements", "20" );
  29. ConVar blob_batchpercent( "blob_batchpercent", "100" );
  30. ConVar blob_radius( "blob_radius", "160" );
  31. //ConVar blob_min_element_speed( "blob_min_element_speed", "50" );
  32. //ConVar blob_max_element_speed( "blob_max_element_speed", "250" );
  33. ConVar npc_blob_use_threading( "npc_blob_use_threading", "1" );
  34. ConVar npc_blob_sin_amplitude( "npc_blob_sin_amplitude", "60.0f" );
  35. ConVar npc_blob_show_centroid( "npc_blob_show_centroid", "0" );
  36. ConVar npc_blob_straggler_dist( "npc_blob_straggler_dist", "240" );
  37. ConVar npc_blob_use_orientation( "npc_blob_use_orientation", "1" );
  38. ConVar npc_blob_use_model( "npc_blob_use_model", "2" );
  39. ConVar npc_blob_think_interval( "npc_blob_think_interval", "0.025" );
  40. #define NPC_BLOB_MODEL "models/headcrab.mdl"
  41. //=========================================================
  42. // Blob movement rules
  43. //=========================================================
  44. enum
  45. {
  46. BLOB_MOVE_SWARM = 0, // Just swarm with the rest of the group
  47. BLOB_MOVE_TO_TARGET_LOCATION, // Move to a designated location
  48. BLOB_MOVE_TO_TARGET_ENTITY, // Chase the designated entity
  49. BLOB_MOVE_DONT_MOVE, // Sit still!!!!
  50. };
  51. //=========================================================
  52. //=========================================================
  53. class CBlobElement : public CBaseAnimating
  54. {
  55. public:
  56. void Precache();
  57. void Spawn();
  58. int DrawDebugTextOverlays(void);
  59. void SetElementVelocity( Vector vecVelocity, bool bPlanarOnly );
  60. void AddElementVelocity( Vector vecVelocityAdd, bool bPlanarOnly );
  61. void ModifyVelocityForSurface( float flInterval, float flSpeed );
  62. void SetSinePhase( float flPhase ) { m_flSinePhase = flPhase; }
  63. float GetSinePhase() { return m_flSinePhase; }
  64. float GetSineAmplitude() { return m_flSineAmplitude; }
  65. float GetSineFrequency() { return m_flSineFrequency; }
  66. void SetActiveMovementRule( int moveRule ) { m_iMovementRule = moveRule; }
  67. int GetActiveMovementRule() { return m_iMovementRule; }
  68. void MoveTowardsTargetEntity( float speed );
  69. void SetTargetEntity( CBaseEntity *pEntity ) { m_hTargetEntity = pEntity; }
  70. CBaseEntity *GetTargetEntity() { return m_hTargetEntity.Get(); }
  71. void MoveTowardsTargetLocation( float speed );
  72. void SetTargetLocation( const Vector &vecLocation ) { m_vecTargetLocation = vecLocation; }
  73. void ReconfigureRandomParams();
  74. void EnforceSpeedLimits( float flMinSpeed, float flMaxSpeed );
  75. DECLARE_DATADESC();
  76. public:
  77. Vector m_vecPrevOrigin; // Only exists for debugging (isolating stuck elements)
  78. int m_iStuckCount;
  79. bool m_bOnWall;
  80. float m_flDistFromCentroidSqr;
  81. int m_iElementNumber;
  82. Vector m_vecTargetLocation;
  83. float m_flRandomEightyPercent;
  84. private:
  85. EHANDLE m_hTargetEntity;
  86. float m_flSinePhase;
  87. float m_flSineAmplitude;
  88. float m_flSineFrequency;
  89. int m_iMovementRule;
  90. };
  91. LINK_ENTITY_TO_CLASS( blob_element, CBlobElement );
  92. //---------------------------------------------------------
  93. // Save/Restore
  94. //---------------------------------------------------------
  95. BEGIN_DATADESC( CBlobElement )
  96. DEFINE_FIELD( m_vecPrevOrigin, FIELD_POSITION_VECTOR ),
  97. DEFINE_FIELD( m_iStuckCount, FIELD_INTEGER ),
  98. DEFINE_FIELD( m_bOnWall, FIELD_BOOLEAN ),
  99. DEFINE_FIELD( m_flDistFromCentroidSqr, FIELD_FLOAT ),
  100. DEFINE_FIELD( m_iElementNumber, FIELD_INTEGER ),
  101. DEFINE_FIELD( m_vecTargetLocation, FIELD_POSITION_VECTOR ),
  102. DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ),
  103. DEFINE_FIELD( m_flSinePhase, FIELD_FLOAT ),
  104. DEFINE_FIELD( m_flSineAmplitude, FIELD_FLOAT ),
  105. DEFINE_FIELD( m_flSineFrequency, FIELD_FLOAT ),
  106. DEFINE_FIELD( m_iMovementRule, FIELD_INTEGER ),
  107. END_DATADESC()
  108. const char *pszBlobModels[] =
  109. {
  110. "models/gibs/agibs.mdl",
  111. "models/props_junk/watermelon01.mdl",
  112. "models/w_squeak.mdl",
  113. "models/baby_headcrab.mdl"
  114. };
  115. const char *GetBlobModelName()
  116. {
  117. int index = npc_blob_use_model.GetInt();
  118. return pszBlobModels[ index ];
  119. }
  120. //---------------------------------------------------------
  121. //---------------------------------------------------------
  122. void CBlobElement::Precache()
  123. {
  124. PrecacheModel( GetBlobModelName() );
  125. m_flRandomEightyPercent = random->RandomFloat( 0.8f, 1.0f );
  126. }
  127. //---------------------------------------------------------
  128. //---------------------------------------------------------
  129. void CBlobElement::Spawn()
  130. {
  131. Precache();
  132. SetSolid( SOLID_NONE );
  133. SetMoveType( MOVETYPE_FLY );
  134. AddSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_NOT_SOLID );
  135. SetModel( GetBlobModelName() );
  136. UTIL_SetSize( this, vec3_origin, vec3_origin );
  137. QAngle angles(0,0,0);
  138. angles.y = random->RandomFloat( 0, 180 );
  139. SetAbsAngles( angles );
  140. AddEffects( EF_NOSHADOW );
  141. ReconfigureRandomParams();
  142. }
  143. //---------------------------------------------------------
  144. //---------------------------------------------------------
  145. int CBlobElement::DrawDebugTextOverlays(void)
  146. {
  147. int text_offset = BaseClass::DrawDebugTextOverlays();
  148. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  149. {
  150. char tempstr[512];
  151. Q_snprintf(tempstr,sizeof(tempstr), "Element #:%d", m_iElementNumber );
  152. EntityText(text_offset,tempstr,0);
  153. text_offset++;
  154. }
  155. return text_offset;
  156. }
  157. //---------------------------------------------------------
  158. // This is the official way to set velocity for an element
  159. // Do not call SetAbsVelocity() directly, since we also
  160. // need to record the last velocity we intended to give the
  161. // element, so that we can detect changes after game physics
  162. // runs.
  163. //---------------------------------------------------------
  164. void CBlobElement::SetElementVelocity( Vector vecVelocity, bool bPlanarOnly )
  165. {
  166. SetAbsVelocity( vecVelocity );
  167. }
  168. //---------------------------------------------------------
  169. // This is the official way to add velocity to an element.
  170. // See SetElementVelocity() for explanation.
  171. //---------------------------------------------------------
  172. void CBlobElement::AddElementVelocity( Vector vecVelocityAdd, bool bPlanarOnly )
  173. {
  174. Vector vecSum = GetAbsVelocity() + vecVelocityAdd;
  175. SetAbsVelocity( vecSum );
  176. }
  177. //---------------------------------------------------------
  178. // This function seeks to keep the blob element moving along
  179. // multiple different types of surfaces (climbing walls, etc)
  180. //---------------------------------------------------------
  181. #define BLOB_TRACE_HEIGHT 8.0f
  182. void CBlobElement::ModifyVelocityForSurface( float flInterval, float flSpeed )
  183. {
  184. trace_t tr;
  185. Vector vecStart = GetAbsOrigin();
  186. Vector up = Vector( 0, 0, BLOB_TRACE_HEIGHT );
  187. Vector vecWishedGoal = vecStart + (GetAbsVelocity() * flInterval);
  188. UTIL_TraceLine( vecStart + up, vecWishedGoal + up, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  189. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 0.1f );
  190. m_bOnWall = false;
  191. if( tr.fraction == 1.0f )
  192. {
  193. UTIL_TraceLine( vecWishedGoal + up, vecWishedGoal - (up * 2.0f), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  194. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 255, 0, false, 0.1f );
  195. tr.endpos.z += MOVE_HEIGHT_EPSILON;
  196. }
  197. else
  198. {
  199. //NDebugOverlay::Cross3D( GetAbsOrigin(), 16, 255, 255, 0, false, 0.025f );
  200. m_bOnWall = true;
  201. if( tr.m_pEnt != NULL && !tr.m_pEnt->IsWorld() )
  202. {
  203. IPhysicsObject *pPhysics = tr.m_pEnt->VPhysicsGetObject();
  204. if( pPhysics != NULL )
  205. {
  206. Vector vecMassCenter;
  207. Vector vecMassCenterWorld;
  208. vecMassCenter = pPhysics->GetMassCenterLocalSpace();
  209. pPhysics->LocalToWorld( &vecMassCenterWorld, vecMassCenter );
  210. if( tr.endpos.z > vecMassCenterWorld.z )
  211. {
  212. pPhysics->ApplyForceOffset( (-150.0f * m_flRandomEightyPercent) * tr.plane.normal, tr.endpos );
  213. }
  214. }
  215. }
  216. }
  217. Vector vecDir = tr.endpos - vecStart;
  218. VectorNormalize( vecDir );
  219. SetElementVelocity( vecDir * flSpeed, false );
  220. }
  221. //---------------------------------------------------------
  222. // Set velocity that will carry me towards a specified entity
  223. // Most often used to move along with the npc_blob that
  224. // is directing me.
  225. //---------------------------------------------------------
  226. void CBlobElement::MoveTowardsTargetEntity( float speed )
  227. {
  228. CBaseEntity *pTarget = m_hTargetEntity.Get();
  229. if( pTarget != NULL )
  230. {
  231. // Try to attack my target's enemy directly if I can.
  232. CBaseEntity *pTargetEnemy = pTarget->GetEnemy();
  233. if( pTargetEnemy != NULL )
  234. {
  235. pTarget = pTargetEnemy;
  236. }
  237. Vector vecDir = pTarget->WorldSpaceCenter() - GetAbsOrigin();
  238. vecDir.NormalizeInPlace();
  239. SetElementVelocity( vecDir * speed, true );
  240. }
  241. else
  242. {
  243. SetElementVelocity( vec3_origin, true );
  244. }
  245. }
  246. //---------------------------------------------------------
  247. // Set velocity that will take me towards a specified location.
  248. // This is often used to send all blob elements to specific
  249. // locations, causing the blob to appear as though it has
  250. // formed a specific shape.
  251. //---------------------------------------------------------
  252. void CBlobElement::MoveTowardsTargetLocation( float speed )
  253. {
  254. Vector vecDir = m_vecTargetLocation - GetAbsOrigin();
  255. float dist = VectorNormalize( vecDir );
  256. //!!!HACKHACK - how about a real way to tell if we've reached our goal?
  257. if( dist <= 8.0f )
  258. {
  259. SetActiveMovementRule( BLOB_MOVE_DONT_MOVE );
  260. }
  261. speed = MIN( dist, speed );
  262. SetElementVelocity( vecDir * speed, true );
  263. }
  264. //---------------------------------------------------------
  265. // Pick new random numbers for the parameters that create
  266. // variations in movement.
  267. //---------------------------------------------------------
  268. void CBlobElement::ReconfigureRandomParams()
  269. {
  270. m_flSinePhase = random->RandomFloat( 0.01f, 0.9f );
  271. m_flSineFrequency = random->RandomFloat( 10.0f, 20.0f );
  272. m_flSineAmplitude = random->RandomFloat( 0.5f, 1.5f );
  273. }
  274. //---------------------------------------------------------
  275. // Adjust velocity if this element is moving faster than
  276. // flMaxSpeed or slower than flMinSpeed
  277. //---------------------------------------------------------
  278. void CBlobElement::EnforceSpeedLimits( float flMinSpeed, float flMaxSpeed )
  279. {
  280. Vector vecVelocity = GetAbsVelocity();
  281. float flSpeed = VectorNormalize( vecVelocity );
  282. if( flSpeed > flMaxSpeed )
  283. {
  284. SetElementVelocity( vecVelocity * flMaxSpeed, true );
  285. }
  286. else if( flSpeed < flMinSpeed )
  287. {
  288. SetElementVelocity( vecVelocity * flMinSpeed, true );
  289. }
  290. }
  291. //=========================================================
  292. // Custom schedules
  293. //=========================================================
  294. enum
  295. {
  296. SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE,
  297. };
  298. //=========================================================
  299. // Custom tasks
  300. //=========================================================
  301. enum
  302. {
  303. TASK_MYCUSTOMTASK = LAST_SHARED_TASK,
  304. };
  305. //=========================================================
  306. // Custom Conditions
  307. //=========================================================
  308. enum
  309. {
  310. COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION,
  311. };
  312. //=========================================================
  313. //=========================================================
  314. class CNPC_Blob : public CAI_BaseNPC
  315. {
  316. DECLARE_CLASS( CNPC_Blob, CAI_BaseNPC );
  317. public:
  318. CNPC_Blob();
  319. void Precache( void );
  320. void Spawn( void );
  321. Class_T Classify( void );
  322. void RunAI();
  323. void GatherConditions( void );
  324. int SelectSchedule( void );
  325. int GetSoundInterests( void ) { return (SOUND_BUGBAIT); }
  326. void ComputeCentroid();
  327. void DoBlobBatchedAI( int iStart, int iEnd );
  328. int ComputeBatchSize();
  329. void AdvanceBatch();
  330. int GetBatchStart();
  331. int GetBatchEnd();
  332. CBlobElement *CreateNewElement();
  333. void InitializeElements();
  334. void RecomputeIdealElementDist();
  335. void RemoveAllElementsExcept( int iExempt );
  336. void RemoveExcessElements( int iNumElements );
  337. void AddNewElements( int iNumElements );
  338. void FormShapeFromPath( string_t iszPathName );
  339. void SetRadius( float flRadius );
  340. DECLARE_DATADESC();
  341. int m_iNumElements;
  342. bool m_bInitialized;
  343. int m_iBatchStart;
  344. Vector m_vecCentroid;
  345. float m_flMinElementDist;
  346. CUtlVector<CHandle< CBlobElement > >m_Elements;
  347. DEFINE_CUSTOM_AI;
  348. public:
  349. void InputFormPathShape( inputdata_t &inputdata );
  350. void InputSetRadius( inputdata_t &inputdata );
  351. void InputChaseEntity( inputdata_t &inputdata );
  352. void InputIsolateElement( inputdata_t &inputdata );
  353. void InputFormHemisphere( inputdata_t &inputdata );
  354. void InputFormTwoSpheres( inputdata_t &inputdata );
  355. public:
  356. Vector m_vecAvoidOrigin[ BLOB_MAX_AVOID_ORIGINS ];
  357. float m_flAvoidRadiusSqr;
  358. private:
  359. int m_iReconfigureElement;
  360. int m_iNumAvoidOrigins;
  361. bool m_bEatCombineHack;
  362. };
  363. LINK_ENTITY_TO_CLASS( npc_blob, CNPC_Blob );
  364. IMPLEMENT_CUSTOM_AI( npc_blob,CNPC_Blob );
  365. //---------------------------------------------------------
  366. // Save/Restore
  367. //---------------------------------------------------------
  368. BEGIN_DATADESC( CNPC_Blob )
  369. DEFINE_FIELD( m_iNumElements, FIELD_INTEGER ),
  370. DEFINE_FIELD( m_bInitialized, FIELD_BOOLEAN ),
  371. DEFINE_FIELD( m_iBatchStart, FIELD_INTEGER ),
  372. DEFINE_FIELD( m_vecCentroid, FIELD_POSITION_VECTOR ),
  373. DEFINE_FIELD( m_flMinElementDist, FIELD_FLOAT ),
  374. DEFINE_FIELD( m_iReconfigureElement, FIELD_INTEGER ),
  375. DEFINE_UTLVECTOR( m_Elements, FIELD_EHANDLE ),
  376. DEFINE_INPUTFUNC( FIELD_STRING, "FormPathShape", InputFormPathShape ),
  377. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetRadius", InputSetRadius ),
  378. DEFINE_INPUTFUNC( FIELD_STRING, "ChaseEntity", InputChaseEntity ),
  379. DEFINE_INPUTFUNC( FIELD_INTEGER, "IsolateElement", InputIsolateElement ),
  380. DEFINE_INPUTFUNC( FIELD_VOID, "FormHemisphere", InputFormHemisphere ),
  381. DEFINE_INPUTFUNC( FIELD_VOID, "FormTwoSpheres", InputFormTwoSpheres ),
  382. END_DATADESC()
  383. //---------------------------------------------------------
  384. //---------------------------------------------------------
  385. CNPC_Blob::CNPC_Blob()
  386. {
  387. m_iNumElements = 0;
  388. m_bInitialized = false;
  389. m_iBatchStart = 0;
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Purpose: Initialize the custom schedules
  393. // Input :
  394. // Output :
  395. //-----------------------------------------------------------------------------
  396. void CNPC_Blob::InitCustomSchedules(void)
  397. {
  398. INIT_CUSTOM_AI(CNPC_Blob);
  399. ADD_CUSTOM_TASK(CNPC_Blob, TASK_MYCUSTOMTASK);
  400. ADD_CUSTOM_SCHEDULE(CNPC_Blob, SCHED_MYCUSTOMSCHEDULE);
  401. ADD_CUSTOM_CONDITION(CNPC_Blob, COND_MYCUSTOMCONDITION);
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. //
  406. //
  407. //-----------------------------------------------------------------------------
  408. void CNPC_Blob::Precache( void )
  409. {
  410. PrecacheModel( NPC_BLOB_MODEL );
  411. UTIL_PrecacheOther( "blob_element" );
  412. BaseClass::Precache();
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose:
  416. //
  417. //
  418. //-----------------------------------------------------------------------------
  419. void CNPC_Blob::Spawn( void )
  420. {
  421. Precache();
  422. SetModel( NPC_BLOB_MODEL );
  423. SetHullType(HULL_TINY);
  424. SetHullSizeNormal();
  425. SetSolid( SOLID_NONE );
  426. AddSolidFlags( FSOLID_NOT_STANDABLE );
  427. SetMoveType( MOVETYPE_STEP );
  428. SetBloodColor( BLOOD_COLOR_RED );
  429. m_iHealth = INT_MAX;
  430. m_flFieldOfView = -1.0f;
  431. m_NPCState = NPC_STATE_NONE;
  432. CapabilitiesClear();
  433. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  434. m_Elements.RemoveAll();
  435. NPCInit();
  436. AddEffects( EF_NODRAW );
  437. m_flMinElementDist = blob_mindist.GetFloat();
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //
  442. //
  443. // Output :
  444. //-----------------------------------------------------------------------------
  445. Class_T CNPC_Blob::Classify( void )
  446. {
  447. return CLASS_PLAYER_ALLY;
  448. }
  449. //-----------------------------------------------------------------------------
  450. //-----------------------------------------------------------------------------
  451. void CNPC_Blob::RunAI()
  452. {
  453. BaseClass::RunAI();
  454. if( !m_bInitialized )
  455. {
  456. // m_bInitialized is set to false in the constructor. So this bit of
  457. // code runs one time, the first time I think.
  458. Msg("I need to initialize\n");
  459. InitializeElements();
  460. m_bInitialized = true;
  461. return;
  462. }
  463. int iIdealNumElements = blob_numelements.GetInt();
  464. if( iIdealNumElements != m_iNumElements )
  465. {
  466. int delta = iIdealNumElements - m_iNumElements;
  467. if( delta < 0 )
  468. {
  469. delta = -delta;
  470. delta = MIN(delta, 5 );
  471. RemoveExcessElements( delta );
  472. if( m_iReconfigureElement > m_iNumElements )
  473. {
  474. // Start this index over at zero, if it is past the new end of the utlvector.
  475. m_iReconfigureElement = 0;
  476. }
  477. }
  478. else
  479. {
  480. delta = MIN(delta, 5 );
  481. AddNewElements( delta );
  482. }
  483. RecomputeIdealElementDist();
  484. }
  485. ComputeCentroid();
  486. if( npc_blob_show_centroid.GetBool() )
  487. {
  488. NDebugOverlay::Cross3D( m_vecCentroid + Vector( 0, 0, 12 ), 32, 0, 255, 0, false, 0.025f );
  489. }
  490. if( npc_blob_use_threading.GetBool() )
  491. {
  492. IterRangeParallel( this, &CNPC_Blob::DoBlobBatchedAI, 0, m_Elements.Count() );
  493. }
  494. else
  495. {
  496. DoBlobBatchedAI( 0, m_Elements.Count() );
  497. }
  498. if( GetEnemy() != NULL )
  499. {
  500. float flEnemyDistSqr = m_vecCentroid.DistToSqr( GetEnemy()->GetAbsOrigin() );
  501. if( flEnemyDistSqr <= Square( 32.0f ) )
  502. {
  503. if( GetEnemy()->Classify() == CLASS_COMBINE )
  504. {
  505. if( !m_bEatCombineHack )
  506. {
  507. variant_t var;
  508. var.SetFloat( 0 );
  509. g_EventQueue.AddEvent( GetEnemy(), "HitByBugBait", 0.0f, this, this );
  510. g_EventQueue.AddEvent( GetEnemy(), "SetHealth", var, 3.0f, this, this );
  511. m_bEatCombineHack = true;
  512. blob_radius.SetValue( 48.0f );
  513. RecomputeIdealElementDist();
  514. }
  515. }
  516. else
  517. {
  518. CTakeDamageInfo info;
  519. info.SetAttacker( this );
  520. info.SetInflictor( this );
  521. info.SetDamage( 5 );
  522. info.SetDamageType( DMG_SLASH );
  523. info.SetDamageForce( Vector( 0, 0, 1 ) );
  524. GetEnemy()->TakeDamage( info );
  525. }
  526. }
  527. }
  528. SetNextThink( gpGlobals->curtime + npc_blob_think_interval.GetFloat() );
  529. }
  530. //-----------------------------------------------------------------------------
  531. //-----------------------------------------------------------------------------
  532. void CNPC_Blob::GatherConditions( void )
  533. {
  534. if( m_bEatCombineHack )
  535. {
  536. // We just ate someone.
  537. if( !GetEnemy() || !GetEnemy()->IsAlive() )
  538. {
  539. m_bEatCombineHack = false;
  540. blob_radius.SetValue( 160.0f );
  541. RecomputeIdealElementDist();
  542. }
  543. }
  544. BaseClass::GatherConditions();
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Either stand still or chase the enemy, for now.
  548. //-----------------------------------------------------------------------------
  549. int CNPC_Blob::SelectSchedule( void )
  550. {
  551. if( GetEnemy() == NULL )
  552. return SCHED_IDLE_STAND;
  553. return SCHED_CHASE_ENEMY;
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Average the origin of all elements to get the centroid for the group
  557. //-----------------------------------------------------------------------------
  558. void CNPC_Blob::ComputeCentroid()
  559. {
  560. m_vecCentroid = vec3_origin;
  561. for( int i = 0 ; i < m_Elements.Count() ; i++ )
  562. {
  563. m_vecCentroid += m_Elements[ i ]->GetAbsOrigin();
  564. }
  565. m_vecCentroid /= m_Elements.Count();
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Run all of the AI for elements within the range iStart to iEnd
  569. //-----------------------------------------------------------------------------
  570. void CNPC_Blob::DoBlobBatchedAI( int iStart, int iEnd )
  571. {
  572. float flInterval = gpGlobals->curtime - GetLastThink();
  573. // Local fields for sin-wave movement variance
  574. float flMySine;
  575. float flAmplitude = npc_blob_sin_amplitude.GetFloat();
  576. float flMyAmplitude;
  577. Vector vecRight;
  578. Vector vecForward;
  579. // Local fields for attract/repel
  580. float minDistSqr = Square( m_flMinElementDist );
  581. float flBlobSpeed = blob_element_speed.GetFloat();
  582. float flSpeed;
  583. // Local fields for speed limiting
  584. float flMinSpeed = blob_element_speed.GetFloat() * 0.5f;
  585. float flMaxSpeed = blob_element_speed.GetFloat() * 1.5f;
  586. bool bEnforceSpeedLimit;
  587. bool bEnforceRelativePositions;
  588. bool bDoMovementVariation;
  589. bool bDoOrientation = npc_blob_use_orientation.GetBool();
  590. float flIdleSpeedFactor = npc_blob_idle_speed_factor.GetFloat();
  591. // Group cohesion
  592. float flBlobRadiusSqr = Square( blob_radius.GetFloat() + 48.0f ); // Four feet of fudge
  593. // Build a right-hand vector along which we'll add some sine wave data to give each
  594. // element a unique insect-like undulation along an axis perpendicular to their path,
  595. // which makes the entire group look far less orderly
  596. if( GetEnemy() != NULL )
  597. {
  598. // If I have an enemy, the right-hand vector is perpendicular to a straight line
  599. // from the group's centroid to the enemy's origin.
  600. vecForward = GetEnemy()->GetAbsOrigin() - m_vecCentroid;
  601. VectorNormalize( vecForward );
  602. vecRight.x = vecForward.y;
  603. vecRight.y = -vecForward.x;
  604. }
  605. else
  606. {
  607. // If there is no enemy, wobble along the axis from the centroid to me.
  608. vecForward = GetAbsOrigin() - m_vecCentroid;
  609. VectorNormalize( vecForward );
  610. vecRight.x = vecForward.y;
  611. vecRight.y = -vecForward.x;
  612. }
  613. //--
  614. // MAIN LOOP - Run all of the elements in the set iStart to iEnd
  615. //--
  616. for( int i = iStart ; i < iEnd ; i++ )
  617. {
  618. CBlobElement *pThisElement = m_Elements[ i ];
  619. //--
  620. // Initial movement
  621. //--
  622. // Start out with bEnforceSpeedLimit set to false. This is because an element
  623. // can't overspeed if it's moving undisturbed towards its target entity or
  624. // target location. An element can only under or overspeed when it is repelled
  625. // by multiple other elements in the group. See "Relative Positions" below.
  626. //
  627. // Initialize some 'defaults' that may be changed for each iteration of this loop
  628. bEnforceSpeedLimit = false;
  629. bEnforceRelativePositions = true;
  630. bDoMovementVariation = true;
  631. flSpeed = flBlobSpeed;
  632. switch( pThisElement->GetActiveMovementRule() )
  633. {
  634. case BLOB_MOVE_DONT_MOVE:
  635. {
  636. pThisElement->SetElementVelocity( vec3_origin, true );
  637. trace_t tr;
  638. Vector vecOrigin = pThisElement->GetAbsOrigin();
  639. UTIL_TraceLine( vecOrigin, vecOrigin - Vector( 0, 0, 16), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  640. if( tr.fraction < 1.0f )
  641. {
  642. QAngle angles;
  643. VectorAngles( tr.plane.normal, angles );
  644. float flSwap = angles.x;
  645. angles.x = -angles.y;
  646. angles.y = flSwap;
  647. pThisElement->SetAbsAngles( angles );
  648. }
  649. }
  650. continue;
  651. break;
  652. case BLOB_MOVE_TO_TARGET_LOCATION:
  653. {
  654. Vector vecDiff = pThisElement->GetAbsOrigin() - pThisElement->m_vecTargetLocation;
  655. if( vecDiff.Length2DSqr() <= Square(80.0f) )
  656. {
  657. // Don't shove this guy around any more, let him get to his goal position.
  658. flSpeed *= 0.5f;
  659. bEnforceRelativePositions = false;
  660. bDoMovementVariation = false;
  661. }
  662. pThisElement->MoveTowardsTargetLocation( flSpeed );
  663. }
  664. break;
  665. case BLOB_MOVE_TO_TARGET_ENTITY:
  666. {
  667. if( !IsMoving() && GetEnemy() == NULL )
  668. {
  669. if( pThisElement->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) <= flBlobRadiusSqr )
  670. {
  671. flSpeed = (flSpeed * flIdleSpeedFactor) * pThisElement->m_flRandomEightyPercent;
  672. }
  673. }
  674. pThisElement->MoveTowardsTargetEntity( flSpeed );
  675. }
  676. break;
  677. default:
  678. Msg("ERROR: Blob Element with unspecified Movement Rule\n");
  679. break;
  680. }
  681. //---
  682. // Relative positions
  683. //--
  684. // Check this element against ALL other elements. If the two elements are closer
  685. // than the allowed minimum distance, repel this element away. (The other element
  686. // will repel when its AI runs). A single element can be repelled by many other
  687. // elements. This is why bEnforceSpeedLimit is set to true if any of the repelling
  688. // code runs for this element. Multiple attempts to repel an element in the same
  689. // direction will cause overspeed. Conflicting attempts to repel an element in opposite
  690. // directions will cause underspeed.
  691. Vector vecDir = Vector( 0, 0, 0 );
  692. Vector vecThisElementOrigin = pThisElement->GetAbsOrigin();
  693. if( bEnforceRelativePositions )
  694. {
  695. for( int j = 0 ; j < m_Elements.Count() ; j++ )
  696. {
  697. // This is the innermost loop! We should optimize here, if anywhere.
  698. // If this element is on the wall, then don't be repelled by anyone. Repelling
  699. // elements that are trying to climb a wall usually make them look like they
  700. // fall off the wall a few times while climbing.
  701. if( pThisElement->m_bOnWall )
  702. continue;
  703. CBlobElement *pThatElement = m_Elements[ j ];
  704. if( i != j )
  705. {
  706. Vector vecThatElementOrigin = pThatElement->GetAbsOrigin();
  707. float distSqr = vecThisElementOrigin.DistToSqr( vecThatElementOrigin );
  708. if( distSqr < minDistSqr )
  709. {
  710. // Too close to the other element. Move away.
  711. float flRepelSpeed;
  712. Vector vecRepelDir = ( vecThisElementOrigin - vecThatElementOrigin );
  713. vecRepelDir.NormalizeInPlace();
  714. flRepelSpeed = (flSpeed * ( 1.0f - ( distSqr / minDistSqr ) ) ) * pThatElement->GetSinePhase();
  715. pThisElement->AddElementVelocity( vecRepelDir * flRepelSpeed, true );
  716. // Since we altered this element's velocity after it was initially set, there's a chance
  717. // that the sums of multiple vectors will cause the element to over or underspeed, so
  718. // mark it for speed limit enforcement
  719. bEnforceSpeedLimit = true;
  720. }
  721. }
  722. }
  723. }
  724. //--
  725. // Movement variation
  726. //--
  727. if( bDoMovementVariation )
  728. {
  729. flMySine = sin( gpGlobals->curtime * pThisElement->GetSineFrequency() );
  730. flMyAmplitude = flAmplitude * pThisElement->GetSineAmplitude();
  731. pThisElement->AddElementVelocity( vecRight * (flMySine * flMyAmplitude), true );
  732. }
  733. // Avoidance
  734. for( int a = 0 ; a < m_iNumAvoidOrigins ; a++ )
  735. {
  736. Vector vecAvoidDir = pThisElement->GetAbsOrigin() - m_vecAvoidOrigin[ a ];
  737. if( vecAvoidDir.LengthSqr() <= (m_flAvoidRadiusSqr * pThisElement->m_flRandomEightyPercent) )
  738. {
  739. VectorNormalize( vecAvoidDir );
  740. pThisElement->AddElementVelocity( vecAvoidDir * (flSpeed * 2.0f), true );
  741. break;
  742. }
  743. }
  744. //--
  745. // Speed limits
  746. //---
  747. if( bEnforceSpeedLimit == true )
  748. {
  749. pThisElement->EnforceSpeedLimits( flMinSpeed, flMaxSpeed );
  750. }
  751. //--
  752. // Wall crawling
  753. //--
  754. pThisElement->ModifyVelocityForSurface( flInterval, flSpeed );
  755. // For identifying stuck elements.
  756. pThisElement->m_vecPrevOrigin = pThisElement->GetAbsOrigin();
  757. pThisElement->m_flDistFromCentroidSqr = pThisElement->m_vecPrevOrigin.DistToSqr( m_vecCentroid );
  758. // Orientation
  759. if( bDoOrientation )
  760. {
  761. QAngle angles;
  762. VectorAngles( pThisElement->GetAbsVelocity(), angles );
  763. pThisElement->SetAbsAngles( angles );
  764. }
  765. /*
  766. //--
  767. // Stragglers/Group integrity
  768. //
  769. if( pThisElement->m_flDistFromCentroidSqr > flStragglerDistSqr )
  770. {
  771. NDebugOverlay::Line( pThisElement->GetAbsOrigin(), m_vecCentroid, 255, 0, 0, false, 0.025f );
  772. }
  773. */
  774. }
  775. }
  776. //-----------------------------------------------------------------------------
  777. // Throw out all elements and their entities except for the the specified
  778. // index into the UTILVector. This is useful for isolating elements that
  779. // get into a bad state.
  780. //-----------------------------------------------------------------------------
  781. void CNPC_Blob::RemoveAllElementsExcept( int iExempt )
  782. {
  783. if( m_Elements.Count() == 1 )
  784. return;
  785. m_Elements[ 0 ].Set( m_Elements[ iExempt ].Get() );
  786. for( int i = 1 ; i < m_Elements.Count() ; i++ )
  787. {
  788. if( i != iExempt )
  789. {
  790. m_Elements[ i ]->SUB_Remove();
  791. }
  792. }
  793. m_Elements.RemoveMultiple( 1, m_Elements.Count() - 1 );
  794. m_iNumElements = 1;
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Purpose: The blob has too many elements. Locate good candidates and remove
  798. // this many elements.
  799. //-----------------------------------------------------------------------------
  800. void CNPC_Blob::RemoveExcessElements( int iNumElements )
  801. {
  802. // For now we're not assessing candidates, just blindly removing.
  803. int i;
  804. for( i = 0 ; i < iNumElements ; i++ )
  805. {
  806. int iLastElement = m_iNumElements - 1;
  807. // Nuke the associated entity
  808. m_Elements[ iLastElement ]->SUB_Remove();
  809. m_Elements.Remove( iLastElement );
  810. m_iNumElements--;
  811. }
  812. }
  813. //-----------------------------------------------------------------------------
  814. // Purpose: This blob has too few elements. Add this many elements by stacking
  815. // them on top of existing elements and allowing them to disperse themselves
  816. // into the blob.
  817. //-----------------------------------------------------------------------------
  818. void CNPC_Blob::AddNewElements( int iNumElements )
  819. {
  820. int i;
  821. // Keep track of how many elements we had when we came into this function.
  822. // Since the new elements copy their origins from existing elements, we only want
  823. // to copy origins from elements that existed before we came into this function.
  824. // Otherwise, the more elements we create while in this function, the more likely it
  825. // becomes that several of them will stack on the same origin.
  826. int iInitialElements = m_iNumElements;
  827. for( i = 0 ; i < iNumElements ; i++ )
  828. {
  829. CBlobElement *pElement = CreateNewElement();
  830. if( pElement != NULL )
  831. {
  832. // Copy the origin of some element that is not me. This will make the expansion
  833. // of the group easier on the eye, since this element will spawn inside of some
  834. // other element, and then be pushed out by the blob's repel rules.
  835. int iCopyElement = random->RandomInt( 0, iInitialElements - 1 );
  836. pElement->SetAbsOrigin( m_Elements[iCopyElement]->GetAbsOrigin() );
  837. }
  838. }
  839. }
  840. //-----------------------------------------------------------------------------
  841. //-----------------------------------------------------------------------------
  842. #define BLOB_MAX_VERTS 128
  843. void CNPC_Blob::FormShapeFromPath( string_t iszPathName )
  844. {
  845. Vector vertex[ BLOB_MAX_VERTS ];
  846. int i;
  847. int iNumVerts = 0;
  848. for ( i = 0 ; i < BLOB_MAX_VERTS ; i++ )
  849. {
  850. if( iszPathName == NULL_STRING )
  851. {
  852. //Msg("Terminal path\n");
  853. break;
  854. }
  855. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, iszPathName );
  856. if( pEntity != NULL )
  857. {
  858. bool bClosedPath = false;
  859. for( int j = 0 ; j < i ; j++ )
  860. {
  861. // Stop if we reach a vertex that's already in the array (closed path)
  862. if( vertex[ j ] == pEntity->GetAbsOrigin() )
  863. {
  864. //Msg("Closed path!\n");
  865. bClosedPath = true;
  866. break;
  867. }
  868. }
  869. vertex[ i ] = pEntity->GetAbsOrigin();
  870. iszPathName = pEntity->m_target;
  871. iNumVerts++;
  872. if( bClosedPath )
  873. break;
  874. }
  875. }
  876. //Msg("%d verts found in path!\n", iNumVerts);
  877. float flPathLength = 0.0f;
  878. float flDistribution;
  879. for( i = 0 ; i < iNumVerts - 1 ; i++ )
  880. {
  881. Vector vecDiff = vertex[ i ] - vertex[ i + 1 ];
  882. flPathLength += vecDiff.Length();
  883. }
  884. flDistribution = flPathLength / m_iNumElements;
  885. Msg("Path length is %f, distribution is %f\n", flPathLength, flDistribution );
  886. int element = 0;
  887. for( i = 0 ; i < iNumVerts - 1 ; i++ )
  888. {
  889. //NDebugOverlay::Line( vertex[ i ], vertex[ i + 1 ], 0, 255, 0, false, 10.0f );
  890. Vector vecDiff = vertex[ i + 1 ] - vertex[ i ];
  891. Vector vecStart = vertex[ i ];
  892. float flSegmentLength = VectorNormalize( vecDiff );
  893. float flStep;
  894. for( flStep = 0.0f ; flStep < flSegmentLength ; flStep += flDistribution )
  895. {
  896. //NDebugOverlay::Cross3D( vecStart + vecDiff * flStep, 16, 255, 255, 255, false, 10.0f );
  897. m_Elements[ element ]->SetTargetLocation( vecStart + vecDiff * flStep );
  898. m_Elements[ element ]->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_LOCATION );
  899. element++;
  900. if( element == m_iNumElements )
  901. return;
  902. }
  903. }
  904. }
  905. //-----------------------------------------------------------------------------
  906. //-----------------------------------------------------------------------------
  907. void CNPC_Blob::SetRadius( float flRadius )
  908. {
  909. blob_radius.SetValue( flRadius );
  910. RecomputeIdealElementDist();
  911. }
  912. //-----------------------------------------------------------------------------
  913. //-----------------------------------------------------------------------------
  914. void CNPC_Blob::InputFormPathShape( inputdata_t &inputdata )
  915. {
  916. string_t shape = inputdata.value.StringID();
  917. if( shape == NULL_STRING )
  918. return;
  919. //Msg("I'm supposed to form some shape called:%s\n", shape );
  920. FormShapeFromPath( shape );
  921. }
  922. //-----------------------------------------------------------------------------
  923. //-----------------------------------------------------------------------------
  924. void CNPC_Blob::InputSetRadius( inputdata_t &inputdata )
  925. {
  926. float flNewRadius = inputdata.value.Float();
  927. SetRadius( flNewRadius );
  928. }
  929. //-----------------------------------------------------------------------------
  930. //-----------------------------------------------------------------------------
  931. void CNPC_Blob::InputChaseEntity( inputdata_t &inputdata )
  932. {
  933. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.StringID(), NULL, inputdata.pActivator, inputdata.pCaller );
  934. if ( pEntity )
  935. {
  936. for( int i = 0 ; i < m_Elements.Count() ; i++ )
  937. {
  938. CBlobElement *pElement = m_Elements[ i ];
  939. pElement->SetTargetEntity( pEntity );
  940. pElement->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_ENTITY );
  941. }
  942. }
  943. }
  944. //-----------------------------------------------------------------------------
  945. //-----------------------------------------------------------------------------
  946. void CNPC_Blob::InputIsolateElement( inputdata_t &inputdata )
  947. {
  948. int iElement = inputdata.value.Int();
  949. RemoveAllElementsExcept( iElement );
  950. }
  951. //-----------------------------------------------------------------------------
  952. //-----------------------------------------------------------------------------
  953. void CNPC_Blob::InputFormHemisphere( inputdata_t &inputdata )
  954. {
  955. Vector center = GetAbsOrigin();
  956. const float flRadius = 240.0f;
  957. Vector vecDir;
  958. for( int i = 0 ; i < m_Elements.Count() ; i++ )
  959. {
  960. CBlobElement *pElement = m_Elements[ i ];
  961. // Compute a point around my center
  962. vecDir.x = random->RandomFloat( -1, 1 );
  963. vecDir.y = random->RandomFloat( -1, 1 );
  964. vecDir.z = random->RandomFloat( 0, 1 );
  965. VectorNormalize( vecDir );
  966. pElement->SetTargetLocation( center + vecDir * flRadius );
  967. pElement->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_LOCATION );
  968. }
  969. }
  970. //-----------------------------------------------------------------------------
  971. //-----------------------------------------------------------------------------
  972. void CNPC_Blob::InputFormTwoSpheres( inputdata_t &inputdata )
  973. {
  974. Vector center = GetAbsOrigin();
  975. Vector sphere1 = GetAbsOrigin() + Vector( 120.0f, 0, 120.0f );
  976. Vector sphere2 = GetAbsOrigin() + Vector( -120.0f, 0, 120.0f );
  977. const float flRadius = 100.0f;
  978. Vector vecDir;
  979. int batchSize = m_Elements.Count() / 2;
  980. for( int i = 0 ; i < batchSize ; i++ )
  981. {
  982. CBlobElement *pElement = m_Elements[ i ];
  983. // Compute a point around my center
  984. vecDir.x = random->RandomFloat( -1, 1 );
  985. vecDir.y = random->RandomFloat( -1, 1 );
  986. vecDir.z = random->RandomFloat( -1, 1 );
  987. VectorNormalize( vecDir );
  988. pElement->SetTargetLocation( sphere1 + vecDir * flRadius );
  989. pElement->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_LOCATION );
  990. }
  991. for( int i = batchSize ; i < m_Elements.Count() ; i++ )
  992. {
  993. CBlobElement *pElement = m_Elements[ i ];
  994. // Compute a point around my center
  995. vecDir.x = random->RandomFloat( -1, 1 );
  996. vecDir.y = random->RandomFloat( -1, 1 );
  997. vecDir.z = random->RandomFloat( -1, 1 );
  998. VectorNormalize( vecDir );
  999. pElement->SetTargetLocation( sphere2 + vecDir * flRadius );
  1000. pElement->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_LOCATION );
  1001. }
  1002. }
  1003. //-----------------------------------------------------------------------------
  1004. // Get the index of the element to start processing with for this batch.
  1005. //-----------------------------------------------------------------------------
  1006. int CNPC_Blob::GetBatchStart()
  1007. {
  1008. return m_iBatchStart;
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Get the index of the element to stop processing with for this batch.
  1012. //-----------------------------------------------------------------------------
  1013. int CNPC_Blob::GetBatchEnd()
  1014. {
  1015. int batchDone = m_iBatchStart + ComputeBatchSize();
  1016. batchDone = MIN( batchDone, m_Elements.Count() );
  1017. return batchDone;
  1018. }
  1019. //-----------------------------------------------------------------------------
  1020. //-----------------------------------------------------------------------------
  1021. int CNPC_Blob::ComputeBatchSize()
  1022. {
  1023. int batchSize = m_Elements.Count() / ( 100 / blob_batchpercent.GetInt() );
  1024. return batchSize;
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. //-----------------------------------------------------------------------------
  1028. void CNPC_Blob::AdvanceBatch()
  1029. {
  1030. m_iBatchStart += ComputeBatchSize();
  1031. if( m_iBatchStart >= m_Elements.Count() )
  1032. m_iBatchStart = 0;
  1033. }
  1034. //-----------------------------------------------------------------------------
  1035. // Creates a new blob element from scratch and adds it to the blob
  1036. //-----------------------------------------------------------------------------
  1037. CBlobElement *CNPC_Blob::CreateNewElement()
  1038. {
  1039. CBlobElement *pElement = static_cast<CBlobElement*>(CreateEntityByName( "blob_element" ));
  1040. if( pElement != NULL )
  1041. {
  1042. pElement->SetOwnerEntity( this );
  1043. pElement->SetSinePhase( fabs( sin(((float)m_iNumElements)/10.0f) ) );
  1044. pElement->SetActiveMovementRule( BLOB_MOVE_TO_TARGET_ENTITY );
  1045. pElement->SetTargetEntity( this );
  1046. pElement->m_iElementNumber = m_iNumElements;
  1047. m_iNumElements++;
  1048. pElement->Spawn();
  1049. m_Elements.AddToTail( pElement );
  1050. return pElement;
  1051. }
  1052. Warning("Blob could not spawn new element!\n");
  1053. return NULL;
  1054. }
  1055. //-----------------------------------------------------------------------------
  1056. // Create, initialize, and distribute all blob elements
  1057. //-----------------------------------------------------------------------------
  1058. void CNPC_Blob::InitializeElements()
  1059. {
  1060. // Squirt all of the elements out into a circle
  1061. int i;
  1062. QAngle angDistributor( 0, 0, 0 );
  1063. int iNumElements = blob_numelements.GetInt();
  1064. float step = 360.0f / ((float)iNumElements);
  1065. for( i = 0 ; i < iNumElements ; i++ )
  1066. {
  1067. Vector vecDir;
  1068. Vector vecDest;
  1069. AngleVectors( angDistributor, &vecDir, NULL, NULL );
  1070. vecDest = WorldSpaceCenter() + vecDir * 64.0f;
  1071. CBlobElement *pElement = CreateNewElement();
  1072. if( !pElement )
  1073. {
  1074. Msg("Blob could not create all elements!!\n");
  1075. return;
  1076. }
  1077. trace_t tr;
  1078. UTIL_TraceLine( vecDest, vecDest + Vector (0, 0, MIN_COORD_FLOAT), MASK_SHOT, pElement, COLLISION_GROUP_NONE, &tr );
  1079. pElement->SetAbsOrigin( tr.endpos + Vector( 0, 0, 1 ) );
  1080. angDistributor.y += step;
  1081. }
  1082. CBaseEntity *pEntity = gEntList.FindEntityByClassname( NULL, "info_target" );
  1083. for( i = 0 ; i < BLOB_MAX_AVOID_ORIGINS ; i++ )
  1084. {
  1085. if( pEntity )
  1086. {
  1087. if( pEntity->NameMatches("avoid") )
  1088. {
  1089. m_vecAvoidOrigin[ i ] = pEntity->GetAbsOrigin();
  1090. m_flAvoidRadiusSqr = Square( 120.0f );
  1091. m_iNumAvoidOrigins++;
  1092. }
  1093. pEntity = gEntList.FindEntityByClassname( pEntity, "info_target" );
  1094. }
  1095. else
  1096. {
  1097. break;
  1098. }
  1099. }
  1100. Msg("%d avoid origins\n", m_iNumAvoidOrigins );
  1101. RecomputeIdealElementDist();
  1102. }
  1103. //-----------------------------------------------------------------------------
  1104. //-----------------------------------------------------------------------------
  1105. void CNPC_Blob::RecomputeIdealElementDist()
  1106. {
  1107. float radius = blob_radius.GetFloat();
  1108. float area = M_PI * Square(radius);
  1109. //Msg("Area of blob is: %f\n", area );
  1110. //m_flMinElementDist = 2.75f * sqrt( area / m_iNumElements );
  1111. m_flMinElementDist = M_PI * sqrt( area / m_iNumElements );
  1112. //Msg("New element dist: %f\n", m_flMinElementDist );
  1113. }