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.

2051 lines
58 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Advisors. Large sluglike aliens with creepy psychic powers!
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "game.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_schedule.h"
  10. #include "ai_hull.h"
  11. #include "ai_hint.h"
  12. #include "ai_motor.h"
  13. #include "ai_navigator.h"
  14. #include "beam_shared.h"
  15. #include "hl2_shareddefs.h"
  16. #include "ai_route.h"
  17. #include "npcevent.h"
  18. #include "gib.h"
  19. #include "ai_interactions.h"
  20. #include "ndebugoverlay.h"
  21. #include "physics_saverestore.h"
  22. #include "saverestore_utlvector.h"
  23. #include "soundent.h"
  24. #include "vstdlib/random.h"
  25. #include "engine/IEngineSound.h"
  26. #include "movevars_shared.h"
  27. #include "particle_parse.h"
  28. #include "weapon_physcannon.h"
  29. // #include "mathlib/noise.h"
  30. // this file contains the definitions for the message ID constants (eg ADVISOR_MSG_START_BEAM etc)
  31. #include "npc_advisor_shared.h"
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. //
  35. // Custom activities.
  36. //
  37. //
  38. // Skill settings.
  39. //
  40. ConVar sk_advisor_health( "sk_advisor_health", "0" );
  41. ConVar advisor_use_impact_table("advisor_use_impact_table","1",FCVAR_NONE,"If true, advisor will use her custom impact damage table.");
  42. #if NPC_ADVISOR_HAS_BEHAVIOR
  43. ConVar advisor_throw_velocity( "advisor_throw_velocity", "1100" );
  44. ConVar advisor_throw_rate( "advisor_throw_rate", "4" ); // Throw an object every 4 seconds.
  45. ConVar advisor_throw_warn_time( "advisor_throw_warn_time", "1.0" ); // Warn players one second before throwing an object.
  46. ConVar advisor_throw_lead_prefetch_time ( "advisor_throw_lead_prefetch_time", "0.66", FCVAR_NONE, "Save off the player's velocity this many seconds before throwing.");
  47. ConVar advisor_throw_stage_distance("advisor_throw_stage_distance","180.0",FCVAR_NONE,"Advisor will try to hold an object this far in front of him just before throwing it at you. Small values will clobber the shield and be very bad.");
  48. // ConVar advisor_staging_num("advisor_staging_num","1",FCVAR_NONE,"Advisor will queue up this many objects to throw at Gordon.");
  49. ConVar advisor_throw_clearout_vel("advisor_throw_clearout_vel","200",FCVAR_NONE,"TEMP: velocity with which advisor clears things out of a throwable's way");
  50. // ConVar advisor_staging_duration("
  51. // how long it will take an object to get hauled to the staging point
  52. #define STAGING_OBJECT_FALLOFF_TIME 0.15f
  53. #endif
  54. //
  55. // Spawnflags.
  56. //
  57. //
  58. // Animation events.
  59. //
  60. #if NPC_ADVISOR_HAS_BEHAVIOR
  61. //
  62. // Custom schedules.
  63. //
  64. enum
  65. {
  66. SCHED_ADVISOR_COMBAT = LAST_SHARED_SCHEDULE,
  67. SCHED_ADVISOR_IDLE_STAND,
  68. SCHED_ADVISOR_TOSS_PLAYER
  69. };
  70. //
  71. // Custom tasks.
  72. //
  73. enum
  74. {
  75. TASK_ADVISOR_FIND_OBJECTS = LAST_SHARED_TASK,
  76. TASK_ADVISOR_LEVITATE_OBJECTS,
  77. TASK_ADVISOR_STAGE_OBJECTS,
  78. TASK_ADVISOR_BARRAGE_OBJECTS,
  79. TASK_ADVISOR_PIN_PLAYER,
  80. };
  81. //
  82. // Custom conditions.
  83. //
  84. enum
  85. {
  86. COND_ADVISOR_PHASE_INTERRUPT = LAST_SHARED_CONDITION,
  87. };
  88. #endif
  89. class CNPC_Advisor;
  90. //-----------------------------------------------------------------------------
  91. //-----------------------------------------------------------------------------
  92. class CAdvisorLevitate : public IMotionEvent
  93. {
  94. DECLARE_SIMPLE_DATADESC();
  95. public:
  96. // in the absence of goal entities, we float up before throwing and down after
  97. inline bool OldStyle( void )
  98. {
  99. return !(m_vecGoalPos1.IsValid() && m_vecGoalPos2.IsValid());
  100. }
  101. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  102. EHANDLE m_Advisor; ///< handle to the advisor.
  103. Vector m_vecGoalPos1;
  104. Vector m_vecGoalPos2;
  105. float m_flFloat;
  106. };
  107. BEGIN_SIMPLE_DATADESC( CAdvisorLevitate )
  108. DEFINE_FIELD( m_flFloat, FIELD_FLOAT ),
  109. DEFINE_FIELD( m_vecGoalPos1, FIELD_POSITION_VECTOR ),
  110. DEFINE_FIELD( m_vecGoalPos2, FIELD_POSITION_VECTOR ),
  111. DEFINE_FIELD( m_Advisor, FIELD_EHANDLE ),
  112. END_DATADESC()
  113. //-----------------------------------------------------------------------------
  114. // The advisor class.
  115. //-----------------------------------------------------------------------------
  116. class CNPC_Advisor : public CAI_BaseNPC
  117. {
  118. DECLARE_CLASS( CNPC_Advisor, CAI_BaseNPC );
  119. #if NPC_ADVISOR_HAS_BEHAVIOR
  120. DECLARE_SERVERCLASS();
  121. #endif
  122. public:
  123. //
  124. // CBaseEntity:
  125. //
  126. virtual void Activate();
  127. virtual void Spawn();
  128. virtual void Precache();
  129. virtual void OnRestore();
  130. virtual void UpdateOnRemove();
  131. virtual int DrawDebugTextOverlays();
  132. //
  133. // CAI_BaseNPC:
  134. //
  135. virtual float MaxYawSpeed() { return 120.0f; }
  136. virtual Class_T Classify();
  137. #if NPC_ADVISOR_HAS_BEHAVIOR
  138. virtual int GetSoundInterests();
  139. virtual int SelectSchedule();
  140. virtual void StartTask( const Task_t *pTask );
  141. virtual void RunTask( const Task_t *pTask );
  142. virtual void OnScheduleChange( void );
  143. #endif
  144. virtual void PainSound( const CTakeDamageInfo &info );
  145. virtual void DeathSound( const CTakeDamageInfo &info );
  146. virtual void IdleSound();
  147. virtual void AlertSound();
  148. #if NPC_ADVISOR_HAS_BEHAVIOR
  149. virtual bool QueryHearSound( CSound *pSound );
  150. virtual void GatherConditions( void );
  151. /// true iff I recently threw the given object (not so fast)
  152. bool DidThrow(const CBaseEntity *pEnt);
  153. #else
  154. inline bool DidThrow(const CBaseEntity *pEnt) { return false; }
  155. #endif
  156. virtual bool IsHeavyDamage( const CTakeDamageInfo &info );
  157. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  158. virtual const impactdamagetable_t &GetPhysicsImpactDamageTable( void );
  159. COutputInt m_OnHealthIsNow;
  160. #if NPC_ADVISOR_HAS_BEHAVIOR
  161. DEFINE_CUSTOM_AI;
  162. void InputSetThrowRate( inputdata_t &inputdata );
  163. void InputWrenchImmediate( inputdata_t &inputdata ); ///< immediately wrench an object into the air
  164. void InputSetStagingNum( inputdata_t &inputdata );
  165. void InputPinPlayer( inputdata_t &inputdata );
  166. void InputTurnBeamOn( inputdata_t &inputdata );
  167. void InputTurnBeamOff( inputdata_t &inputdata );
  168. void InputElightOn( inputdata_t &inputdata );
  169. void InputElightOff( inputdata_t &inputdata );
  170. COutputEvent m_OnPickingThrowable, m_OnThrowWarn, m_OnThrow;
  171. enum { kMaxThrownObjectsTracked = 4 };
  172. #endif
  173. DECLARE_DATADESC();
  174. protected:
  175. #if NPC_ADVISOR_HAS_BEHAVIOR
  176. Vector GetThrowFromPos( CBaseEntity *pEnt ); ///< Get the position in which we shall hold an object prior to throwing it
  177. #endif
  178. bool CanLevitateEntity( CBaseEntity *pEntity, int minMass, int maxMass );
  179. void StartLevitatingObjects( void );
  180. #if NPC_ADVISOR_HAS_BEHAVIOR
  181. // void PurgeThrownObjects(); ///< clean out the recently thrown objects array
  182. void AddToThrownObjects(CBaseEntity *pEnt); ///< add to the recently thrown objects array
  183. void HurlObjectAtPlayer( CBaseEntity *pEnt, const Vector &leadVel );
  184. void PullObjectToStaging( CBaseEntity *pEnt, const Vector &stagingPos );
  185. CBaseEntity *ThrowObjectPrepare( void );
  186. CBaseEntity *PickThrowable( bool bRequireInView ); ///< choose an object to throw at the player (so it can get stuffed in the handle array)
  187. /// push everything out of the way between an object I'm about to throw and the player.
  188. void PreHurlClearTheWay( CBaseEntity *pThrowable, const Vector &toPos );
  189. #endif
  190. CUtlVector<EHANDLE> m_physicsObjects;
  191. IPhysicsMotionController *m_pLevitateController;
  192. CAdvisorLevitate m_levitateCallback;
  193. EHANDLE m_hLevitateGoal1;
  194. EHANDLE m_hLevitateGoal2;
  195. EHANDLE m_hLevitationArea;
  196. #if NPC_ADVISOR_HAS_BEHAVIOR
  197. // EHANDLE m_hThrowEnt;
  198. CUtlVector<EHANDLE> m_hvStagedEnts;
  199. CUtlVector<EHANDLE> m_hvStagingPositions;
  200. // todo: write accessor functions for m_hvStagedEnts so that it doesn't have members added and removed willy nilly throughout
  201. // code (will make the networking below more reliable)
  202. void Write_BeamOn( CBaseEntity *pEnt ); ///< write a message turning a beam on
  203. void Write_BeamOff( CBaseEntity *pEnt ); ///< write a message turning a beam off
  204. void Write_AllBeamsOff( void ); ///< tell client to kill all beams
  205. // for the pin-the-player-to-something behavior
  206. EHANDLE m_hPlayerPinPos;
  207. float m_playerPinFailsafeTime;
  208. // keep track of up to four objects after we have thrown them, to prevent oscillation or levitation of recently thrown ammo.
  209. EHANDLE m_haRecentlyThrownObjects[kMaxThrownObjectsTracked];
  210. float m_flaRecentlyThrownObjectTimes[kMaxThrownObjectsTracked];
  211. #endif
  212. string_t m_iszLevitateGoal1;
  213. string_t m_iszLevitateGoal2;
  214. string_t m_iszLevitationArea;
  215. #if NPC_ADVISOR_HAS_BEHAVIOR
  216. string_t m_iszStagingEntities;
  217. string_t m_iszPriorityEntityGroupName;
  218. float m_flStagingEnd;
  219. float m_flThrowPhysicsTime;
  220. float m_flLastThrowTime;
  221. float m_flLastPlayerAttackTime; ///< last time the player attacked something.
  222. int m_iStagingNum; ///< number of objects advisor stages at once
  223. bool m_bWasScripting;
  224. // unsigned char m_pickFailures; // the number of times we have tried to pick a throwable and failed
  225. Vector m_vSavedLeadVel; ///< save off player velocity for leading a bit before actually pelting them.
  226. #endif
  227. };
  228. LINK_ENTITY_TO_CLASS( npc_advisor, CNPC_Advisor );
  229. BEGIN_DATADESC( CNPC_Advisor )
  230. DEFINE_KEYFIELD( m_iszLevitateGoal1, FIELD_STRING, "levitategoal_bottom" ),
  231. DEFINE_KEYFIELD( m_iszLevitateGoal2, FIELD_STRING, "levitategoal_top" ),
  232. DEFINE_KEYFIELD( m_iszLevitationArea, FIELD_STRING, "levitationarea"), ///< we will float all the objects in this volume
  233. DEFINE_PHYSPTR( m_pLevitateController ),
  234. DEFINE_EMBEDDED( m_levitateCallback ),
  235. DEFINE_UTLVECTOR( m_physicsObjects, FIELD_EHANDLE ),
  236. DEFINE_FIELD( m_hLevitateGoal1, FIELD_EHANDLE ),
  237. DEFINE_FIELD( m_hLevitateGoal2, FIELD_EHANDLE ),
  238. DEFINE_FIELD( m_hLevitationArea, FIELD_EHANDLE ),
  239. #if NPC_ADVISOR_HAS_BEHAVIOR
  240. DEFINE_KEYFIELD( m_iszStagingEntities, FIELD_STRING, "staging_ent_names"), ///< entities named this constitute the positions to which we stage objects to be thrown
  241. DEFINE_KEYFIELD( m_iszPriorityEntityGroupName, FIELD_STRING, "priority_grab_name"),
  242. DEFINE_UTLVECTOR( m_hvStagedEnts, FIELD_EHANDLE ),
  243. DEFINE_UTLVECTOR( m_hvStagingPositions, FIELD_EHANDLE ),
  244. DEFINE_ARRAY( m_haRecentlyThrownObjects, FIELD_EHANDLE, CNPC_Advisor::kMaxThrownObjectsTracked ),
  245. DEFINE_ARRAY( m_flaRecentlyThrownObjectTimes, FIELD_TIME, CNPC_Advisor::kMaxThrownObjectsTracked ),
  246. DEFINE_FIELD( m_hPlayerPinPos, FIELD_EHANDLE ),
  247. DEFINE_FIELD( m_playerPinFailsafeTime, FIELD_TIME ),
  248. // DEFINE_FIELD( m_hThrowEnt, FIELD_EHANDLE ),
  249. DEFINE_FIELD( m_flThrowPhysicsTime, FIELD_TIME ),
  250. DEFINE_FIELD( m_flLastPlayerAttackTime, FIELD_TIME ),
  251. DEFINE_FIELD( m_flStagingEnd, FIELD_TIME ),
  252. DEFINE_FIELD( m_iStagingNum, FIELD_INTEGER ),
  253. DEFINE_FIELD( m_bWasScripting, FIELD_BOOLEAN ),
  254. DEFINE_FIELD( m_flLastThrowTime, FIELD_TIME ),
  255. DEFINE_FIELD( m_vSavedLeadVel, FIELD_VECTOR ),
  256. DEFINE_OUTPUT( m_OnPickingThrowable, "OnPickingThrowable" ),
  257. DEFINE_OUTPUT( m_OnThrowWarn, "OnThrowWarn" ),
  258. DEFINE_OUTPUT( m_OnThrow, "OnThrow" ),
  259. DEFINE_OUTPUT( m_OnHealthIsNow, "OnHealthIsNow" ),
  260. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetThrowRate", InputSetThrowRate ),
  261. DEFINE_INPUTFUNC( FIELD_STRING, "WrenchImmediate", InputWrenchImmediate ),
  262. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetStagingNum", InputSetStagingNum),
  263. DEFINE_INPUTFUNC( FIELD_STRING, "PinPlayer", InputPinPlayer ),
  264. DEFINE_INPUTFUNC( FIELD_STRING, "BeamOn", InputTurnBeamOn ),
  265. DEFINE_INPUTFUNC( FIELD_STRING, "BeamOff", InputTurnBeamOff ),
  266. DEFINE_INPUTFUNC( FIELD_STRING, "ElightOn", InputElightOn ),
  267. DEFINE_INPUTFUNC( FIELD_STRING, "ElightOff", InputElightOff ),
  268. #endif
  269. END_DATADESC()
  270. #if NPC_ADVISOR_HAS_BEHAVIOR
  271. IMPLEMENT_SERVERCLASS_ST(CNPC_Advisor, DT_NPC_Advisor)
  272. END_SEND_TABLE()
  273. #endif
  274. //-----------------------------------------------------------------------------
  275. //-----------------------------------------------------------------------------
  276. void CNPC_Advisor::Spawn()
  277. {
  278. BaseClass::Spawn();
  279. #ifdef _XBOX
  280. // Always fade the corpse
  281. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  282. #endif // _XBOX
  283. Precache();
  284. SetModel( STRING( GetModelName() ) );
  285. m_iHealth = sk_advisor_health.GetFloat();
  286. m_takedamage = DAMAGE_NO;
  287. SetHullType( HULL_LARGE_CENTERED );
  288. SetHullSizeNormal();
  289. SetSolid( SOLID_BBOX );
  290. // AddSolidFlags( FSOLID_NOT_SOLID );
  291. SetMoveType( MOVETYPE_FLY );
  292. m_flFieldOfView = VIEW_FIELD_FULL;
  293. SetViewOffset( Vector( 0, 0, 80 ) ); // Position of the eyes relative to NPC's origin.
  294. SetBloodColor( BLOOD_COLOR_GREEN );
  295. m_NPCState = NPC_STATE_NONE;
  296. CapabilitiesClear();
  297. NPCInit();
  298. SetGoalEnt( NULL );
  299. AddEFlags( EFL_NO_DISSOLVE );
  300. }
  301. #if NPC_ADVISOR_HAS_BEHAVIOR
  302. //-----------------------------------------------------------------------------
  303. // comparison function for qsort used below. Compares "StagingPriority" keyfield
  304. //-----------------------------------------------------------------------------
  305. int __cdecl AdvisorStagingComparator(const EHANDLE *pe1, const EHANDLE *pe2)
  306. {
  307. // bool ReadKeyField( const char *varName, variant_t *var );
  308. variant_t var;
  309. int val1 = 10, val2 = 10; // default priority is ten
  310. // read field one
  311. if ( pe1->Get()->ReadKeyField( "StagingPriority", &var ) )
  312. {
  313. val1 = var.Int();
  314. }
  315. // read field two
  316. if ( pe2->Get()->ReadKeyField( "StagingPriority", &var ) )
  317. {
  318. val2 = var.Int();
  319. }
  320. // return comparison (< 0 if pe1<pe2)
  321. return( val1 - val2 );
  322. }
  323. #endif
  324. //-----------------------------------------------------------------------------
  325. //-----------------------------------------------------------------------------
  326. #pragma warning(push)
  327. #pragma warning(disable : 4706)
  328. void CNPC_Advisor::Activate()
  329. {
  330. BaseClass::Activate();
  331. m_hLevitateGoal1 = gEntList.FindEntityGeneric( NULL, STRING( m_iszLevitateGoal1 ), this );
  332. m_hLevitateGoal2 = gEntList.FindEntityGeneric( NULL, STRING( m_iszLevitateGoal2 ), this );
  333. m_hLevitationArea = gEntList.FindEntityGeneric( NULL, STRING( m_iszLevitationArea ), this );
  334. m_levitateCallback.m_Advisor = this;
  335. #if NPC_ADVISOR_HAS_BEHAVIOR
  336. // load the staging positions
  337. CBaseEntity *pEnt = NULL;
  338. m_hvStagingPositions.EnsureCapacity(6); // reserve six
  339. // conditional assignment: find an entity by name and save it into pEnt. Bail out when none are left.
  340. while ( pEnt = gEntList.FindEntityByName(pEnt,m_iszStagingEntities) )
  341. {
  342. m_hvStagingPositions.AddToTail(pEnt);
  343. }
  344. // sort the staging positions by their staging number.
  345. m_hvStagingPositions.Sort( AdvisorStagingComparator );
  346. // positions loaded, null out the m_hvStagedEnts array with exactly as many null spaces
  347. m_hvStagedEnts.SetCount( m_hvStagingPositions.Count() );
  348. m_iStagingNum = 1;
  349. AssertMsg(m_hvStagingPositions.Count() > 0, "You did not specify any staging positions in the advisor's staging_ent_names !");
  350. #endif
  351. }
  352. #pragma warning(pop)
  353. //-----------------------------------------------------------------------------
  354. //-----------------------------------------------------------------------------
  355. void CNPC_Advisor::UpdateOnRemove()
  356. {
  357. if ( m_pLevitateController )
  358. {
  359. physenv->DestroyMotionController( m_pLevitateController );
  360. }
  361. BaseClass::UpdateOnRemove();
  362. }
  363. //-----------------------------------------------------------------------------
  364. //-----------------------------------------------------------------------------
  365. void CNPC_Advisor::OnRestore()
  366. {
  367. BaseClass::OnRestore();
  368. StartLevitatingObjects();
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Returns this monster's classification in the relationship table.
  372. //-----------------------------------------------------------------------------
  373. Class_T CNPC_Advisor::Classify()
  374. {
  375. return CLASS_COMBINE;
  376. }
  377. //-----------------------------------------------------------------------------
  378. //-----------------------------------------------------------------------------
  379. bool CNPC_Advisor::IsHeavyDamage( const CTakeDamageInfo &info )
  380. {
  381. return (info.GetDamage() > 0);
  382. }
  383. //-----------------------------------------------------------------------------
  384. //-----------------------------------------------------------------------------
  385. void CNPC_Advisor::StartLevitatingObjects()
  386. {
  387. if ( !m_pLevitateController )
  388. {
  389. m_pLevitateController = physenv->CreateMotionController( &m_levitateCallback );
  390. }
  391. m_pLevitateController->ClearObjects();
  392. int nCount = m_physicsObjects.Count();
  393. for ( int i = 0; i < nCount; i++ )
  394. {
  395. CBaseEntity *pEnt = m_physicsObjects.Element( i );
  396. if ( !pEnt )
  397. continue;
  398. //NDebugOverlay::Box( pEnt->GetAbsOrigin(), pEnt->CollisionProp()->OBBMins(), pEnt->CollisionProp()->OBBMaxs(), 0, 255, 0, 1, 0.1 );
  399. IPhysicsObject *pPhys = pEnt->VPhysicsGetObject();
  400. if ( pPhys && pPhys->IsMoveable() )
  401. {
  402. m_pLevitateController->AttachObject( pPhys, false );
  403. pPhys->Wake();
  404. }
  405. }
  406. }
  407. // This function is used by both version of the entity finder below
  408. bool CNPC_Advisor::CanLevitateEntity( CBaseEntity *pEntity, int minMass, int maxMass )
  409. {
  410. if (!pEntity || pEntity->IsNPC())
  411. return false;
  412. IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
  413. if (!pPhys)
  414. return false;
  415. float mass = pPhys->GetMass();
  416. return ( mass >= minMass &&
  417. mass <= maxMass &&
  418. //pEntity->VPhysicsGetObject()->IsAsleep() &&
  419. pPhys->IsMoveable() /* &&
  420. !DidThrow(pEntity) */ );
  421. }
  422. #if NPC_ADVISOR_HAS_BEHAVIOR
  423. // find an object to throw at the player and start the warning on it. Return object's
  424. // pointer if we got something. Otherwise, return NULL if nothing left to throw. Will
  425. // always leave the prepared object at the head of m_hvStagedEnts
  426. CBaseEntity *CNPC_Advisor::ThrowObjectPrepare()
  427. {
  428. CBaseEntity *pThrowable = NULL;
  429. while (m_hvStagedEnts.Count() > 0)
  430. {
  431. pThrowable = m_hvStagedEnts[0];
  432. if (pThrowable)
  433. {
  434. IPhysicsObject *pPhys = pThrowable->VPhysicsGetObject();
  435. if ( !pPhys )
  436. {
  437. // reject!
  438. Write_BeamOff(m_hvStagedEnts[0]);
  439. pThrowable = NULL;
  440. }
  441. }
  442. // if we still have pThrowable...
  443. if (pThrowable)
  444. {
  445. // we're good
  446. break;
  447. }
  448. else
  449. {
  450. m_hvStagedEnts.Remove(0);
  451. }
  452. }
  453. if (pThrowable)
  454. {
  455. Assert( pThrowable->VPhysicsGetObject() );
  456. // play the sound, attach the light, fire the trigger
  457. EmitSound( "NPC_Advisor.ObjectChargeUp" );
  458. m_OnThrowWarn.FireOutput(pThrowable,this);
  459. m_flThrowPhysicsTime = gpGlobals->curtime + advisor_throw_warn_time.GetFloat();
  460. if ( GetEnemy() )
  461. {
  462. PreHurlClearTheWay( pThrowable, GetEnemy()->EyePosition() );
  463. }
  464. return pThrowable;
  465. }
  466. else // we had nothing to throw
  467. {
  468. return NULL;
  469. }
  470. }
  471. //-----------------------------------------------------------------------------
  472. //-----------------------------------------------------------------------------
  473. void CNPC_Advisor::StartTask( const Task_t *pTask )
  474. {
  475. switch ( pTask->iTask )
  476. {
  477. // DVS: TODO: if this gets expensive we can start caching the results and doing it less often.
  478. case TASK_ADVISOR_FIND_OBJECTS:
  479. {
  480. // if we have a trigger volume, use the contents of that. If not, use a hardcoded box (for debugging purposes)
  481. // in both cases we validate the objects using the same helper funclet just above. When we can count on the
  482. // trigger vol being there, we can elide the else{} clause here.
  483. CBaseEntity *pVolume = m_hLevitationArea;
  484. AssertMsg(pVolume, "Combine advisor needs 'levitationarea' key pointing to a trigger volume." );
  485. if (!pVolume)
  486. {
  487. TaskFail( "No levitation area found!" );
  488. break;
  489. }
  490. touchlink_t *touchroot = ( touchlink_t * )pVolume->GetDataObject( TOUCHLINK );
  491. if ( touchroot )
  492. {
  493. m_physicsObjects.RemoveAll();
  494. for ( touchlink_t *link = touchroot->nextLink; link != touchroot; link = link->nextLink )
  495. {
  496. CBaseEntity *pTouch = link->entityTouched;
  497. if ( CanLevitateEntity( pTouch, 10, 220 ) )
  498. {
  499. if ( pTouch->GetMoveType() == MOVETYPE_VPHYSICS )
  500. {
  501. //Msg( " %d added %s\n", m_physicsObjects.Count(), STRING( list[i]->GetModelName() ) );
  502. m_physicsObjects.AddToTail( pTouch );
  503. }
  504. }
  505. }
  506. }
  507. /*
  508. // this is the old mechanism, using a hardcoded box and an entity enumerator.
  509. // since deprecated.
  510. else
  511. {
  512. CBaseEntity *list[128];
  513. m_physicsObjects.RemoveAll();
  514. //NDebugOverlay::Box( GetAbsOrigin(), Vector( -408, -368, -188 ), Vector( 92, 208, 168 ), 255, 255, 0, 1, 5 );
  515. // one-off class used to determine which entities we want from the UTIL_EntitiesInBox
  516. class CAdvisorLevitateEntitiesEnum : public CFlaggedEntitiesEnum
  517. {
  518. public:
  519. CAdvisorLevitateEntitiesEnum( CBaseEntity **pList, int listMax, int nMinMass, int nMaxMass )
  520. : CFlaggedEntitiesEnum( pList, listMax, 0 ),
  521. m_nMinMass( nMinMass ),
  522. m_nMaxMass( nMaxMass )
  523. {
  524. }
  525. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  526. {
  527. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  528. if ( AdvisorCanLevitateEntity( pEntity, m_nMinMass, m_nMaxMass ) )
  529. {
  530. return CFlaggedEntitiesEnum::EnumElement( pHandleEntity );
  531. }
  532. return ITERATION_CONTINUE;
  533. }
  534. int m_nMinMass;
  535. int m_nMaxMass;
  536. };
  537. CAdvisorLevitateEntitiesEnum levitateEnum( list, ARRAYSIZE( list ), 10, 220 );
  538. int nCount = UTIL_EntitiesInBox( GetAbsOrigin() - Vector( 554, 368, 188 ), GetAbsOrigin() + Vector( 92, 208, 168 ), &levitateEnum );
  539. for ( int i = 0; i < nCount; i++ )
  540. {
  541. //Msg( "%d found %s\n", m_physicsObjects.Count(), STRING( list[i]->GetModelName() ) );
  542. if ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS )
  543. {
  544. //Msg( " %d added %s\n", m_physicsObjects.Count(), STRING( list[i]->GetModelName() ) );
  545. m_physicsObjects.AddToTail( list[i] );
  546. }
  547. }
  548. }
  549. */
  550. if ( m_physicsObjects.Count() > 0 )
  551. {
  552. TaskComplete();
  553. }
  554. else
  555. {
  556. TaskFail( "No physics objects found!" );
  557. }
  558. break;
  559. }
  560. case TASK_ADVISOR_LEVITATE_OBJECTS:
  561. {
  562. StartLevitatingObjects();
  563. m_flThrowPhysicsTime = gpGlobals->curtime + advisor_throw_rate.GetFloat();
  564. break;
  565. }
  566. case TASK_ADVISOR_STAGE_OBJECTS:
  567. {
  568. // m_pickFailures = 0;
  569. // clear out previously staged throwables
  570. /*
  571. for (int ii = m_hvStagedEnts.Count() - 1; ii >= 0 ; --ii)
  572. {
  573. m_hvStagedEnts[ii] = NULL;
  574. }
  575. */
  576. Write_AllBeamsOff();
  577. m_hvStagedEnts.RemoveAll();
  578. m_OnPickingThrowable.FireOutput(NULL,this);
  579. m_flStagingEnd = gpGlobals->curtime + pTask->flTaskData;
  580. break;
  581. }
  582. // we're about to pelt the player with everything. Start the warning effect on the first object.
  583. case TASK_ADVISOR_BARRAGE_OBJECTS:
  584. {
  585. CBaseEntity *pThrowable = ThrowObjectPrepare();
  586. if (!pThrowable || m_hvStagedEnts.Count() < 1)
  587. {
  588. TaskFail( "Nothing to throw!" );
  589. return;
  590. }
  591. m_vSavedLeadVel.Invalidate();
  592. break;
  593. }
  594. case TASK_ADVISOR_PIN_PLAYER:
  595. {
  596. // should never be here
  597. /*
  598. Assert( m_hPlayerPinPos.IsValid() );
  599. m_playerPinFailsafeTime = gpGlobals->curtime + 10.0f;
  600. break;
  601. */
  602. }
  603. default:
  604. {
  605. BaseClass::StartTask( pTask );
  606. }
  607. }
  608. }
  609. //-----------------------------------------------------------------------------
  610. // todo: find a way to guarantee that objects are made pickupable again when bailing out of a task
  611. //-----------------------------------------------------------------------------
  612. void CNPC_Advisor::RunTask( const Task_t *pTask )
  613. {
  614. switch ( pTask->iTask )
  615. {
  616. // Raise up the objects that we found and then hold them.
  617. case TASK_ADVISOR_LEVITATE_OBJECTS:
  618. {
  619. float flTimeToThrow = m_flThrowPhysicsTime - gpGlobals->curtime;
  620. if ( flTimeToThrow < 0 )
  621. {
  622. TaskComplete();
  623. return;
  624. }
  625. // set the top and bottom on the levitation volume from the entities. If we don't have
  626. // both, zero it out so that we can use the old-style simpler mechanism.
  627. if ( m_hLevitateGoal1 && m_hLevitateGoal2 )
  628. {
  629. m_levitateCallback.m_vecGoalPos1 = m_hLevitateGoal1->GetAbsOrigin();
  630. m_levitateCallback.m_vecGoalPos2 = m_hLevitateGoal2->GetAbsOrigin();
  631. // swap them if necessary (1 must be the bottom)
  632. if (m_levitateCallback.m_vecGoalPos1.z > m_levitateCallback.m_vecGoalPos2.z)
  633. {
  634. swap(m_levitateCallback.m_vecGoalPos1,m_levitateCallback.m_vecGoalPos2);
  635. }
  636. m_levitateCallback.m_flFloat = 0.06f; // this is an absolute accumulation upon gravity
  637. }
  638. else
  639. {
  640. m_levitateCallback.m_vecGoalPos1.Invalidate();
  641. m_levitateCallback.m_vecGoalPos2.Invalidate();
  642. // the below two stanzas are used for old-style floating, which is linked
  643. // to float up before thrown and down after
  644. if ( flTimeToThrow > 2.0f )
  645. {
  646. m_levitateCallback.m_flFloat = 1.06f;
  647. }
  648. else
  649. {
  650. m_levitateCallback.m_flFloat = 0.94f;
  651. }
  652. }
  653. /*
  654. // Draw boxes around the objects we're levitating.
  655. for ( int i = 0; i < m_physicsObjects.Count(); i++ )
  656. {
  657. CBaseEntity *pEnt = m_physicsObjects.Element( i );
  658. if ( !pEnt )
  659. continue; // The prop has been broken!
  660. IPhysicsObject *pPhys = pEnt->VPhysicsGetObject();
  661. if ( pPhys && pPhys->IsMoveable() )
  662. {
  663. NDebugOverlay::Box( pEnt->GetAbsOrigin(), pEnt->CollisionProp()->OBBMins(), pEnt->CollisionProp()->OBBMaxs(), 0, 255, 0, 1, 0.1 );
  664. }
  665. }*/
  666. break;
  667. }
  668. // Pick a random object that we are levitating. If we have a clear LOS from that object
  669. // to our enemy's eyes, choose that one to throw. Otherwise, keep looking.
  670. case TASK_ADVISOR_STAGE_OBJECTS:
  671. {
  672. if (m_iStagingNum > m_hvStagingPositions.Count())
  673. {
  674. Warning( "Advisor tries to stage %d objects but only has %d positions named %s! Overriding.\n", m_iStagingNum, m_hvStagingPositions.Count(), m_iszStagingEntities );
  675. m_iStagingNum = m_hvStagingPositions.Count() ;
  676. }
  677. // advisor_staging_num
  678. // in the future i'll distribute the staging chronologically. For now, yank all the objects at once.
  679. if (m_hvStagedEnts.Count() < m_iStagingNum)
  680. {
  681. // pull another object
  682. bool bDesperate = m_flStagingEnd - gpGlobals->curtime < 0.50f; // less than one half second left
  683. CBaseEntity *pThrowable = PickThrowable(!bDesperate);
  684. if (pThrowable)
  685. {
  686. // don't let the player take it from me
  687. IPhysicsObject *pPhys = pThrowable->VPhysicsGetObject();
  688. if ( pPhys )
  689. {
  690. // no pickup!
  691. pPhys->SetGameFlags(pPhys->GetGameFlags() | FVPHYSICS_NO_PLAYER_PICKUP );;
  692. }
  693. m_hvStagedEnts.AddToTail( pThrowable );
  694. Write_BeamOn(pThrowable);
  695. DispatchParticleEffect( "advisor_object_charge", PATTACH_ABSORIGIN_FOLLOW,
  696. pThrowable, 0,
  697. false );
  698. }
  699. }
  700. Assert(m_hvStagedEnts.Count() <= m_hvStagingPositions.Count());
  701. // yank all objects into place
  702. for (int ii = m_hvStagedEnts.Count() - 1 ; ii >= 0 ; --ii)
  703. {
  704. // just ignore lost objects (if the player destroys one, that's fine, leave a hole)
  705. CBaseEntity *pThrowable = m_hvStagedEnts[ii];
  706. if (pThrowable)
  707. {
  708. PullObjectToStaging(pThrowable, m_hvStagingPositions[ii]->GetAbsOrigin());
  709. }
  710. }
  711. // are we done yet?
  712. if (gpGlobals->curtime > m_flStagingEnd)
  713. {
  714. TaskComplete();
  715. break;
  716. }
  717. break;
  718. }
  719. // Fling the object that we picked at our enemy's eyes!
  720. case TASK_ADVISOR_BARRAGE_OBJECTS:
  721. {
  722. Assert(m_hvStagedEnts.Count() > 0);
  723. // do I still have an enemy?
  724. if ( !GetEnemy() )
  725. {
  726. // no? bail all the objects.
  727. for (int ii = m_hvStagedEnts.Count() - 1 ; ii >=0 ; --ii)
  728. {
  729. IPhysicsObject *pPhys = m_hvStagedEnts[ii]->VPhysicsGetObject();
  730. if ( pPhys )
  731. {
  732. pPhys->SetGameFlags(pPhys->GetGameFlags() & (~FVPHYSICS_NO_PLAYER_PICKUP) );
  733. }
  734. }
  735. Write_AllBeamsOff();
  736. m_hvStagedEnts.RemoveAll();
  737. TaskFail( "Lost enemy" );
  738. return;
  739. }
  740. // do I still have something to throw at the player?
  741. CBaseEntity *pThrowable = m_hvStagedEnts[0];
  742. while (!pThrowable)
  743. { // player has destroyed whatever I planned to hit him with, get something else
  744. if (m_hvStagedEnts.Count() > 0)
  745. {
  746. pThrowable = ThrowObjectPrepare();
  747. }
  748. else
  749. {
  750. TaskComplete();
  751. break;
  752. }
  753. }
  754. // If we've gone NULL, then opt out
  755. if ( pThrowable == NULL )
  756. {
  757. TaskComplete();
  758. break;
  759. }
  760. if ( (gpGlobals->curtime > m_flThrowPhysicsTime - advisor_throw_lead_prefetch_time.GetFloat()) &&
  761. !m_vSavedLeadVel.IsValid() )
  762. {
  763. // save off the velocity we will use to lead the player a little early, so that if he jukes
  764. // at the last moment he'll have a better shot of dodging the object.
  765. m_vSavedLeadVel = GetEnemy()->GetAbsVelocity();
  766. }
  767. // if it's time to throw something, throw it and go on to the next one.
  768. if (gpGlobals->curtime > m_flThrowPhysicsTime)
  769. {
  770. IPhysicsObject *pPhys = pThrowable->VPhysicsGetObject();
  771. Assert(pPhys);
  772. pPhys->SetGameFlags(pPhys->GetGameFlags() & (~FVPHYSICS_NO_PLAYER_PICKUP) );
  773. HurlObjectAtPlayer(pThrowable,Vector(0,0,0)/*m_vSavedLeadVel*/);
  774. m_flLastThrowTime = gpGlobals->curtime;
  775. m_flThrowPhysicsTime = gpGlobals->curtime + 0.75f;
  776. // invalidate saved lead for next time
  777. m_vSavedLeadVel.Invalidate();
  778. EmitSound( "NPC_Advisor.Blast" );
  779. Write_BeamOff(m_hvStagedEnts[0]);
  780. m_hvStagedEnts.Remove(0);
  781. if (!ThrowObjectPrepare())
  782. {
  783. TaskComplete();
  784. break;
  785. }
  786. }
  787. else
  788. {
  789. // wait, bide time
  790. // PullObjectToStaging(pThrowable, m_hvStagingPositions[ii]->GetAbsOrigin());
  791. }
  792. break;
  793. }
  794. case TASK_ADVISOR_PIN_PLAYER:
  795. {
  796. /*
  797. // bail out if the pin entity went away.
  798. CBaseEntity *pPinEnt = m_hPlayerPinPos;
  799. if (!pPinEnt)
  800. {
  801. GetEnemy()->SetGravity(1.0f);
  802. GetEnemy()->SetMoveType( MOVETYPE_WALK );
  803. TaskComplete();
  804. break;
  805. }
  806. // failsafe: don't do this for more than ten seconds.
  807. if ( gpGlobals->curtime > m_playerPinFailsafeTime )
  808. {
  809. GetEnemy()->SetGravity(1.0f);
  810. GetEnemy()->SetMoveType( MOVETYPE_WALK );
  811. Warning( "Advisor did not leave PIN PLAYER mode. Aborting due to ten second failsafe!\n" );
  812. TaskFail("Advisor did not leave PIN PLAYER mode. Aborting due to ten second failsafe!\n");
  813. break;
  814. }
  815. // if the player isn't the enemy, bail out.
  816. if ( !GetEnemy()->IsPlayer() )
  817. {
  818. GetEnemy()->SetGravity(1.0f);
  819. GetEnemy()->SetMoveType( MOVETYPE_WALK );
  820. TaskFail( "Player is not the enemy?!" );
  821. break;
  822. }
  823. GetEnemy()->SetMoveType( MOVETYPE_FLY );
  824. GetEnemy()->SetGravity(0);
  825. // use exponential falloff to peg the player to the pin point
  826. const Vector &desiredPos = pPinEnt->GetAbsOrigin();
  827. const Vector &playerPos = GetEnemy()->GetAbsOrigin();
  828. Vector displacement = desiredPos - playerPos;
  829. float desiredDisplacementLen = ExponentialDecay(0.250f,gpGlobals->frametime);// * sqrt(displacementLen);
  830. Vector nuPos = playerPos + (displacement * (1.0f - desiredDisplacementLen));
  831. GetEnemy()->SetAbsOrigin( nuPos );
  832. break;
  833. */
  834. }
  835. default:
  836. {
  837. BaseClass::RunTask( pTask );
  838. }
  839. }
  840. }
  841. #endif
  842. // helper function for testing whether or not an avisor is allowed to grab an object
  843. static bool AdvisorCanPickObject(CBasePlayer *pPlayer, CBaseEntity *pEnt)
  844. {
  845. Assert( pPlayer != NULL );
  846. // Is the player carrying something?
  847. CBaseEntity *pHeldObject = GetPlayerHeldEntity(pPlayer);
  848. if( !pHeldObject )
  849. {
  850. pHeldObject = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() );
  851. }
  852. if( pHeldObject == pEnt )
  853. {
  854. return false;
  855. }
  856. if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  857. {
  858. return false;
  859. }
  860. return true;
  861. }
  862. #if NPC_ADVISOR_HAS_BEHAVIOR
  863. //-----------------------------------------------------------------------------
  864. // Choose an object to throw.
  865. // param bRequireInView : if true, only accept objects that are in the player's fov.
  866. //
  867. // Can always return NULL.
  868. // todo priority_grab_name
  869. //-----------------------------------------------------------------------------
  870. CBaseEntity *CNPC_Advisor::PickThrowable( bool bRequireInView )
  871. {
  872. CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );
  873. Assert(pPlayer);
  874. if (!pPlayer)
  875. return NULL;
  876. const int numObjs = m_physicsObjects.Count(); ///< total number of physics objects in my system
  877. if (numObjs < 1)
  878. return NULL; // bail out if nothing available
  879. // used for require-in-view
  880. Vector eyeForward, eyeOrigin;
  881. if (pPlayer)
  882. {
  883. eyeOrigin = pPlayer->EyePosition();
  884. pPlayer->EyeVectors(&eyeForward);
  885. }
  886. else
  887. {
  888. bRequireInView = false;
  889. }
  890. // filter-and-choose algorithm:
  891. // build a list of candidates
  892. Assert(numObjs < 128); /// I'll come back and utlvector this shortly -- wanted easier debugging
  893. unsigned int candidates[128];
  894. unsigned int numCandidates = 0;
  895. if (!!m_iszPriorityEntityGroupName) // if the string isn't null
  896. {
  897. // first look to see if we have any priority objects.
  898. for (int ii = 0 ; ii < numObjs ; ++ii )
  899. {
  900. CBaseEntity *pThrowEnt = m_physicsObjects[ii];
  901. // Assert(pThrowEnt);
  902. if (!pThrowEnt)
  903. continue;
  904. if (!pThrowEnt->NameMatches(m_iszPriorityEntityGroupName)) // if this is not a priority object
  905. continue;
  906. bool bCanPick = AdvisorCanPickObject( pPlayer, pThrowEnt ) && !m_hvStagedEnts.HasElement( m_physicsObjects[ii] );
  907. if (!bCanPick)
  908. continue;
  909. // bCanPick guaranteed true here
  910. if ( bRequireInView )
  911. {
  912. bCanPick = (pThrowEnt->GetAbsOrigin() - eyeOrigin).Dot(eyeForward) > 0;
  913. }
  914. if ( bCanPick )
  915. {
  916. candidates[numCandidates++] = ii;
  917. }
  918. }
  919. }
  920. // if we found no priority objects (or don't have a priority), just grab whatever
  921. if (numCandidates == 0)
  922. {
  923. for (int ii = 0 ; ii < numObjs ; ++ii )
  924. {
  925. CBaseEntity *pThrowEnt = m_physicsObjects[ii];
  926. // Assert(pThrowEnt);
  927. if (!pThrowEnt)
  928. continue;
  929. bool bCanPick = AdvisorCanPickObject( pPlayer, pThrowEnt ) && !m_hvStagedEnts.HasElement( m_physicsObjects[ii] );
  930. if (!bCanPick)
  931. continue;
  932. // bCanPick guaranteed true here
  933. if ( bRequireInView )
  934. {
  935. bCanPick = (pThrowEnt->GetAbsOrigin() - eyeOrigin).Dot(eyeForward) > 0;
  936. }
  937. if ( bCanPick )
  938. {
  939. candidates[numCandidates++] = ii;
  940. }
  941. }
  942. }
  943. if ( numCandidates == 0 )
  944. return NULL; // must have at least one candidate
  945. // pick a random candidate.
  946. int nRandomIndex = random->RandomInt( 0, numCandidates - 1 );
  947. return m_physicsObjects[candidates[nRandomIndex]];
  948. }
  949. /*! \TODO
  950. Correct bug where Advisor seemed to be throwing stuff at people's feet.
  951. This is because the object was falling slightly in between the staging
  952. and when he threw it, and that downward velocity was getting accumulated
  953. into the throw speed. This is temporarily fixed here by using SetVelocity
  954. instead of AddVelocity, but the proper fix is to pin the object to its
  955. staging point during the warn period. That will require maintaining a map
  956. of throwables to their staging points during the throw task.
  957. */
  958. //-----------------------------------------------------------------------------
  959. // Impart necessary force on any entity to make it clobber Gordon.
  960. // Also detaches from levitate controller.
  961. // The optional lead velocity parameter is for cases when we pre-save off the
  962. // player's speed, to make last-moment juking more effective
  963. //-----------------------------------------------------------------------------
  964. void CNPC_Advisor::HurlObjectAtPlayer( CBaseEntity *pEnt, const Vector &leadVel )
  965. {
  966. IPhysicsObject *pPhys = pEnt->VPhysicsGetObject();
  967. //
  968. // Lead the target accurately. This encourages hiding behind cover
  969. // and/or catching the thrown physics object!
  970. //
  971. Vector vecObjOrigin = pEnt->CollisionProp()->WorldSpaceCenter();
  972. Vector vecEnemyPos = GetEnemy()->EyePosition();
  973. // disabled -- no longer compensate for gravity: // vecEnemyPos.y += 12.0f;
  974. // const Vector &leadVel = pLeadVelocity ? *pLeadVelocity : GetEnemy()->GetAbsVelocity();
  975. Vector vecDelta = vecEnemyPos - vecObjOrigin;
  976. float flDist = vecDelta.Length();
  977. float flVelocity = advisor_throw_velocity.GetFloat();
  978. if ( flVelocity == 0 )
  979. {
  980. flVelocity = 1000;
  981. }
  982. float flFlightTime = flDist / flVelocity;
  983. Vector vecThrowAt = vecEnemyPos + flFlightTime * leadVel;
  984. Vector vecThrowDir = vecThrowAt - vecObjOrigin;
  985. VectorNormalize( vecThrowDir );
  986. Vector vecVelocity = flVelocity * vecThrowDir;
  987. pPhys->SetVelocity( &vecVelocity, NULL );
  988. AddToThrownObjects(pEnt);
  989. m_OnThrow.FireOutput(pEnt,this);
  990. }
  991. //-----------------------------------------------------------------------------
  992. // do a sweep from an object I'm about to throw, to the target, pushing aside
  993. // anything floating in the way.
  994. // TODO: this is probably a good profiling candidate.
  995. //-----------------------------------------------------------------------------
  996. void CNPC_Advisor::PreHurlClearTheWay( CBaseEntity *pThrowable, const Vector &toPos )
  997. {
  998. // look for objects in the way of chucking.
  999. CBaseEntity *list[128];
  1000. Ray_t ray;
  1001. float boundingRadius = pThrowable->BoundingRadius();
  1002. ray.Init( pThrowable->GetAbsOrigin(), toPos,
  1003. Vector(-boundingRadius,-boundingRadius,-boundingRadius),
  1004. Vector( boundingRadius, boundingRadius, boundingRadius) );
  1005. int nFoundCt = UTIL_EntitiesAlongRay( list, 128, ray, 0 );
  1006. AssertMsg(nFoundCt < 128, "Found more than 128 obstructions between advisor and Gordon while throwing. (safe to continue)\n");
  1007. // for each thing in the way that I levitate, but is not something I'm staging
  1008. // or throwing, push it aside.
  1009. for (int i = 0 ; i < nFoundCt ; ++i )
  1010. {
  1011. CBaseEntity *obstruction = list[i];
  1012. if ( obstruction != pThrowable &&
  1013. m_physicsObjects.HasElement( obstruction ) && // if it's floating
  1014. !m_hvStagedEnts.HasElement( obstruction ) && // and I'm not staging it
  1015. !DidThrow( obstruction ) ) // and I didn't just throw it
  1016. {
  1017. IPhysicsObject *pPhys = obstruction->VPhysicsGetObject();
  1018. Assert(pPhys);
  1019. // this is an object we want to push out of the way. Compute a vector perpendicular
  1020. // to the path of the throwables's travel, and thrust the object along that vector.
  1021. Vector thrust;
  1022. CalcClosestPointOnLine( obstruction->GetAbsOrigin(),
  1023. pThrowable->GetAbsOrigin(),
  1024. toPos,
  1025. thrust );
  1026. // "thrust" is now the closest point on the line to the obstruction.
  1027. // compute the difference to get the direction of impulse
  1028. thrust = obstruction->GetAbsOrigin() - thrust;
  1029. // and renormalize it to equal a giant kick out of the way
  1030. // (which I'll say is about ten feet per second -- if we want to be
  1031. // more precise we could do some kind of interpolation based on how
  1032. // far away the object is)
  1033. float thrustLen = thrust.Length();
  1034. if (thrustLen > 0.0001f)
  1035. {
  1036. thrust *= advisor_throw_clearout_vel.GetFloat() / thrustLen;
  1037. }
  1038. // heave!
  1039. pPhys->AddVelocity( &thrust, NULL );
  1040. }
  1041. }
  1042. /*
  1043. // Otherwise only help out a little
  1044. Vector extents = Vector(256, 256, 256);
  1045. Ray_t ray;
  1046. ray.Init( vecStartPoint, vecStartPoint + 2048 * vecVelDir, -extents, extents );
  1047. int nCount = UTIL_EntitiesAlongRay( list, 1024, ray, FL_NPC | FL_CLIENT );
  1048. for ( int i = 0; i < nCount; i++ )
  1049. {
  1050. if ( !IsAttractiveTarget( list[i] ) )
  1051. continue;
  1052. VectorSubtract( list[i]->WorldSpaceCenter(), vecStartPoint, vecDelta );
  1053. distance = VectorNormalize( vecDelta );
  1054. flDot = DotProduct( vecDelta, vecVelDir );
  1055. if ( flDot > flMaxDot )
  1056. {
  1057. if ( distance < flBestDist )
  1058. {
  1059. pBestTarget = list[i];
  1060. flBestDist = distance;
  1061. }
  1062. }
  1063. }
  1064. */
  1065. }
  1066. /*
  1067. // commented out because unnecessary: we will do this during the DidThrow check
  1068. //-----------------------------------------------------------------------------
  1069. // clean out the recently thrown objects array
  1070. //-----------------------------------------------------------------------------
  1071. void CNPC_Advisor::PurgeThrownObjects()
  1072. {
  1073. float threeSecondsAgo = gpGlobals->curtime - 3.0f; // two seconds ago
  1074. for (int ii = 0 ; ii < kMaxThrownObjectsTracked ; ++ii)
  1075. {
  1076. if ( m_haRecentlyThrownObjects[ii].IsValid() &&
  1077. m_flaRecentlyThrownObjectTimes[ii] < threeSecondsAgo )
  1078. {
  1079. m_haRecentlyThrownObjects[ii].Set(NULL);
  1080. }
  1081. }
  1082. }
  1083. */
  1084. //-----------------------------------------------------------------------------
  1085. // true iff an advisor threw the object in the last three seconds
  1086. //-----------------------------------------------------------------------------
  1087. bool CNPC_Advisor::DidThrow(const CBaseEntity *pEnt)
  1088. {
  1089. // look through all my objects and see if they match this entity. Incidentally if
  1090. // they're more than three seconds old, purge them.
  1091. float threeSecondsAgo = gpGlobals->curtime - 3.0f;
  1092. for (int ii = 0 ; ii < kMaxThrownObjectsTracked ; ++ii)
  1093. {
  1094. // if object is old, skip it.
  1095. CBaseEntity *pTestEnt = m_haRecentlyThrownObjects[ii];
  1096. if ( pTestEnt )
  1097. {
  1098. if ( m_flaRecentlyThrownObjectTimes[ii] < threeSecondsAgo )
  1099. {
  1100. m_haRecentlyThrownObjects[ii].Set(NULL);
  1101. continue;
  1102. }
  1103. else if (pTestEnt == pEnt)
  1104. {
  1105. return true;
  1106. }
  1107. }
  1108. }
  1109. return false;
  1110. }
  1111. //-----------------------------------------------------------------------------
  1112. //-----------------------------------------------------------------------------
  1113. void CNPC_Advisor::AddToThrownObjects(CBaseEntity *pEnt)
  1114. {
  1115. Assert(pEnt);
  1116. // try to find an empty slot, or if none exists, the oldest object
  1117. int oldestThrownObject = 0;
  1118. for (int ii = 0 ; ii < kMaxThrownObjectsTracked ; ++ii)
  1119. {
  1120. if (m_haRecentlyThrownObjects[ii].IsValid())
  1121. {
  1122. if (m_flaRecentlyThrownObjectTimes[ii] < m_flaRecentlyThrownObjectTimes[oldestThrownObject])
  1123. {
  1124. oldestThrownObject = ii;
  1125. }
  1126. }
  1127. else
  1128. { // just use this one
  1129. oldestThrownObject = ii;
  1130. break;
  1131. }
  1132. }
  1133. m_haRecentlyThrownObjects[oldestThrownObject] = pEnt;
  1134. m_flaRecentlyThrownObjectTimes[oldestThrownObject] = gpGlobals->curtime;
  1135. }
  1136. //-----------------------------------------------------------------------------
  1137. // Drag a particular object towards its staging location.
  1138. //-----------------------------------------------------------------------------
  1139. void CNPC_Advisor::PullObjectToStaging( CBaseEntity *pEnt, const Vector &stagingPos )
  1140. {
  1141. IPhysicsObject *pPhys = pEnt->VPhysicsGetObject();
  1142. Assert(pPhys);
  1143. Vector curPos = pEnt->CollisionProp()->WorldSpaceCenter();
  1144. Vector displacement = stagingPos - curPos;
  1145. // quick and dirty -- use exponential decay to haul the object into place
  1146. // ( a better looking solution would be to use a spring system )
  1147. float desiredDisplacementLen = ExponentialDecay(STAGING_OBJECT_FALLOFF_TIME, gpGlobals->frametime);// * sqrt(displacementLen);
  1148. Vector vel; AngularImpulse angimp;
  1149. pPhys->GetVelocity(&vel,&angimp);
  1150. vel = (1.0f / gpGlobals->frametime)*(displacement * (1.0f - desiredDisplacementLen));
  1151. pPhys->SetVelocity(&vel,&angimp);
  1152. }
  1153. #endif
  1154. int CNPC_Advisor::OnTakeDamage( const CTakeDamageInfo &info )
  1155. {
  1156. // Clip our max
  1157. CTakeDamageInfo newInfo = info;
  1158. if ( newInfo.GetDamage() > 20.0f )
  1159. {
  1160. newInfo.SetDamage( 20.0f );
  1161. }
  1162. // Hack to make him constantly flinch
  1163. m_flNextFlinchTime = gpGlobals->curtime;
  1164. const float oldLastDamageTime = m_flLastDamageTime;
  1165. int retval = BaseClass::OnTakeDamage(newInfo);
  1166. // we have a special reporting output
  1167. if ( oldLastDamageTime != gpGlobals->curtime )
  1168. {
  1169. // only fire once per frame
  1170. m_OnHealthIsNow.Set( GetHealth(), newInfo.GetAttacker(), this);
  1171. }
  1172. return retval;
  1173. }
  1174. #if NPC_ADVISOR_HAS_BEHAVIOR
  1175. //-----------------------------------------------------------------------------
  1176. // Returns the best new schedule for this NPC based on current conditions.
  1177. //-----------------------------------------------------------------------------
  1178. int CNPC_Advisor::SelectSchedule()
  1179. {
  1180. if ( IsInAScript() )
  1181. return SCHED_ADVISOR_IDLE_STAND;
  1182. switch ( m_NPCState )
  1183. {
  1184. case NPC_STATE_IDLE:
  1185. case NPC_STATE_ALERT:
  1186. {
  1187. return SCHED_ADVISOR_IDLE_STAND;
  1188. }
  1189. case NPC_STATE_COMBAT:
  1190. {
  1191. if ( GetEnemy() && GetEnemy()->IsAlive() )
  1192. {
  1193. if ( false /* m_hPlayerPinPos.IsValid() */ )
  1194. return SCHED_ADVISOR_TOSS_PLAYER;
  1195. else
  1196. return SCHED_ADVISOR_COMBAT;
  1197. }
  1198. return SCHED_ADVISOR_IDLE_STAND;
  1199. }
  1200. }
  1201. return BaseClass::SelectSchedule();
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // return the position where an object should be staged before throwing
  1205. //-----------------------------------------------------------------------------
  1206. Vector CNPC_Advisor::GetThrowFromPos( CBaseEntity *pEnt )
  1207. {
  1208. Assert(pEnt);
  1209. Assert(pEnt->VPhysicsGetObject());
  1210. const CCollisionProperty *cProp = pEnt->CollisionProp();
  1211. Assert(cProp);
  1212. float effecRadius = cProp->BoundingRadius(); // radius of object (important for kickout)
  1213. float howFarInFront = advisor_throw_stage_distance.GetFloat() + effecRadius * 1.43f;// clamp(lenToPlayer - posDist + effecRadius,effecRadius*2,90.f + effecRadius);
  1214. Vector fwd;
  1215. GetVectors(&fwd,NULL,NULL);
  1216. return GetAbsOrigin() + fwd*howFarInFront;
  1217. }
  1218. #endif
  1219. //-----------------------------------------------------------------------------
  1220. //-----------------------------------------------------------------------------
  1221. void CNPC_Advisor::Precache()
  1222. {
  1223. BaseClass::Precache();
  1224. PrecacheModel( STRING( GetModelName() ) );
  1225. #if NPC_ADVISOR_HAS_BEHAVIOR
  1226. PrecacheModel( "sprites/lgtning.vmt" );
  1227. #endif
  1228. PrecacheScriptSound( "NPC_Advisor.Blast" );
  1229. PrecacheScriptSound( "NPC_Advisor.Gib" );
  1230. PrecacheScriptSound( "NPC_Advisor.Idle" );
  1231. PrecacheScriptSound( "NPC_Advisor.Alert" );
  1232. PrecacheScriptSound( "NPC_Advisor.Die" );
  1233. PrecacheScriptSound( "NPC_Advisor.Pain" );
  1234. PrecacheScriptSound( "NPC_Advisor.ObjectChargeUp" );
  1235. PrecacheParticleSystem( "Advisor_Psychic_Beam" );
  1236. PrecacheParticleSystem( "advisor_object_charge" );
  1237. PrecacheModel("sprites/greenglow1.vmt");
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. //-----------------------------------------------------------------------------
  1241. void CNPC_Advisor::IdleSound()
  1242. {
  1243. EmitSound( "NPC_Advisor.Idle" );
  1244. }
  1245. void CNPC_Advisor::AlertSound()
  1246. {
  1247. EmitSound( "NPC_Advisor.Alert" );
  1248. }
  1249. void CNPC_Advisor::PainSound( const CTakeDamageInfo &info )
  1250. {
  1251. EmitSound( "NPC_Advisor.Pain" );
  1252. }
  1253. void CNPC_Advisor::DeathSound( const CTakeDamageInfo &info )
  1254. {
  1255. EmitSound( "NPC_Advisor.Die" );
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. //-----------------------------------------------------------------------------
  1259. int CNPC_Advisor::DrawDebugTextOverlays()
  1260. {
  1261. int nOffset = BaseClass::DrawDebugTextOverlays();
  1262. return nOffset;
  1263. }
  1264. #if NPC_ADVISOR_HAS_BEHAVIOR
  1265. //-----------------------------------------------------------------------------
  1266. // Determines which sounds the advisor cares about.
  1267. //-----------------------------------------------------------------------------
  1268. int CNPC_Advisor::GetSoundInterests()
  1269. {
  1270. return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER;
  1271. }
  1272. //-----------------------------------------------------------------------------
  1273. // record the last time we heard a combat sound
  1274. //-----------------------------------------------------------------------------
  1275. bool CNPC_Advisor::QueryHearSound( CSound *pSound )
  1276. {
  1277. // Disregard footsteps from our own class type
  1278. CBaseEntity *pOwner = pSound->m_hOwner;
  1279. if ( pOwner && pSound->IsSoundType( SOUND_COMBAT ) && pSound->SoundChannel() != SOUNDENT_CHANNEL_NPC_FOOTSTEP && pSound->m_hOwner.IsValid() && pOwner->IsPlayer() )
  1280. {
  1281. // Msg("Heard player combat.\n");
  1282. m_flLastPlayerAttackTime = gpGlobals->curtime;
  1283. }
  1284. return BaseClass::QueryHearSound(pSound);
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. // designer hook for setting throw rate
  1288. //-----------------------------------------------------------------------------
  1289. void CNPC_Advisor::InputSetThrowRate( inputdata_t &inputdata )
  1290. {
  1291. advisor_throw_rate.SetValue(inputdata.value.Float());
  1292. }
  1293. void CNPC_Advisor::InputSetStagingNum( inputdata_t &inputdata )
  1294. {
  1295. m_iStagingNum = inputdata.value.Int();
  1296. }
  1297. //
  1298. // cause the player to be pinned to a point in space
  1299. //
  1300. void CNPC_Advisor::InputPinPlayer( inputdata_t &inputdata )
  1301. {
  1302. string_t targetname = inputdata.value.StringID();
  1303. // null string means designer is trying to unpin the player
  1304. if (!targetname)
  1305. {
  1306. m_hPlayerPinPos = NULL;
  1307. }
  1308. // otherwise try to look up the entity and make it a target.
  1309. CBaseEntity *pEnt = gEntList.FindEntityByName(NULL,targetname);
  1310. if (pEnt)
  1311. {
  1312. m_hPlayerPinPos = pEnt;
  1313. }
  1314. else
  1315. {
  1316. // if we couldn't find the target, just bail on the behavior.
  1317. Warning("Advisor tried to pin player to %s but that does not exist.\n", targetname.ToCStr());
  1318. m_hPlayerPinPos = NULL;
  1319. }
  1320. }
  1321. //-----------------------------------------------------------------------------
  1322. // Purpose:
  1323. //-----------------------------------------------------------------------------
  1324. void CNPC_Advisor::OnScheduleChange( void )
  1325. {
  1326. Write_AllBeamsOff();
  1327. m_hvStagedEnts.RemoveAll();
  1328. BaseClass::OnScheduleChange();
  1329. }
  1330. //-----------------------------------------------------------------------------
  1331. // Purpose:
  1332. //-----------------------------------------------------------------------------
  1333. void CNPC_Advisor::GatherConditions( void )
  1334. {
  1335. BaseClass::GatherConditions();
  1336. // Handle script state changes
  1337. bool bInScript = IsInAScript();
  1338. if ( ( m_bWasScripting && bInScript == false ) || ( m_bWasScripting == false && bInScript ) )
  1339. {
  1340. SetCondition( COND_ADVISOR_PHASE_INTERRUPT );
  1341. }
  1342. // Retain this
  1343. m_bWasScripting = bInScript;
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // designer hook for yanking an object into the air right now
  1347. //-----------------------------------------------------------------------------
  1348. void CNPC_Advisor::InputWrenchImmediate( inputdata_t &inputdata )
  1349. {
  1350. string_t groupname = inputdata.value.StringID();
  1351. Assert(!!groupname);
  1352. // for all entities with that name that aren't floating, punt them at me and add them to the levitation
  1353. CBaseEntity *pEnt = NULL;
  1354. const Vector &myPos = GetAbsOrigin() + Vector(0,36.0f,0);
  1355. // conditional assignment: find an entity by name and save it into pEnt. Bail out when none are left.
  1356. while ( ( pEnt = gEntList.FindEntityByName(pEnt,groupname) ) != NULL )
  1357. {
  1358. // if I'm not already levitating it, and if I didn't just throw it
  1359. if (!m_physicsObjects.HasElement(pEnt) )
  1360. {
  1361. // add to levitation
  1362. IPhysicsObject *pPhys = pEnt->VPhysicsGetObject();
  1363. if ( pPhys )
  1364. {
  1365. // if the object isn't moveable, make it so.
  1366. if ( !pPhys->IsMoveable() )
  1367. {
  1368. pPhys->EnableMotion( true );
  1369. }
  1370. // first, kick it at me
  1371. Vector objectToMe;
  1372. pPhys->GetPosition(&objectToMe,NULL);
  1373. objectToMe = myPos - objectToMe;
  1374. // compute a velocity that will get it here in about a second
  1375. objectToMe /= (1.5f * gpGlobals->frametime);
  1376. objectToMe *= random->RandomFloat(0.25f,1.0f);
  1377. pPhys->SetVelocity( &objectToMe, NULL );
  1378. // add it to tracked physics objects
  1379. m_physicsObjects.AddToTail( pEnt );
  1380. m_pLevitateController->AttachObject( pPhys, false );
  1381. pPhys->Wake();
  1382. }
  1383. else
  1384. {
  1385. Warning( "Advisor tried to wrench %s, but it is not moveable!", pEnt->GetEntityName().ToCStr());
  1386. }
  1387. }
  1388. }
  1389. }
  1390. //-----------------------------------------------------------------------------
  1391. // write a message turning a beam on
  1392. //-----------------------------------------------------------------------------
  1393. void CNPC_Advisor::Write_BeamOn( CBaseEntity *pEnt )
  1394. {
  1395. Assert( pEnt );
  1396. EntityMessageBegin( this, true );
  1397. WRITE_BYTE( ADVISOR_MSG_START_BEAM );
  1398. WRITE_LONG( pEnt->entindex() );
  1399. MessageEnd();
  1400. }
  1401. //-----------------------------------------------------------------------------
  1402. // write a message turning a beam off
  1403. //-----------------------------------------------------------------------------
  1404. void CNPC_Advisor::Write_BeamOff( CBaseEntity *pEnt )
  1405. {
  1406. Assert( pEnt );
  1407. EntityMessageBegin( this, true );
  1408. WRITE_BYTE( ADVISOR_MSG_STOP_BEAM );
  1409. WRITE_LONG( pEnt->entindex() );
  1410. MessageEnd();
  1411. }
  1412. //-----------------------------------------------------------------------------
  1413. // tell client to kill all beams
  1414. //-----------------------------------------------------------------------------
  1415. void CNPC_Advisor::Write_AllBeamsOff( void )
  1416. {
  1417. EntityMessageBegin( this, true );
  1418. WRITE_BYTE( ADVISOR_MSG_STOP_ALL_BEAMS );
  1419. MessageEnd();
  1420. }
  1421. //-----------------------------------------------------------------------------
  1422. // input wrapper around Write_BeamOn
  1423. //-----------------------------------------------------------------------------
  1424. void CNPC_Advisor::InputTurnBeamOn( inputdata_t &inputdata )
  1425. {
  1426. // inputdata should specify a target
  1427. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.StringID() );
  1428. if ( pTarget )
  1429. {
  1430. Write_BeamOn( pTarget );
  1431. }
  1432. else
  1433. {
  1434. Warning("InputTurnBeamOn could not find object %s", inputdata.value.String() );
  1435. }
  1436. }
  1437. //-----------------------------------------------------------------------------
  1438. // input wrapper around Write_BeamOff
  1439. //-----------------------------------------------------------------------------
  1440. void CNPC_Advisor::InputTurnBeamOff( inputdata_t &inputdata )
  1441. {
  1442. // inputdata should specify a target
  1443. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.StringID() );
  1444. if ( pTarget )
  1445. {
  1446. Write_BeamOff( pTarget );
  1447. }
  1448. else
  1449. {
  1450. Warning("InputTurnBeamOn could not find object %s", inputdata.value.String() );
  1451. }
  1452. }
  1453. void CNPC_Advisor::InputElightOn( inputdata_t &inputdata )
  1454. {
  1455. EntityMessageBegin( this, true );
  1456. WRITE_BYTE( ADVISOR_MSG_START_ELIGHT );
  1457. MessageEnd();
  1458. }
  1459. void CNPC_Advisor::InputElightOff( inputdata_t &inputdata )
  1460. {
  1461. EntityMessageBegin( this, true );
  1462. WRITE_BYTE( ADVISOR_MSG_STOP_ELIGHT );
  1463. MessageEnd();
  1464. }
  1465. #endif
  1466. //==============================================================================================
  1467. // MOTION CALLBACK
  1468. //==============================================================================================
  1469. CAdvisorLevitate::simresult_e CAdvisorLevitate::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  1470. {
  1471. // this function can be optimized to minimize branching if necessary (PPE branch prediction)
  1472. CNPC_Advisor *pAdvisor = static_cast<CNPC_Advisor *>(m_Advisor.Get());
  1473. Assert(pAdvisor);
  1474. if ( !OldStyle() )
  1475. { // independent movement of all objects
  1476. // if an object was recently thrown, just zero out its gravity.
  1477. if (pAdvisor->DidThrow(static_cast<CBaseEntity *>(pObject->GetGameData())))
  1478. {
  1479. linear = Vector( 0, 0, GetCurrentGravity() );
  1480. return SIM_GLOBAL_ACCELERATION;
  1481. }
  1482. else
  1483. {
  1484. Vector vel; AngularImpulse angvel;
  1485. pObject->GetVelocity(&vel,&angvel);
  1486. Vector pos;
  1487. pObject->GetPosition(&pos,NULL);
  1488. bool bMovingUp = vel.z > 0;
  1489. // if above top limit and moving up, move down. if below bottom limit and moving down, move up.
  1490. if (bMovingUp)
  1491. {
  1492. if (pos.z > m_vecGoalPos2.z)
  1493. {
  1494. // turn around move down
  1495. linear = Vector( 0, 0, Square((1.0f - m_flFloat)) * GetCurrentGravity() );
  1496. angular = Vector( 0, -5, 0 );
  1497. }
  1498. else
  1499. { // keep moving up
  1500. linear = Vector( 0, 0, (1.0f + m_flFloat) * GetCurrentGravity() );
  1501. angular = Vector( 0, 0, 10 );
  1502. }
  1503. }
  1504. else
  1505. {
  1506. if (pos.z < m_vecGoalPos1.z)
  1507. {
  1508. // turn around move up
  1509. linear = Vector( 0, 0, Square((1.0f + m_flFloat)) * GetCurrentGravity() );
  1510. angular = Vector( 0, 5, 0 );
  1511. }
  1512. else
  1513. { // keep moving down
  1514. linear = Vector( 0, 0, (1.0f - m_flFloat) * GetCurrentGravity() );
  1515. angular = Vector( 0, 0, 10 );
  1516. }
  1517. }
  1518. return SIM_GLOBAL_ACCELERATION;
  1519. }
  1520. //NDebugOverlay::Cross3D(pos,24.0f,255,255,0,true,0.04f);
  1521. }
  1522. else // old stateless technique
  1523. {
  1524. Warning("Advisor using old-style object movement!\n");
  1525. /* // obsolete
  1526. CBaseEntity *pEnt = (CBaseEntity *)pObject->GetGameData();
  1527. Vector vecDir1 = m_vecGoalPos1 - pEnt->GetAbsOrigin();
  1528. VectorNormalize( vecDir1 );
  1529. Vector vecDir2 = m_vecGoalPos2 - pEnt->GetAbsOrigin();
  1530. VectorNormalize( vecDir2 );
  1531. */
  1532. linear = Vector( 0, 0, m_flFloat * GetCurrentGravity() );// + m_flFloat * 0.5 * ( vecDir1 + vecDir2 );
  1533. angular = Vector( 0, 0, 10 );
  1534. return SIM_GLOBAL_ACCELERATION;
  1535. }
  1536. }
  1537. //==============================================================================================
  1538. // ADVISOR PHYSICS DAMAGE TABLE
  1539. //==============================================================================================
  1540. static impactentry_t advisorLinearTable[] =
  1541. {
  1542. { 100*100, 10 },
  1543. { 250*250, 25 },
  1544. { 350*350, 50 },
  1545. { 500*500, 75 },
  1546. { 1000*1000,100 },
  1547. };
  1548. static impactentry_t advisorAngularTable[] =
  1549. {
  1550. { 50* 50, 10 },
  1551. { 100*100, 25 },
  1552. { 150*150, 50 },
  1553. { 200*200, 75 },
  1554. };
  1555. static impactdamagetable_t gAdvisorImpactDamageTable =
  1556. {
  1557. advisorLinearTable,
  1558. advisorAngularTable,
  1559. ARRAYSIZE(advisorLinearTable),
  1560. ARRAYSIZE(advisorAngularTable),
  1561. 200*200,// minimum linear speed squared
  1562. 180*180,// minimum angular speed squared (360 deg/s to cause spin/slice damage)
  1563. 15, // can't take damage from anything under 15kg
  1564. 10, // anything less than 10kg is "small"
  1565. 5, // never take more than 1 pt of damage from anything under 15kg
  1566. 128*128,// <15kg objects must go faster than 36 in/s to do damage
  1567. 45, // large mass in kg
  1568. 2, // large mass scale (anything over 500kg does 4X as much energy to read from damage table)
  1569. 1, // large mass falling scale
  1570. 0, // my min velocity
  1571. };
  1572. //-----------------------------------------------------------------------------
  1573. // Purpose:
  1574. // Output : const impactdamagetable_t
  1575. //-----------------------------------------------------------------------------
  1576. const impactdamagetable_t &CNPC_Advisor::GetPhysicsImpactDamageTable( void )
  1577. {
  1578. return advisor_use_impact_table.GetBool() ? gAdvisorImpactDamageTable : BaseClass::GetPhysicsImpactDamageTable();
  1579. }
  1580. #if NPC_ADVISOR_HAS_BEHAVIOR
  1581. //-----------------------------------------------------------------------------
  1582. //
  1583. // Schedules
  1584. //
  1585. //-----------------------------------------------------------------------------
  1586. AI_BEGIN_CUSTOM_NPC( npc_advisor, CNPC_Advisor )
  1587. DECLARE_TASK( TASK_ADVISOR_FIND_OBJECTS )
  1588. DECLARE_TASK( TASK_ADVISOR_LEVITATE_OBJECTS )
  1589. /*
  1590. DECLARE_TASK( TASK_ADVISOR_PICK_THROW_OBJECT )
  1591. DECLARE_TASK( TASK_ADVISOR_THROW_OBJECT )
  1592. */
  1593. DECLARE_CONDITION( COND_ADVISOR_PHASE_INTERRUPT ) // A stage has interrupted us
  1594. DECLARE_TASK( TASK_ADVISOR_STAGE_OBJECTS ) // haul all the objects into the throw-from slots
  1595. DECLARE_TASK( TASK_ADVISOR_BARRAGE_OBJECTS ) // hurl all the objects in sequence
  1596. DECLARE_TASK( TASK_ADVISOR_PIN_PLAYER ) // pinion the player to a point in space
  1597. //=========================================================
  1598. DEFINE_SCHEDULE
  1599. (
  1600. SCHED_ADVISOR_COMBAT,
  1601. " Tasks"
  1602. " TASK_ADVISOR_FIND_OBJECTS 0"
  1603. " TASK_ADVISOR_LEVITATE_OBJECTS 0"
  1604. " TASK_ADVISOR_STAGE_OBJECTS 1"
  1605. " TASK_ADVISOR_BARRAGE_OBJECTS 0"
  1606. " "
  1607. " Interrupts"
  1608. " COND_ADVISOR_PHASE_INTERRUPT"
  1609. " COND_ENEMY_DEAD"
  1610. )
  1611. //=========================================================
  1612. DEFINE_SCHEDULE
  1613. (
  1614. SCHED_ADVISOR_IDLE_STAND,
  1615. " Tasks"
  1616. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1617. " TASK_WAIT 3"
  1618. ""
  1619. " Interrupts"
  1620. " COND_NEW_ENEMY"
  1621. " COND_SEE_FEAR"
  1622. " COND_ADVISOR_PHASE_INTERRUPT"
  1623. )
  1624. DEFINE_SCHEDULE
  1625. (
  1626. SCHED_ADVISOR_TOSS_PLAYER,
  1627. " Tasks"
  1628. " TASK_ADVISOR_FIND_OBJECTS 0"
  1629. " TASK_ADVISOR_LEVITATE_OBJECTS 0"
  1630. " TASK_ADVISOR_PIN_PLAYER 0"
  1631. " "
  1632. " Interrupts"
  1633. )
  1634. AI_END_CUSTOM_NPC()
  1635. #endif