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.

1693 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #undef strncpy // we use std::string below that needs a good strncpy define
  7. #undef sprintf // "
  8. #include "cbase.h"
  9. #include "ai_behavior_lead.h"
  10. #include "ai_goalentity.h"
  11. #include "ai_navigator.h"
  12. #include "ai_speech.h"
  13. #include "ai_senses.h"
  14. #include "ai_playerally.h"
  15. #include "ai_route.h"
  16. #include "ai_pathfinder.h"
  17. #include "sceneentity.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. // Minimum time between leader nags
  21. #define LEAD_NAG_TIME 3.0
  22. #define LEAD_MIN_RETRIEVEDIST_OFFSET 24
  23. //-----------------------------------------------------------------------------
  24. // class CAI_LeadBehavior
  25. //
  26. // Purpose:
  27. //
  28. //-----------------------------------------------------------------------------
  29. BEGIN_SIMPLE_DATADESC( AI_LeadArgs_t )
  30. // Only the flags needs saving
  31. DEFINE_FIELD( flags, FIELD_INTEGER ),
  32. //DEFINE_FIELD( pszGoal, FIELD_STRING ),
  33. //DEFINE_FIELD( pszWaitPoint, FIELD_STRING ),
  34. //DEFINE_FIELD( flWaitDistance, FIELD_FLOAT ),
  35. //DEFINE_FIELD( flLeadDistance, FIELD_FLOAT ),
  36. //DEFINE_FIELD( flRetrieveDistance, FIELD_FLOAT ),
  37. //DEFINE_FIELD( flSuccessDistance, FIELD_FLOAT ),
  38. //DEFINE_FIELD( bRun, FIELD_BOOLEAN ),
  39. //DEFINE_FIELD( bDontSpeakStart, FIELD_BOOLEAN ),
  40. //DEFINE_FIELD( bGagLeader, FIELD_BOOLEAN ),
  41. DEFINE_FIELD( iRetrievePlayer, FIELD_INTEGER ),
  42. DEFINE_FIELD( iRetrieveWaitForSpeak, FIELD_INTEGER ),
  43. DEFINE_FIELD( iComingBackWaitForSpeak, FIELD_INTEGER ),
  44. DEFINE_FIELD( bStopScenesWhenPlayerLost, FIELD_BOOLEAN ),
  45. DEFINE_FIELD( bLeadDuringCombat, FIELD_BOOLEAN ),
  46. END_DATADESC();
  47. BEGIN_DATADESC( CAI_LeadBehavior )
  48. DEFINE_EMBEDDED( m_args ),
  49. // m_pSink (reconnected on load)
  50. DEFINE_FIELD( m_hSinkImplementor, FIELD_EHANDLE ),
  51. DEFINE_FIELD( m_goal, FIELD_POSITION_VECTOR ),
  52. DEFINE_FIELD( m_goalyaw, FIELD_FLOAT ),
  53. DEFINE_FIELD( m_waitpoint, FIELD_POSITION_VECTOR ),
  54. DEFINE_FIELD( m_waitdistance, FIELD_FLOAT ),
  55. DEFINE_FIELD( m_leaddistance, FIELD_FLOAT ),
  56. DEFINE_FIELD( m_retrievedistance, FIELD_FLOAT ),
  57. DEFINE_FIELD( m_successdistance, FIELD_FLOAT ),
  58. DEFINE_FIELD( m_weaponname, FIELD_STRING ),
  59. DEFINE_FIELD( m_run, FIELD_BOOLEAN ),
  60. DEFINE_FIELD( m_gagleader, FIELD_BOOLEAN ),
  61. DEFINE_FIELD( m_hasspokenstart, FIELD_BOOLEAN ),
  62. DEFINE_FIELD( m_hasspokenarrival, FIELD_BOOLEAN ),
  63. DEFINE_FIELD( m_hasPausedScenes, FIELD_BOOLEAN ),
  64. DEFINE_FIELD( m_flSpeakNextNagTime, FIELD_TIME ),
  65. DEFINE_FIELD( m_flWeaponSafetyTimeOut, FIELD_TIME ),
  66. DEFINE_FIELD( m_flNextLeadIdle, FIELD_TIME ),
  67. DEFINE_FIELD( m_bInitialAheadTest, FIELD_BOOLEAN ),
  68. DEFINE_EMBEDDED( m_MoveMonitor ),
  69. DEFINE_EMBEDDED( m_LostTimer ),
  70. DEFINE_EMBEDDED( m_LostLOSTimer ),
  71. END_DATADESC();
  72. //-----------------------------------------------------------------------------
  73. void CAI_LeadBehavior::OnRestore()
  74. {
  75. CBaseEntity *pSinkImplementor = m_hSinkImplementor;
  76. if ( pSinkImplementor )
  77. {
  78. m_pSink = dynamic_cast<CAI_LeadBehaviorHandler *>(pSinkImplementor);
  79. if ( !m_pSink )
  80. {
  81. DevMsg( "Failed to reconnect to CAI_LeadBehaviorHandler\n" );
  82. m_hSinkImplementor = NULL;
  83. }
  84. }
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Draw any text overlays
  88. // Input : Previous text offset from the top
  89. // Output : Current text offset from the top
  90. //-----------------------------------------------------------------------------
  91. int CAI_LeadBehavior::DrawDebugTextOverlays( int text_offset )
  92. {
  93. char tempstr[ 512 ];
  94. int offset;
  95. offset = BaseClass::DrawDebugTextOverlays( text_offset );
  96. if ( GetOuter()->m_debugOverlays & OVERLAY_TEXT_BIT )
  97. {
  98. if ( HasGoal() )
  99. {
  100. Q_snprintf( tempstr, sizeof(tempstr), "Goal: %s %s", m_args.pszGoal, VecToString( m_goal ) );
  101. GetOuter()->EntityText( offset, tempstr, 0 );
  102. offset++;
  103. }
  104. else
  105. {
  106. Q_snprintf( tempstr, sizeof(tempstr), "Goal: None" );
  107. GetOuter()->EntityText( offset, tempstr, 0 );
  108. offset++;
  109. }
  110. }
  111. return offset;
  112. }
  113. //-----------------------------------------------------------------------------
  114. //-----------------------------------------------------------------------------
  115. bool CAI_LeadBehavior::IsNavigationUrgent( void )
  116. {
  117. #if defined( HL2_DLL )
  118. if( HasGoal() && !hl2_episodic.GetBool() )
  119. {
  120. return (GetOuter()->Classify() == CLASS_PLAYER_ALLY_VITAL);
  121. }
  122. #endif
  123. return BaseClass::IsNavigationUrgent();
  124. }
  125. //-------------------------------------
  126. void CAI_LeadBehavior::LeadPlayer( const AI_LeadArgs_t &leadArgs, CAI_LeadBehaviorHandler *pSink )
  127. {
  128. #ifndef CSTRIKE_DLL
  129. CAI_PlayerAlly *pOuter = dynamic_cast<CAI_PlayerAlly*>(GetOuter());
  130. if ( pOuter && AI_IsSinglePlayer() )
  131. {
  132. pOuter->SetSpeechTarget( UTIL_GetLocalPlayer() );
  133. }
  134. #endif
  135. if( SetGoal( leadArgs ) )
  136. {
  137. SetCondition( COND_PROVOKED );
  138. Connect( pSink );
  139. NotifyChangeBehaviorStatus();
  140. }
  141. else
  142. {
  143. DevMsg( "*** Warning! LeadPlayer() has a NULL Goal Ent\n" );
  144. }
  145. }
  146. //-------------------------------------
  147. void CAI_LeadBehavior::StopLeading( void )
  148. {
  149. ClearGoal();
  150. m_pSink = NULL;
  151. NotifyChangeBehaviorStatus();
  152. }
  153. //-------------------------------------
  154. bool CAI_LeadBehavior::CanSelectSchedule()
  155. {
  156. if ( !AI_GetSinglePlayer() || AI_GetSinglePlayer()->IsDead() )
  157. return false;
  158. bool fAttacked = ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) );
  159. bool fNonCombat = ( GetNpcState() == NPC_STATE_IDLE || GetNpcState() == NPC_STATE_ALERT );
  160. return ( !fAttacked && (fNonCombat || m_args.bLeadDuringCombat) && HasGoal() );
  161. }
  162. //-------------------------------------
  163. void CAI_LeadBehavior::BeginScheduleSelection()
  164. {
  165. SetTarget( AI_GetSinglePlayer() );
  166. CAI_Expresser *pExpresser = GetOuter()->GetExpresser();
  167. if ( pExpresser )
  168. pExpresser->ClearSpokeConcept( TLK_LEAD_ARRIVAL );
  169. }
  170. //-------------------------------------
  171. bool CAI_LeadBehavior::SetGoal( const AI_LeadArgs_t &args )
  172. {
  173. CBaseEntity *pGoalEnt;
  174. pGoalEnt = gEntList.FindEntityByName( NULL, args.pszGoal );
  175. if ( !pGoalEnt )
  176. return false;
  177. m_args = args; // @Q (toml 08-13-02): need to copy string?
  178. m_goal = pGoalEnt->GetLocalOrigin();
  179. m_goalyaw = (args.flags & AILF_USE_GOAL_FACING) ? pGoalEnt->GetLocalAngles().y : -1;
  180. m_waitpoint = vec3_origin;
  181. m_waitdistance = args.flWaitDistance;
  182. m_leaddistance = args.flLeadDistance ? args.flLeadDistance : 64;
  183. m_retrievedistance = args.flRetrieveDistance ? args.flRetrieveDistance : (m_leaddistance + LEAD_MIN_RETRIEVEDIST_OFFSET);
  184. m_successdistance = args.flSuccessDistance ? args.flSuccessDistance : 0;
  185. m_run = args.bRun;
  186. m_gagleader = args.bGagLeader;
  187. m_hasspokenstart = args.bDontSpeakStart;
  188. m_hasspokenarrival = false;
  189. m_hasPausedScenes = false;
  190. m_flSpeakNextNagTime = 0;
  191. m_flWeaponSafetyTimeOut = 0;
  192. m_flNextLeadIdle = gpGlobals->curtime + 10;
  193. m_bInitialAheadTest = true;
  194. if ( args.pszWaitPoint && args.pszWaitPoint[0] )
  195. {
  196. CBaseEntity *pWaitPoint = gEntList.FindEntityByName( NULL, args.pszWaitPoint );
  197. if ( pWaitPoint )
  198. {
  199. m_waitpoint = pWaitPoint->GetLocalOrigin();
  200. }
  201. }
  202. return true;
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. bool CAI_LeadBehavior::GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint )
  208. {
  209. AI_Waypoint_t *waypoint = GetOuter()->GetNavigator()->GetPath()->GetCurWaypoint();
  210. AI_Waypoint_t *builtwaypoints = NULL;
  211. if ( !waypoint )
  212. {
  213. // We arrive here twice when lead behaviour starts:
  214. // - When the lead behaviour is first enabled. We have no schedule. We want to know if the player is ahead of us.
  215. // - A frame later when we've chosen to lead the player, but we still haven't built our route. We know that the
  216. // the player isn't lagging, so it's safe to go ahead and simply say he's ahead of us. This avoids building
  217. // the temp route twice.
  218. if ( IsCurSchedule( SCHED_LEAD_PLAYER, false ) )
  219. return true;
  220. // Build a temp route to the gold and use that
  221. builtwaypoints = GetOuter()->GetPathfinder()->BuildRoute( GetOuter()->GetAbsOrigin(), m_goal, NULL, GetOuter()->GetDefaultNavGoalTolerance(), GetOuter()->GetNavType(), true );
  222. if ( !builtwaypoints )
  223. return false;
  224. GetOuter()->GetPathfinder()->UnlockRouteNodes( builtwaypoints );
  225. waypoint = builtwaypoints;
  226. }
  227. // Find the nearest node to the target (going forward)
  228. float flNearestDist2D = 999999999;
  229. float flNearestDist = 999999999;
  230. float flPathDist, flPathDist2D;
  231. Vector vecNearestPoint(0, 0, 0);
  232. Vector vecPrevPos = GetOuter()->GetAbsOrigin();
  233. for ( ; (waypoint != NULL) ; waypoint = waypoint->GetNext() )
  234. {
  235. // Find the closest point on the line segment on the path
  236. Vector vecClosest;
  237. CalcClosestPointOnLineSegment( targetPos, vecPrevPos, waypoint->GetPos(), vecClosest );
  238. /*
  239. if ( builtwaypoints )
  240. {
  241. NDebugOverlay::Line( vecPrevPos, waypoint->GetPos(), 0,0,255,true, 10.0 );
  242. }
  243. */
  244. vecPrevPos = waypoint->GetPos();
  245. // Find the distance between this test point and our goal point
  246. flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() );
  247. if ( flPathDist2D > flNearestDist2D )
  248. continue;
  249. flPathDist = vecClosest.z - targetPos.z;
  250. flPathDist *= flPathDist;
  251. flPathDist += flPathDist2D;
  252. if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist ))
  253. continue;
  254. flNearestDist2D = flPathDist2D;
  255. flNearestDist = flPathDist;
  256. vecNearestPoint = vecClosest;
  257. }
  258. if ( builtwaypoints )
  259. {
  260. //NDebugOverlay::Line( vecNearestPoint, targetPos, 0,255,0,true, 10.0 );
  261. DeleteAll( builtwaypoints );
  262. }
  263. *pVecClosestPoint = vecNearestPoint;
  264. return true;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Return true if the player is further ahead on the lead route than I am
  268. //-----------------------------------------------------------------------------
  269. bool CAI_LeadBehavior::PlayerIsAheadOfMe( bool bForce )
  270. {
  271. // Find the nearest point on our route to the player, and see if that's further
  272. // ahead of us than our nearest point.
  273. // If we're not leading, our route doesn't lead to the goal, so we can't use it.
  274. // If we just started leading, go ahead and test, and we'll build a temp route.
  275. if ( !m_bInitialAheadTest && !IsCurSchedule( SCHED_LEAD_PLAYER, false ) && !bForce )
  276. return false;
  277. m_bInitialAheadTest = false;
  278. Vector vecClosestPoint;
  279. if ( GetClosestPointOnRoute( AI_GetSinglePlayer()->GetAbsOrigin(), &vecClosestPoint ) )
  280. {
  281. // If the closest point is not right next to me, then
  282. // the player is somewhere ahead of me on the route.
  283. if ( (vecClosestPoint - GetOuter()->GetAbsOrigin()).LengthSqr() > (32*32) )
  284. return true;
  285. }
  286. return false;
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. //-----------------------------------------------------------------------------
  291. void CAI_LeadBehavior::GatherConditions( void )
  292. {
  293. BaseClass::GatherConditions();
  294. if ( HasGoal() )
  295. {
  296. // Fix for bad transition case (to investigate)
  297. if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() > (64*64) && IsCurSchedule( SCHED_LEAD_AWAIT_SUCCESS, false) )
  298. {
  299. GetOuter()->ClearSchedule( "Lead behavior - bad transition?" );
  300. }
  301. // We have to collect data about the person we're leading around.
  302. CBaseEntity *pFollower = AI_GetSinglePlayer();
  303. if( pFollower )
  304. {
  305. ClearCondition( COND_LEAD_FOLLOWER_VERY_CLOSE );
  306. ClearCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME );
  307. // Check distance to the follower
  308. float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length();
  309. bool bLagging = flFollowerDist > (m_leaddistance*4);
  310. if ( bLagging )
  311. {
  312. if ( PlayerIsAheadOfMe() )
  313. {
  314. bLagging = false;
  315. }
  316. }
  317. // Player heading towards me?
  318. // Only factor this in if you're not too far from them
  319. if ( flFollowerDist < (m_leaddistance*4) )
  320. {
  321. Vector vecVelocity = pFollower->GetSmoothedVelocity();
  322. if ( VectorNormalize(vecVelocity) > 50 )
  323. {
  324. Vector vecToPlayer = (GetAbsOrigin() - pFollower->GetAbsOrigin());
  325. VectorNormalize( vecToPlayer );
  326. if ( DotProduct( vecVelocity, vecToPlayer ) > 0.5 )
  327. {
  328. SetCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME );
  329. bLagging = false;
  330. }
  331. }
  332. }
  333. // If he's outside our lag range, consider him lagging
  334. if ( bLagging )
  335. {
  336. SetCondition( COND_LEAD_FOLLOWER_LAGGING );
  337. ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );
  338. }
  339. else
  340. {
  341. ClearCondition( COND_LEAD_FOLLOWER_LAGGING );
  342. SetCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );
  343. // If he's really close, note that
  344. if ( flFollowerDist < m_leaddistance )
  345. {
  346. SetCondition( COND_LEAD_FOLLOWER_VERY_CLOSE );
  347. }
  348. }
  349. // To be considered not lagging, the follower must be visible, and within the lead distance
  350. if ( GetOuter()->FVisible( pFollower ) && GetOuter()->GetSenses()->ShouldSeeEntity( pFollower ) )
  351. {
  352. SetCondition( COND_LEAD_HAVE_FOLLOWER_LOS );
  353. m_LostLOSTimer.Stop();
  354. }
  355. else
  356. {
  357. ClearCondition( COND_LEAD_HAVE_FOLLOWER_LOS );
  358. // We don't have a LOS. But if we did have LOS, don't clear it until the timer is up.
  359. if ( m_LostLOSTimer.IsRunning() )
  360. {
  361. if ( m_LostLOSTimer.Expired() )
  362. {
  363. SetCondition( COND_LEAD_FOLLOWER_LAGGING );
  364. ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );
  365. }
  366. }
  367. else
  368. {
  369. m_LostLOSTimer.Start();
  370. }
  371. }
  372. // Now we want to see if the follower is lost. Being lost means being (far away || out of LOS )
  373. // && some time has passed. Also, lagging players are considered lost if the NPC's never delivered
  374. // the start speech, because it means the NPC should run to the player to start the lead.
  375. if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) )
  376. {
  377. if ( !m_hasspokenstart )
  378. {
  379. SetCondition( COND_LEAD_FOLLOWER_LOST );
  380. }
  381. else
  382. {
  383. if ( m_args.bStopScenesWhenPlayerLost )
  384. {
  385. // Try and stop me speaking my monolog, if I am
  386. if ( !m_hasPausedScenes && IsRunningScriptedScene( GetOuter() ) )
  387. {
  388. //Msg("Stopping scenes.\n");
  389. PauseActorsScriptedScenes( GetOuter(), false );
  390. m_hasPausedScenes = true;
  391. }
  392. }
  393. if( m_LostTimer.IsRunning() )
  394. {
  395. if( m_LostTimer.Expired() )
  396. {
  397. SetCondition( COND_LEAD_FOLLOWER_LOST );
  398. }
  399. }
  400. else
  401. {
  402. m_LostTimer.Start();
  403. }
  404. }
  405. }
  406. else
  407. {
  408. // If I was speaking a monolog, resume it
  409. if ( m_args.bStopScenesWhenPlayerLost && m_hasPausedScenes )
  410. {
  411. if ( IsRunningScriptedScene( GetOuter() ) )
  412. {
  413. //Msg("Resuming scenes.\n");
  414. ResumeActorsScriptedScenes( GetOuter(), false );
  415. }
  416. m_hasPausedScenes = false;
  417. }
  418. m_LostTimer.Stop();
  419. ClearCondition( COND_LEAD_FOLLOWER_LOST );
  420. }
  421. // Evaluate for success
  422. // Success right now means being stationary, close to the goal, and having the player close by
  423. if ( !( m_args.flags & AILF_NO_DEF_SUCCESS ) )
  424. {
  425. ClearCondition( COND_LEAD_SUCCESS );
  426. // Check Z first, and only check 2d if we're within that
  427. bool bWithinZ = fabs(GetLocalOrigin().z - m_goal.z) < 64;
  428. if ( bWithinZ && (GetLocalOrigin() - m_goal).Length2D() <= 64 )
  429. {
  430. if ( HasCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ) )
  431. {
  432. SetCondition( COND_LEAD_SUCCESS );
  433. }
  434. else if ( m_successdistance )
  435. {
  436. float flDistSqr = (pFollower->GetAbsOrigin() - GetLocalOrigin()).Length2DSqr();
  437. if ( flDistSqr < (m_successdistance*m_successdistance) )
  438. {
  439. SetCondition( COND_LEAD_SUCCESS );
  440. }
  441. }
  442. }
  443. }
  444. if ( m_MoveMonitor.IsMarkSet() && m_MoveMonitor.TargetMoved( pFollower ) )
  445. SetCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK );
  446. else
  447. ClearCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK );
  448. }
  449. }
  450. if( m_args.bLeadDuringCombat )
  451. {
  452. ClearCondition( COND_LIGHT_DAMAGE );
  453. ClearCondition( COND_HEAVY_DAMAGE );
  454. }
  455. }
  456. //-------------------------------------
  457. int CAI_LeadBehavior::SelectSchedule()
  458. {
  459. if ( HasGoal() )
  460. {
  461. if( HasCondition(COND_LEAD_SUCCESS) )
  462. {
  463. return SCHED_LEAD_SUCCEED;
  464. }
  465. // Player's here, but does he have the weapon we want him to have?
  466. if ( m_weaponname != NULL_STRING )
  467. {
  468. CBasePlayer *pFollower = AI_GetSinglePlayer();
  469. if ( pFollower && !pFollower->Weapon_OwnsThisType( STRING(m_weaponname) ) )
  470. {
  471. // If the safety timeout has run out, just give the player the weapon
  472. if ( !m_flWeaponSafetyTimeOut || (m_flWeaponSafetyTimeOut > gpGlobals->curtime) )
  473. return SCHED_LEAD_PLAYERNEEDSWEAPON;
  474. string_t iszItem = AllocPooledString( "weapon_bugbait" );
  475. pFollower->GiveNamedItem( STRING(iszItem) );
  476. }
  477. }
  478. // If we have a waitpoint, we want to wait at it for the player.
  479. if( HasWaitPoint() && !PlayerIsAheadOfMe( true ) )
  480. {
  481. bool bKeepWaiting = true;
  482. // If we have no wait distance, trigger as soon as the player comes in view
  483. if ( !m_waitdistance )
  484. {
  485. if ( HasCondition( COND_SEE_PLAYER ) )
  486. {
  487. // We've spotted the player, so stop waiting
  488. bKeepWaiting = false;
  489. }
  490. }
  491. else
  492. {
  493. // We have to collect data about the person we're leading around.
  494. CBaseEntity *pFollower = AI_GetSinglePlayer();
  495. if( pFollower )
  496. {
  497. float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length();
  498. if ( flFollowerDist < m_waitdistance )
  499. {
  500. bKeepWaiting = false;
  501. }
  502. }
  503. }
  504. // Player still not here?
  505. if ( bKeepWaiting )
  506. return SCHED_LEAD_WAITFORPLAYER;
  507. // We're finished waiting
  508. m_waitpoint = vec3_origin;
  509. Speak( TLK_LEAD_WAITOVER );
  510. // Don't speak the start line, because we've said
  511. m_hasspokenstart = true;
  512. return SCHED_WAIT_FOR_SPEAK_FINISH;
  513. }
  514. // If we haven't spoken our start speech, do that first
  515. if ( !m_hasspokenstart )
  516. {
  517. if ( HasCondition(COND_LEAD_HAVE_FOLLOWER_LOS) && HasCondition(COND_LEAD_FOLLOWER_VERY_CLOSE) )
  518. return SCHED_LEAD_SPEAK_START;
  519. // We haven't spoken to him, and we still need to. Go get him.
  520. return SCHED_LEAD_RETRIEVE;
  521. }
  522. if( HasCondition( COND_LEAD_FOLLOWER_LOST ) )
  523. {
  524. if( m_args.iRetrievePlayer )
  525. {
  526. // If not, we want to go get the player.
  527. DevMsg( GetOuter(), "Follower lost. Spoke COMING_BACK.\n");
  528. Speak( TLK_LEAD_COMINGBACK );
  529. m_MoveMonitor.ClearMark();
  530. // If we spoke something, wait for it to finish
  531. if ( m_args.iComingBackWaitForSpeak && IsSpeaking() )
  532. return SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER;
  533. return SCHED_LEAD_RETRIEVE;
  534. }
  535. else
  536. {
  537. // Just stay right here and wait.
  538. return SCHED_LEAD_WAITFORPLAYERIDLE;
  539. }
  540. }
  541. if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) )
  542. {
  543. DevMsg( GetOuter(), "Follower lagging. Spoke CATCHUP.\n");
  544. Speak( TLK_LEAD_CATCHUP );
  545. return SCHED_LEAD_PAUSE;
  546. }
  547. else
  548. {
  549. // If we're at the goal, wait for the player to get here
  550. if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() < (64*64) )
  551. return SCHED_LEAD_AWAIT_SUCCESS;
  552. // If we were retrieving the player, speak the resume
  553. if ( IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) || IsCurSchedule( SCHED_LEAD_WAITFORPLAYERIDLE, false ) )
  554. {
  555. Speak( TLK_LEAD_RETRIEVE );
  556. // If we spoke something, wait for it to finish, if the mapmakers wants us to
  557. if ( m_args.iRetrieveWaitForSpeak && IsSpeaking() )
  558. return SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER;
  559. }
  560. DevMsg( GetOuter(), "Leading Follower.\n");
  561. return SCHED_LEAD_PLAYER;
  562. }
  563. }
  564. return BaseClass::SelectSchedule();
  565. }
  566. //-------------------------------------
  567. int CAI_LeadBehavior::TranslateSchedule( int scheduleType )
  568. {
  569. bool bInCombat = (m_args.bLeadDuringCombat && GetOuter()->GetState() == NPC_STATE_COMBAT);
  570. switch( scheduleType )
  571. {
  572. case SCHED_LEAD_PAUSE:
  573. if( bInCombat )
  574. return SCHED_LEAD_PAUSE_COMBAT;
  575. break;
  576. }
  577. return BaseClass::TranslateSchedule( scheduleType );
  578. }
  579. //-------------------------------------
  580. bool CAI_LeadBehavior::IsCurTaskContinuousMove()
  581. {
  582. const Task_t *pCurTask = GetCurTask();
  583. if ( pCurTask && pCurTask->iTask == TASK_LEAD_MOVE_TO_RANGE )
  584. return true;
  585. return BaseClass::IsCurTaskContinuousMove();
  586. }
  587. //-------------------------------------
  588. void CAI_LeadBehavior::StartTask( const Task_t *pTask )
  589. {
  590. switch ( pTask->iTask )
  591. {
  592. case TASK_LEAD_FACE_GOAL:
  593. {
  594. if ( m_goalyaw != -1 )
  595. {
  596. GetMotor()->SetIdealYaw( m_goalyaw );
  597. }
  598. TaskComplete();
  599. break;
  600. }
  601. case TASK_LEAD_SUCCEED:
  602. {
  603. Speak( TLK_LEAD_SUCCESS );
  604. NotifyEvent( LBE_SUCCESS );
  605. break;
  606. }
  607. case TASK_LEAD_ARRIVE:
  608. {
  609. // Only speak the first time we arrive
  610. if ( !m_hasspokenarrival )
  611. {
  612. Speak( TLK_LEAD_ARRIVAL );
  613. NotifyEvent( LBE_ARRIVAL );
  614. m_hasspokenarrival = true;
  615. }
  616. else
  617. {
  618. TaskComplete();
  619. }
  620. break;
  621. }
  622. case TASK_STOP_LEADING:
  623. {
  624. ClearGoal();
  625. TaskComplete();
  626. break;
  627. }
  628. case TASK_GET_PATH_TO_LEAD_GOAL:
  629. {
  630. if ( GetNavigator()->SetGoal( m_goal ) )
  631. {
  632. TaskComplete();
  633. }
  634. else
  635. {
  636. TaskFail("NO PATH");
  637. }
  638. break;
  639. }
  640. case TASK_LEAD_GET_PATH_TO_WAITPOINT:
  641. {
  642. if ( GetNavigator()->SetGoal( m_waitpoint ) )
  643. {
  644. TaskComplete();
  645. }
  646. else
  647. {
  648. TaskFail("NO PATH");
  649. }
  650. break;
  651. }
  652. case TASK_LEAD_WALK_PATH:
  653. {
  654. // If we're leading, and we're supposed to run, run instead of walking
  655. if ( m_run &&
  656. ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) )
  657. {
  658. ChainStartTask( TASK_RUN_PATH );
  659. }
  660. else
  661. {
  662. ChainStartTask( TASK_WALK_PATH );
  663. }
  664. break;
  665. }
  666. case TASK_LEAD_WAVE_TO_PLAYER:
  667. {
  668. // Wave to the player if we can see him. Otherwise, just idle.
  669. if ( HasCondition( COND_SEE_PLAYER ) )
  670. {
  671. Speak( TLK_LEAD_ATTRACTPLAYER );
  672. if ( HaveSequenceForActivity(ACT_SIGNAL1) )
  673. {
  674. SetActivity(ACT_SIGNAL1);
  675. }
  676. }
  677. else
  678. {
  679. SetActivity(ACT_IDLE);
  680. }
  681. TaskComplete();
  682. break;
  683. }
  684. case TASK_LEAD_PLAYER_NEEDS_WEAPON:
  685. {
  686. float flAvailableTime = GetOuter()->GetExpresser()->GetSemaphoreAvailableTime( GetOuter() );
  687. // if someone else is talking, don't speak
  688. if ( flAvailableTime <= gpGlobals->curtime )
  689. {
  690. Speak( TLK_LEAD_MISSINGWEAPON );
  691. }
  692. SetActivity(ACT_IDLE);
  693. TaskComplete();
  694. break;
  695. }
  696. case TASK_LEAD_SPEAK_START:
  697. {
  698. m_hasspokenstart = true;
  699. Speak( TLK_LEAD_START );
  700. SetActivity(ACT_IDLE);
  701. TaskComplete();
  702. break;
  703. }
  704. case TASK_LEAD_MOVE_TO_RANGE:
  705. {
  706. // If we haven't spoken our start speech, move closer
  707. if ( !m_hasspokenstart)
  708. {
  709. ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 );
  710. }
  711. else
  712. {
  713. ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance );
  714. }
  715. break;
  716. }
  717. case TASK_LEAD_RETRIEVE_WAIT:
  718. {
  719. m_MoveMonitor.SetMark( AI_GetSinglePlayer(), 24 );
  720. ChainStartTask( TASK_WAIT_INDEFINITE );
  721. break;
  722. }
  723. case TASK_STOP_MOVING:
  724. {
  725. BaseClass::StartTask( pTask);
  726. if ( IsCurSchedule( SCHED_LEAD_PAUSE, false ) && pTask->flTaskData == 1 )
  727. {
  728. GetNavigator()->SetArrivalDirection( GetTarget() );
  729. }
  730. break;
  731. }
  732. case TASK_WAIT_FOR_SPEAK_FINISH:
  733. {
  734. BaseClass::StartTask( pTask);
  735. if( GetOuter()->GetState() == NPC_STATE_COMBAT )
  736. {
  737. // Don't stand around jabbering in combat.
  738. TaskComplete();
  739. }
  740. // If we're not supposed to wait for the player, don't wait for speech to finish.
  741. // Instead, just wait a wee tad, and then start moving. NPC will speak on the go.
  742. if ( TaskIsRunning() && !m_args.iRetrievePlayer )
  743. {
  744. if ( gpGlobals->curtime - GetOuter()->GetTimeTaskStarted() > 0.3 )
  745. {
  746. TaskComplete();
  747. }
  748. }
  749. break;
  750. }
  751. default:
  752. BaseClass::StartTask( pTask);
  753. }
  754. }
  755. //-------------------------------------
  756. void CAI_LeadBehavior::RunTask( const Task_t *pTask )
  757. {
  758. switch ( pTask->iTask )
  759. {
  760. case TASK_LEAD_SUCCEED:
  761. {
  762. if ( !IsSpeaking() )
  763. {
  764. TaskComplete();
  765. NotifyEvent( LBE_DONE );
  766. }
  767. break;
  768. }
  769. case TASK_LEAD_ARRIVE:
  770. {
  771. if ( !IsSpeaking() )
  772. {
  773. TaskComplete();
  774. NotifyEvent( LBE_ARRIVAL_DONE );
  775. }
  776. break;
  777. }
  778. case TASK_LEAD_MOVE_TO_RANGE:
  779. {
  780. // If we haven't spoken our start speech, move closer
  781. if ( !m_hasspokenstart)
  782. {
  783. ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 );
  784. }
  785. else
  786. {
  787. ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance );
  788. if ( !TaskIsComplete() )
  789. {
  790. // Transition to a walk when we get near the player
  791. // Check Z first, and only check 2d if we're within that
  792. Vector vecGoalPos = GetNavigator()->GetGoalPos();
  793. float distance = fabs(vecGoalPos.z - GetLocalOrigin().z);
  794. bool bWithinZ = false;
  795. if ( distance < m_retrievedistance )
  796. {
  797. distance = ( vecGoalPos - GetLocalOrigin() ).Length2D();
  798. bWithinZ = true;
  799. }
  800. if ( distance > m_retrievedistance )
  801. {
  802. Activity followActivity = ACT_WALK;
  803. if ( GetOuter()->GetState() == NPC_STATE_COMBAT || ( (!bWithinZ || distance < (m_retrievedistance*4)) && GetOuter()->GetState() != NPC_STATE_COMBAT ) )
  804. {
  805. followActivity = ACT_RUN;
  806. }
  807. // Don't confuse move and shoot by resetting the activity every think
  808. Activity curActivity = GetNavigator()->GetMovementActivity();
  809. switch( curActivity )
  810. {
  811. case ACT_WALK_AIM: curActivity = ACT_WALK; break;
  812. case ACT_RUN_AIM: curActivity = ACT_RUN; break;
  813. }
  814. if ( curActivity != followActivity )
  815. {
  816. GetNavigator()->SetMovementActivity(followActivity);
  817. }
  818. GetNavigator()->SetArrivalDirection( GetOuter()->GetTarget() );
  819. }
  820. }
  821. }
  822. break;
  823. }
  824. case TASK_LEAD_RETRIEVE_WAIT:
  825. {
  826. ChainRunTask( TASK_WAIT_INDEFINITE );
  827. break;
  828. }
  829. case TASK_LEAD_WALK_PATH:
  830. {
  831. // If we're leading, and we're supposed to run, run instead of walking
  832. if ( m_run &&
  833. ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) )
  834. {
  835. ChainRunTask( TASK_RUN_PATH );
  836. }
  837. else
  838. {
  839. ChainRunTask( TASK_WALK_PATH );
  840. }
  841. // While we're walking
  842. if ( TaskIsRunning() && IsCurSchedule( SCHED_LEAD_PLAYER, false ) )
  843. {
  844. // If we're not speaking, and we haven't tried for a while, try to speak lead idle
  845. if ( m_flNextLeadIdle < gpGlobals->curtime && !IsSpeaking() )
  846. {
  847. m_flNextLeadIdle = gpGlobals->curtime + RandomFloat( 10,15 );
  848. if ( !m_args.iRetrievePlayer && HasCondition( COND_LEAD_FOLLOWER_LOST ) && HasCondition(COND_SEE_PLAYER) )
  849. {
  850. Speak( TLK_LEAD_COMINGBACK );
  851. }
  852. else
  853. {
  854. Speak( TLK_LEAD_IDLE );
  855. }
  856. }
  857. }
  858. break;
  859. }
  860. default:
  861. BaseClass::RunTask( pTask);
  862. }
  863. }
  864. //-------------------------------------
  865. bool CAI_LeadBehavior::Speak( AIConcept_t concept )
  866. {
  867. CAI_Expresser *pExpresser = GetOuter()->GetExpresser();
  868. if ( !pExpresser )
  869. return false;
  870. // If the leader is gagged, don't speak any lead speech
  871. if ( m_gagleader )
  872. return false;
  873. // If we haven't said the start speech, don't nag
  874. bool bNag = ( FStrEq(concept,TLK_LEAD_COMINGBACK) || FStrEq(concept, TLK_LEAD_CATCHUP) || FStrEq(concept, TLK_LEAD_RETRIEVE) );
  875. if ( !m_hasspokenstart && bNag )
  876. return false;
  877. if ( hl2_episodic.GetBool() )
  878. {
  879. // If we're a player ally, only speak the concept if we're allowed to.
  880. // This allows the response rules to control it better (i.e. handles respeakdelay)
  881. // We ignore nag timers for this, because the response rules will control refire rates.
  882. CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly*>(GetOuter());
  883. if ( pAlly )
  884. return pAlly->SpeakIfAllowed( concept, GetConceptModifiers( concept ) );
  885. }
  886. // Don't spam Nags
  887. if ( bNag )
  888. {
  889. if ( m_flSpeakNextNagTime > gpGlobals->curtime )
  890. {
  891. DevMsg( GetOuter(), "Leader didn't speak due to Nag timer.\n");
  892. return false;
  893. }
  894. }
  895. if ( pExpresser->Speak( concept, GetConceptModifiers( concept ) ) )
  896. {
  897. m_flSpeakNextNagTime = gpGlobals->curtime + LEAD_NAG_TIME;
  898. return true;
  899. }
  900. return false;
  901. }
  902. //-------------------------------------
  903. bool CAI_LeadBehavior::IsSpeaking()
  904. {
  905. CAI_Expresser *pExpresser = GetOuter()->GetExpresser();
  906. if ( !pExpresser )
  907. return false;
  908. return pExpresser->IsSpeaking();
  909. }
  910. //-------------------------------------
  911. bool CAI_LeadBehavior::Connect( CAI_LeadBehaviorHandler *pSink )
  912. {
  913. m_pSink = pSink;
  914. m_hSinkImplementor = dynamic_cast<CBaseEntity *>(pSink);
  915. if ( m_hSinkImplementor == NULL )
  916. DevMsg( 2, "Note: CAI_LeadBehaviorHandler connected to a sink that isn't an entity. Manual fixup on load will be necessary\n" );
  917. return true;
  918. }
  919. //-------------------------------------
  920. bool CAI_LeadBehavior::Disconnect( CAI_LeadBehaviorHandler *pSink )
  921. {
  922. Assert( pSink == m_pSink );
  923. m_pSink = NULL;
  924. m_hSinkImplementor = NULL;
  925. return true;
  926. }
  927. //-------------------------------------
  928. AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_LeadBehavior )
  929. DECLARE_CONDITION( COND_LEAD_FOLLOWER_LOST )
  930. DECLARE_CONDITION( COND_LEAD_FOLLOWER_LAGGING )
  931. DECLARE_CONDITION( COND_LEAD_FOLLOWER_NOT_LAGGING )
  932. DECLARE_CONDITION( COND_LEAD_FOLLOWER_VERY_CLOSE )
  933. DECLARE_CONDITION( COND_LEAD_SUCCESS )
  934. DECLARE_CONDITION( COND_LEAD_HAVE_FOLLOWER_LOS )
  935. DECLARE_CONDITION( COND_LEAD_FOLLOWER_MOVED_FROM_MARK )
  936. DECLARE_CONDITION( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME )
  937. //---------------------------------
  938. //
  939. // Lead
  940. //
  941. DECLARE_TASK( TASK_GET_PATH_TO_LEAD_GOAL )
  942. DECLARE_TASK( TASK_STOP_LEADING )
  943. DECLARE_TASK( TASK_LEAD_ARRIVE )
  944. DECLARE_TASK( TASK_LEAD_SUCCEED )
  945. DECLARE_TASK( TASK_LEAD_FACE_GOAL )
  946. DECLARE_TASK( TASK_LEAD_GET_PATH_TO_WAITPOINT )
  947. DECLARE_TASK( TASK_LEAD_WAVE_TO_PLAYER )
  948. DECLARE_TASK( TASK_LEAD_PLAYER_NEEDS_WEAPON )
  949. DECLARE_TASK( TASK_LEAD_MOVE_TO_RANGE )
  950. DECLARE_TASK( TASK_LEAD_SPEAK_START )
  951. DECLARE_TASK( TASK_LEAD_RETRIEVE_WAIT )
  952. DECLARE_TASK( TASK_LEAD_WALK_PATH )
  953. DEFINE_SCHEDULE
  954. (
  955. SCHED_LEAD_RETRIEVE,
  956. " Tasks"
  957. " TASK_GET_PATH_TO_PLAYER 0"
  958. " TASK_LEAD_MOVE_TO_RANGE 0"
  959. " TASK_STOP_MOVING 0"
  960. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  961. " TASK_SET_SCHEDULE SCHEDULE:SCHED_LEAD_RETRIEVE_WAIT"
  962. " "
  963. " Interrupts"
  964. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  965. " COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME"
  966. " COND_LIGHT_DAMAGE"
  967. " COND_NEW_ENEMY"
  968. " COND_HEAR_DANGER"
  969. )
  970. //-------------------------------------
  971. DEFINE_SCHEDULE
  972. (
  973. SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER,
  974. " Tasks"
  975. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  976. " TASK_SET_SCHEDULE SCHEDULE:SCHED_LEAD_RETRIEVE"
  977. " "
  978. " Interrupts"
  979. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  980. " COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME"
  981. " COND_LIGHT_DAMAGE"
  982. " COND_NEW_ENEMY"
  983. " COND_HEAR_DANGER"
  984. )
  985. //-------------------------------------
  986. DEFINE_SCHEDULE
  987. (
  988. SCHED_LEAD_RETRIEVE_WAIT,
  989. " Tasks"
  990. " TASK_LEAD_RETRIEVE_WAIT 0"
  991. " "
  992. " Interrupts"
  993. " COND_LEAD_FOLLOWER_LOST"
  994. " COND_LEAD_FOLLOWER_LAGGING"
  995. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  996. " COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME"
  997. " COND_LEAD_FOLLOWER_MOVED_FROM_MARK"
  998. " COND_LIGHT_DAMAGE"
  999. " COND_NEW_ENEMY"
  1000. " COND_HEAR_DANGER"
  1001. )
  1002. //---------------------------------
  1003. DEFINE_SCHEDULE
  1004. (
  1005. SCHED_LEAD_PLAYER,
  1006. " Tasks"
  1007. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  1008. " TASK_GET_PATH_TO_LEAD_GOAL 0"
  1009. " TASK_LEAD_WALK_PATH 0"
  1010. " TASK_WAIT_FOR_MOVEMENT 0"
  1011. " TASK_STOP_MOVING 0"
  1012. ""
  1013. " Interrupts"
  1014. " COND_LEAD_FOLLOWER_LOST"
  1015. " COND_LEAD_FOLLOWER_LAGGING"
  1016. " COND_LIGHT_DAMAGE"
  1017. " COND_NEW_ENEMY"
  1018. " COND_HEAR_DANGER"
  1019. )
  1020. //---------------------------------
  1021. DEFINE_SCHEDULE
  1022. (
  1023. SCHED_LEAD_AWAIT_SUCCESS,
  1024. " Tasks"
  1025. " TASK_LEAD_FACE_GOAL 0"
  1026. " TASK_FACE_IDEAL 0"
  1027. " TASK_LEAD_ARRIVE 0"
  1028. " TASK_WAIT_INDEFINITE 0"
  1029. ""
  1030. " Interrupts"
  1031. " COND_LEAD_FOLLOWER_LOST"
  1032. " COND_LEAD_FOLLOWER_LAGGING"
  1033. " COND_LEAD_SUCCESS"
  1034. " COND_LIGHT_DAMAGE"
  1035. " COND_NEW_ENEMY"
  1036. " COND_HEAR_DANGER"
  1037. )
  1038. //---------------------------------
  1039. DEFINE_SCHEDULE
  1040. (
  1041. SCHED_LEAD_SUCCEED,
  1042. " Tasks"
  1043. " TASK_LEAD_SUCCEED 0"
  1044. " TASK_STOP_LEADING 0"
  1045. ""
  1046. )
  1047. //---------------------------------
  1048. // This is the schedule Odell uses to pause the tour momentarily
  1049. // if the player lags behind. If the player shows up in a
  1050. // couple of seconds, the tour will resume. Otherwise, Odell
  1051. // moves to retrieve.
  1052. //---------------------------------
  1053. DEFINE_SCHEDULE
  1054. (
  1055. SCHED_LEAD_PAUSE,
  1056. " Tasks"
  1057. " TASK_STOP_MOVING 1"
  1058. " TASK_FACE_TARGET 0"
  1059. " TASK_WAIT 5"
  1060. " TASK_WAIT_RANDOM 5"
  1061. " TASK_SET_SCHEDULE SCHEDULE:SCHED_LEAD_RETRIEVE"
  1062. ""
  1063. " Interrupts"
  1064. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  1065. " COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME"
  1066. " COND_LEAD_FOLLOWER_NOT_LAGGING"
  1067. " COND_LEAD_FOLLOWER_LOST"
  1068. " COND_LIGHT_DAMAGE"
  1069. " COND_NEW_ENEMY"
  1070. " COND_HEAR_DANGER"
  1071. )
  1072. DEFINE_SCHEDULE
  1073. (
  1074. SCHED_LEAD_PAUSE_COMBAT,
  1075. " Tasks"
  1076. " TASK_STOP_MOVING 1"
  1077. " TASK_FACE_TARGET 0"
  1078. " TASK_WAIT 1"
  1079. " TASK_SET_SCHEDULE SCHEDULE:SCHED_LEAD_RETRIEVE"
  1080. ""
  1081. " Interrupts"
  1082. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  1083. " COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME"
  1084. " COND_LEAD_FOLLOWER_NOT_LAGGING"
  1085. " COND_LEAD_FOLLOWER_LOST"
  1086. " COND_HEAR_DANGER"
  1087. )
  1088. //---------------------------------
  1089. DEFINE_SCHEDULE
  1090. (
  1091. SCHED_LEAD_WAITFORPLAYER,
  1092. " Tasks"
  1093. " TASK_LEAD_GET_PATH_TO_WAITPOINT 0"
  1094. " TASK_LEAD_WALK_PATH 0"
  1095. " TASK_WAIT_FOR_MOVEMENT 0"
  1096. " TASK_STOP_MOVING 0"
  1097. " TASK_WAIT 0.5"
  1098. " TASK_FACE_TARGET 0"
  1099. " TASK_LEAD_WAVE_TO_PLAYER 0"
  1100. " TASK_WAIT 5.0"
  1101. " "
  1102. " Interrupts"
  1103. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  1104. " COND_LIGHT_DAMAGE"
  1105. " COND_NEW_ENEMY"
  1106. " COND_HEAR_DANGER"
  1107. )
  1108. //---------------------------------
  1109. DEFINE_SCHEDULE
  1110. (
  1111. SCHED_LEAD_WAITFORPLAYERIDLE,
  1112. " Tasks"
  1113. " TASK_STOP_MOVING 0"
  1114. " TASK_WAIT 0.5"
  1115. " TASK_FACE_TARGET 0"
  1116. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_IDLE"
  1117. " TASK_WAIT 2"
  1118. " "
  1119. " Interrupts"
  1120. " COND_LEAD_FOLLOWER_VERY_CLOSE"
  1121. " COND_LIGHT_DAMAGE"
  1122. " COND_NEW_ENEMY"
  1123. " COND_HEAR_DANGER"
  1124. )
  1125. //---------------------------------
  1126. DEFINE_SCHEDULE
  1127. (
  1128. SCHED_LEAD_PLAYERNEEDSWEAPON,
  1129. " Tasks"
  1130. " TASK_FACE_PLAYER 0"
  1131. " TASK_LEAD_PLAYER_NEEDS_WEAPON 0"
  1132. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  1133. " TASK_WAIT 8"
  1134. " "
  1135. " Interrupts"
  1136. )
  1137. //---------------------------------
  1138. DEFINE_SCHEDULE
  1139. (
  1140. SCHED_LEAD_SPEAK_START,
  1141. " Tasks"
  1142. " TASK_LEAD_SPEAK_START 0"
  1143. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  1144. ""
  1145. " Interrupts"
  1146. )
  1147. //---------------------------------
  1148. DEFINE_SCHEDULE
  1149. (
  1150. SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER,
  1151. " Tasks"
  1152. " TASK_STOP_MOVING 0"
  1153. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  1154. " TASK_SET_SCHEDULE SCHEDULE:SCHED_LEAD_PLAYER"
  1155. ""
  1156. " Interrupts"
  1157. " COND_LEAD_FOLLOWER_LOST"
  1158. " COND_LEAD_FOLLOWER_LAGGING"
  1159. " COND_LIGHT_DAMAGE"
  1160. " COND_NEW_ENEMY"
  1161. " COND_HEAR_DANGER"
  1162. )
  1163. AI_END_CUSTOM_SCHEDULE_PROVIDER()
  1164. //-----------------------------------------------------------------------------
  1165. //
  1166. // Purpose: A level tool to control the lead behavior. Use is not required
  1167. // in order to use behavior.
  1168. //
  1169. class CAI_LeadGoal : public CAI_GoalEntity,
  1170. public CAI_LeadBehaviorHandler
  1171. {
  1172. DECLARE_CLASS( CAI_LeadGoal, CAI_GoalEntity );
  1173. public:
  1174. CAI_LeadGoal()
  1175. : m_fArrived( false )
  1176. {
  1177. // These fields got added after existing levels shipped, so we set
  1178. // the default values here in the constructor.
  1179. m_iRetrievePlayer = 1;
  1180. m_iRetrieveWaitForSpeak = 0;
  1181. m_iComingBackWaitForSpeak = 0;
  1182. m_bStopScenesWhenPlayerLost = false;
  1183. m_bDontSpeakStart = false;
  1184. m_bLeadDuringCombat = false;
  1185. m_bGagLeader = false;
  1186. }
  1187. CAI_LeadBehavior *GetLeadBehavior();
  1188. virtual const char *GetConceptModifiers( const char *pszConcept );
  1189. virtual void InputActivate( inputdata_t &inputdata );
  1190. virtual void InputDeactivate( inputdata_t &inputdata );
  1191. DECLARE_DATADESC();
  1192. private:
  1193. virtual void OnEvent( int event );
  1194. void InputSetSuccess( inputdata_t &inputdata );
  1195. void InputSetFailure( inputdata_t &inputdata );
  1196. bool m_fArrived; // @TODO (toml 08-16-02): move arrived tracking onto behavior
  1197. float m_flWaitDistance;
  1198. float m_flLeadDistance;
  1199. float m_flRetrieveDistance;
  1200. float m_flSuccessDistance;
  1201. bool m_bRun;
  1202. int m_iRetrievePlayer;
  1203. int m_iRetrieveWaitForSpeak;
  1204. int m_iComingBackWaitForSpeak;
  1205. bool m_bStopScenesWhenPlayerLost;
  1206. bool m_bDontSpeakStart;
  1207. bool m_bLeadDuringCombat;
  1208. bool m_bGagLeader;
  1209. string_t m_iszWaitPointName;
  1210. string_t m_iszStartConceptModifier;
  1211. string_t m_iszAttractPlayerConceptModifier;
  1212. string_t m_iszWaitOverConceptModifier;
  1213. string_t m_iszArrivalConceptModifier;
  1214. string_t m_iszPostArrivalConceptModifier;
  1215. string_t m_iszSuccessConceptModifier;
  1216. string_t m_iszFailureConceptModifier;
  1217. string_t m_iszRetrieveConceptModifier;
  1218. string_t m_iszComingBackConceptModifier;
  1219. // Output handlers
  1220. COutputEvent m_OnArrival;
  1221. COutputEvent m_OnArrivalDone;
  1222. COutputEvent m_OnSuccess;
  1223. COutputEvent m_OnFailure;
  1224. COutputEvent m_OnDone;
  1225. };
  1226. //-----------------------------------------------------------------------------
  1227. //
  1228. // CAI_LeadGoal implementation
  1229. //
  1230. LINK_ENTITY_TO_CLASS( ai_goal_lead, CAI_LeadGoal );
  1231. BEGIN_DATADESC( CAI_LeadGoal )
  1232. DEFINE_FIELD( m_fArrived, FIELD_BOOLEAN ),
  1233. DEFINE_KEYFIELD(m_flWaitDistance, FIELD_FLOAT, "WaitDistance"),
  1234. DEFINE_KEYFIELD(m_iszWaitPointName, FIELD_STRING, "WaitPointName"),
  1235. DEFINE_KEYFIELD(m_flLeadDistance, FIELD_FLOAT, "LeadDistance"),
  1236. DEFINE_KEYFIELD(m_flRetrieveDistance, FIELD_FLOAT, "RetrieveDistance"),
  1237. DEFINE_KEYFIELD(m_flSuccessDistance, FIELD_FLOAT, "SuccessDistance"),
  1238. DEFINE_KEYFIELD(m_bRun, FIELD_BOOLEAN, "Run"),
  1239. DEFINE_KEYFIELD(m_iRetrievePlayer, FIELD_INTEGER, "Retrieve"),
  1240. DEFINE_KEYFIELD(m_iRetrieveWaitForSpeak, FIELD_INTEGER, "RetrieveWaitForSpeak"),
  1241. DEFINE_KEYFIELD(m_iComingBackWaitForSpeak, FIELD_INTEGER, "ComingBackWaitForSpeak"),
  1242. DEFINE_KEYFIELD(m_bStopScenesWhenPlayerLost, FIELD_BOOLEAN, "StopScenes"),
  1243. DEFINE_KEYFIELD(m_bDontSpeakStart, FIELD_BOOLEAN, "DontSpeakStart"),
  1244. DEFINE_KEYFIELD(m_bLeadDuringCombat, FIELD_BOOLEAN, "LeadDuringCombat"),
  1245. DEFINE_KEYFIELD(m_bGagLeader, FIELD_BOOLEAN, "GagLeader"),
  1246. DEFINE_KEYFIELD(m_iszStartConceptModifier, FIELD_STRING, "StartConceptModifier"),
  1247. DEFINE_KEYFIELD(m_iszAttractPlayerConceptModifier, FIELD_STRING, "AttractPlayerConceptModifier"),
  1248. DEFINE_KEYFIELD(m_iszWaitOverConceptModifier, FIELD_STRING, "WaitOverConceptModifier"),
  1249. DEFINE_KEYFIELD(m_iszArrivalConceptModifier, FIELD_STRING, "ArrivalConceptModifier"),
  1250. DEFINE_KEYFIELD(m_iszPostArrivalConceptModifier, FIELD_STRING, "PostArrivalConceptModifier"),
  1251. DEFINE_KEYFIELD(m_iszSuccessConceptModifier, FIELD_STRING, "SuccessConceptModifier"),
  1252. DEFINE_KEYFIELD(m_iszFailureConceptModifier, FIELD_STRING, "FailureConceptModifier"),
  1253. DEFINE_KEYFIELD(m_iszRetrieveConceptModifier, FIELD_STRING, "RetrieveConceptModifier"),
  1254. DEFINE_KEYFIELD(m_iszComingBackConceptModifier, FIELD_STRING, "ComingBackConceptModifier"),
  1255. DEFINE_OUTPUT( m_OnSuccess, "OnSuccess" ),
  1256. DEFINE_OUTPUT( m_OnArrival, "OnArrival" ),
  1257. DEFINE_OUTPUT( m_OnArrivalDone, "OnArrivalDone" ),
  1258. DEFINE_OUTPUT( m_OnFailure, "OnFailure" ),
  1259. DEFINE_OUTPUT( m_OnDone, "OnDone" ),
  1260. // Inputs
  1261. DEFINE_INPUTFUNC( FIELD_VOID, "SetSuccess", InputSetSuccess ),
  1262. DEFINE_INPUTFUNC( FIELD_VOID, "SetFailure", InputSetFailure ),
  1263. END_DATADESC()
  1264. //-----------------------------------------------------------------------------
  1265. CAI_LeadBehavior *CAI_LeadGoal::GetLeadBehavior()
  1266. {
  1267. CAI_BaseNPC *pActor = GetActor();
  1268. if ( !pActor )
  1269. return NULL;
  1270. CAI_LeadBehavior *pBehavior;
  1271. if ( !pActor->GetBehavior( &pBehavior ) )
  1272. {
  1273. return NULL;
  1274. }
  1275. return pBehavior;
  1276. }
  1277. //-----------------------------------------------------------------------------
  1278. void CAI_LeadGoal::InputSetSuccess( inputdata_t &inputdata )
  1279. {
  1280. CAI_LeadBehavior *pBehavior = GetLeadBehavior();
  1281. if ( !pBehavior )
  1282. return;
  1283. // @TODO (toml 02-14-03): Hackly!
  1284. pBehavior->SetCondition( CAI_LeadBehavior::COND_LEAD_SUCCESS);
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. void CAI_LeadGoal::InputSetFailure( inputdata_t &inputdata )
  1288. {
  1289. DevMsg( "SetFailure unimplemented\n" );
  1290. }
  1291. //-----------------------------------------------------------------------------
  1292. void CAI_LeadGoal::InputActivate( inputdata_t &inputdata )
  1293. {
  1294. BaseClass::InputActivate( inputdata );
  1295. CAI_LeadBehavior *pBehavior = GetLeadBehavior();
  1296. if ( !pBehavior )
  1297. {
  1298. DevMsg( "Lead goal entity activated for an NPC that doesn't have the lead behavior\n" );
  1299. return;
  1300. }
  1301. #ifdef HL2_EPISODIC
  1302. if ( (m_flLeadDistance*4) < m_flRetrieveDistance )
  1303. {
  1304. Warning("ai_goal_lead '%s': lead distance (%.2f) * 4 is < retrieve distance (%.2f). This will make the NPC act stupid. Either reduce the retrieve distance, or increase the lead distance.\n", GetDebugName(), m_flLeadDistance, m_flRetrieveDistance );
  1305. }
  1306. #endif
  1307. if ( m_flRetrieveDistance < (m_flLeadDistance + LEAD_MIN_RETRIEVEDIST_OFFSET) )
  1308. {
  1309. #ifdef HL2_EPISODIC
  1310. Warning("ai_goal_lead '%s': retrieve distance (%.2f) < lead distance (%.2f) + %d. Retrieve distance should be at least %d greater than the lead distance, or NPC will ping-pong while retrieving.\n", GetDebugName(), m_flRetrieveDistance, m_flLeadDistance, LEAD_MIN_RETRIEVEDIST_OFFSET, LEAD_MIN_RETRIEVEDIST_OFFSET );
  1311. #endif
  1312. m_flRetrieveDistance = m_flLeadDistance + LEAD_MIN_RETRIEVEDIST_OFFSET;
  1313. }
  1314. AI_LeadArgs_t leadArgs = {
  1315. GetGoalEntityName(),
  1316. STRING(m_iszWaitPointName),
  1317. (unsigned)m_spawnflags,
  1318. m_flWaitDistance,
  1319. m_flLeadDistance,
  1320. m_flRetrieveDistance,
  1321. m_flSuccessDistance,
  1322. m_bRun,
  1323. m_iRetrievePlayer,
  1324. m_iRetrieveWaitForSpeak,
  1325. m_iComingBackWaitForSpeak,
  1326. m_bStopScenesWhenPlayerLost,
  1327. m_bDontSpeakStart,
  1328. m_bLeadDuringCombat,
  1329. m_bGagLeader,
  1330. };
  1331. pBehavior->LeadPlayer( leadArgs, this );
  1332. }
  1333. //-----------------------------------------------------------------------------
  1334. void CAI_LeadGoal::InputDeactivate( inputdata_t &inputdata )
  1335. {
  1336. BaseClass::InputDeactivate( inputdata );
  1337. CAI_LeadBehavior *pBehavior = GetLeadBehavior();
  1338. if ( !pBehavior )
  1339. return;
  1340. pBehavior->StopLeading();
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. void CAI_LeadGoal::OnEvent( int event )
  1344. {
  1345. COutputEvent *pOutputEvent = NULL;
  1346. switch ( event )
  1347. {
  1348. case LBE_ARRIVAL: pOutputEvent = &m_OnArrival; break;
  1349. case LBE_ARRIVAL_DONE: pOutputEvent = &m_OnArrivalDone; break;
  1350. case LBE_SUCCESS: pOutputEvent = &m_OnSuccess; break;
  1351. case LBE_FAILURE: pOutputEvent = &m_OnFailure; break;
  1352. case LBE_DONE: pOutputEvent = &m_OnDone; break;
  1353. }
  1354. // @TODO (toml 08-16-02): move arrived tracking onto behavior
  1355. if ( event == LBE_ARRIVAL )
  1356. m_fArrived = true;
  1357. if ( pOutputEvent )
  1358. pOutputEvent->FireOutput( this, this );
  1359. }
  1360. //-----------------------------------------------------------------------------
  1361. const char *CAI_LeadGoal::GetConceptModifiers( const char *pszConcept )
  1362. {
  1363. if ( m_iszStartConceptModifier != NULL_STRING && *STRING(m_iszStartConceptModifier) && strcmp( pszConcept, TLK_LEAD_START) == 0 )
  1364. return STRING( m_iszStartConceptModifier );
  1365. if ( m_iszAttractPlayerConceptModifier != NULL_STRING && *STRING(m_iszAttractPlayerConceptModifier) && strcmp( pszConcept, TLK_LEAD_ATTRACTPLAYER) == 0 )
  1366. return STRING( m_iszAttractPlayerConceptModifier );
  1367. if ( m_iszWaitOverConceptModifier != NULL_STRING && *STRING(m_iszWaitOverConceptModifier) && strcmp( pszConcept, TLK_LEAD_WAITOVER) == 0 )
  1368. return STRING( m_iszWaitOverConceptModifier );
  1369. if ( m_iszArrivalConceptModifier != NULL_STRING && *STRING(m_iszArrivalConceptModifier) && strcmp( pszConcept, TLK_LEAD_ARRIVAL) == 0 )
  1370. return STRING( m_iszArrivalConceptModifier );
  1371. if ( m_iszSuccessConceptModifier != NULL_STRING && *STRING(m_iszSuccessConceptModifier) && strcmp( pszConcept, TLK_LEAD_SUCCESS) == 0 )
  1372. return STRING( m_iszSuccessConceptModifier );
  1373. if (m_iszFailureConceptModifier != NULL_STRING && *STRING(m_iszFailureConceptModifier) && strcmp( pszConcept, TLK_LEAD_FAILURE) == 0 )
  1374. return STRING( m_iszFailureConceptModifier );
  1375. if (m_iszRetrieveConceptModifier != NULL_STRING && *STRING(m_iszRetrieveConceptModifier) && strcmp( pszConcept, TLK_LEAD_RETRIEVE) == 0 )
  1376. return STRING( m_iszRetrieveConceptModifier );
  1377. if (m_iszComingBackConceptModifier != NULL_STRING && *STRING(m_iszComingBackConceptModifier) && strcmp( pszConcept, TLK_LEAD_COMINGBACK) == 0 )
  1378. return STRING( m_iszComingBackConceptModifier );
  1379. if ( m_fArrived && m_iszPostArrivalConceptModifier != NULL_STRING && *STRING(m_iszPostArrivalConceptModifier) )
  1380. return STRING( m_iszPostArrivalConceptModifier );
  1381. return NULL;
  1382. }
  1383. //-----------------------------------------------------------------------------
  1384. //-----------------------------------------------------------------------------
  1385. //
  1386. // Purpose: A custom lead goal that waits until the player has a weapon.
  1387. //
  1388. class CAI_LeadGoal_Weapon : public CAI_LeadGoal
  1389. {
  1390. DECLARE_CLASS( CAI_LeadGoal_Weapon, CAI_LeadGoal );
  1391. public:
  1392. virtual const char *GetConceptModifiers( const char *pszConcept );
  1393. virtual void InputActivate( inputdata_t &inputdata );
  1394. private:
  1395. string_t m_iszWeaponName;
  1396. string_t m_iszMissingWeaponConceptModifier;
  1397. DECLARE_DATADESC();
  1398. };
  1399. //-----------------------------------------------------------------------------
  1400. //
  1401. // CAI_LeadGoal_Weapon implementation
  1402. //
  1403. LINK_ENTITY_TO_CLASS( ai_goal_lead_weapon, CAI_LeadGoal_Weapon );
  1404. BEGIN_DATADESC( CAI_LeadGoal_Weapon )
  1405. DEFINE_KEYFIELD( m_iszWeaponName, FIELD_STRING, "WeaponName"),
  1406. DEFINE_KEYFIELD( m_iszMissingWeaponConceptModifier, FIELD_STRING, "MissingWeaponConceptModifier"),
  1407. END_DATADESC()
  1408. //-----------------------------------------------------------------------------
  1409. const char *CAI_LeadGoal_Weapon::GetConceptModifiers( const char *pszConcept )
  1410. {
  1411. if ( m_iszMissingWeaponConceptModifier != NULL_STRING && *STRING(m_iszMissingWeaponConceptModifier) && strcmp( pszConcept, TLK_LEAD_MISSINGWEAPON) == 0 )
  1412. return STRING( m_iszMissingWeaponConceptModifier );
  1413. return BaseClass::GetConceptModifiers( pszConcept );
  1414. }
  1415. //-----------------------------------------------------------------------------
  1416. void CAI_LeadGoal_Weapon::InputActivate( inputdata_t &inputdata )
  1417. {
  1418. BaseClass::InputActivate( inputdata );
  1419. CAI_LeadBehavior *pBehavior = GetLeadBehavior();
  1420. if ( pBehavior )
  1421. {
  1422. pBehavior->SetWaitForWeapon( m_iszWeaponName );
  1423. }
  1424. }