Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

580 lines
15 KiB

  1. // ai_addon.cpp
  2. #include "cbase.h"
  3. #include "ai_addon.h"
  4. #include "ai_basenpc.h"
  5. #include "ai_behavior_addonhost.h"
  6. #include "strtools.h"
  7. //---------------------------------------------------------
  8. // Answers YES if the attachment is on the model and not
  9. // currently plugged by another add-on.
  10. //
  11. // Answers NO if the host's model lacks the attachment or
  12. // the add-on is currently plugged by another add-on.
  13. //
  14. // This is terribly inefficient right now, but it works
  15. // and lets us move on.
  16. //---------------------------------------------------------
  17. bool IsAddOnAttachmentAvailable( CAI_BaseNPC *pHost, char *pszAttachmentName )
  18. {
  19. char szOrigialAttachmentName[ 256 ];
  20. V_strcpy_safe( szOrigialAttachmentName, pszAttachmentName );
  21. int iCount = 0;
  22. // If this loops 20 times, the translate function probably isn't returning the end of list value "" -Jeep
  23. while ( iCount < 20 )
  24. {
  25. // Try another
  26. Q_strcpy( pszAttachmentName, szOrigialAttachmentName );
  27. pHost->TranslateAddOnAttachment( pszAttachmentName, iCount );
  28. if ( pszAttachmentName[ 0 ] == '\0' )
  29. {
  30. return false;
  31. }
  32. if ( pHost->LookupAttachment(pszAttachmentName) == 0 )
  33. {
  34. // Translated to an attachment that doesn't exist
  35. Msg("***AddOn Error! Host NPC %s does not have attachment %s\n", pHost->GetDebugName(), pszAttachmentName );
  36. return false;
  37. }
  38. int iWishedAttachmentID = pHost->LookupAttachment( pszAttachmentName );
  39. CAI_BehaviorBase **ppBehaviors = pHost->AccessBehaviors();
  40. int nBehaviors = pHost->NumBehaviors();
  41. bool bAttachmentFilled = false;
  42. for ( int i = 0; i < nBehaviors && !bAttachmentFilled; i++ )
  43. {
  44. CAI_AddOnBehaviorBase *pAddOnBehavior = dynamic_cast<CAI_AddOnBehaviorBase *>(ppBehaviors[i]);
  45. if ( pAddOnBehavior )
  46. {
  47. CAI_AddOn **ppAddOns = pAddOnBehavior->GetAddOnsBase();
  48. int nAddOns = pAddOnBehavior->NumAddOns();
  49. for ( int j = 0; j < nAddOns && !bAttachmentFilled; j++ )
  50. {
  51. bAttachmentFilled = ( ppAddOns[j]->GetAttachmentID() == iWishedAttachmentID );
  52. }
  53. }
  54. }
  55. if ( !bAttachmentFilled )
  56. {
  57. return true;
  58. }
  59. ++iCount;
  60. }
  61. // We should never get here
  62. DevWarning( "Translating the attachment was tried more than 50 times!\n" );
  63. return false;
  64. }
  65. //---------------------------------------------------------
  66. //---------------------------------------------------------
  67. int CountAddOns( CAI_BaseNPC *pHost )
  68. {
  69. int nAddOns = 0;
  70. CAI_BehaviorBase **ppBehaviors = pHost->AccessBehaviors();
  71. int nBehaviors = pHost->NumBehaviors();
  72. for ( int i = 0; i < nBehaviors; i++ )
  73. {
  74. CAI_AddOnBehaviorBase *pAddOnBehavior = dynamic_cast<CAI_AddOnBehaviorBase *>(ppBehaviors[i]);
  75. if ( pAddOnBehavior )
  76. {
  77. nAddOns += pAddOnBehavior->NumAddOns();
  78. }
  79. }
  80. return nAddOns;
  81. }
  82. //---------------------------------------------------------
  83. //---------------------------------------------------------
  84. BEGIN_DATADESC( CAI_AddOn )
  85. DEFINE_FIELD( m_hNPCHost, FIELD_EHANDLE ),
  86. DEFINE_THINKFUNC( DispatchAddOnThink ),
  87. DEFINE_FIELD( m_hPhysReplacement, FIELD_EHANDLE ),
  88. DEFINE_FIELD( m_iPhysReplacementSolidFlags, FIELD_INTEGER ),
  89. DEFINE_FIELD( m_iPhysReplacementMoveType, FIELD_INTEGER ),
  90. DEFINE_FIELD( m_angPhysReplacementLocalOrientation, FIELD_VECTOR ),
  91. DEFINE_FIELD( m_vecPhysReplacementDetatchForce, FIELD_VECTOR ),
  92. DEFINE_FIELD( m_bWasAttached, FIELD_BOOLEAN ),
  93. DEFINE_FIELD( m_flWaitFinished, FIELD_TIME ),
  94. DEFINE_FIELD( m_flNextAttachTime, FIELD_FLOAT ),
  95. DEFINE_INPUTFUNC( FIELD_STRING, "Install", InputInstall ),
  96. DEFINE_INPUTFUNC( FIELD_VOID, "Remove", InputRemove ),
  97. END_DATADESC()
  98. //---------------------------------------------------------
  99. //---------------------------------------------------------
  100. void CAI_AddOn::Precache()
  101. {
  102. BaseClass::Precache();
  103. PrecacheModel( GetAddOnModelName() );
  104. PrecacheScriptSound( "AddOn.Install" );
  105. }
  106. //---------------------------------------------------------
  107. //---------------------------------------------------------
  108. void CAI_AddOn::Spawn()
  109. {
  110. BaseClass::Spawn();
  111. Precache();
  112. CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
  113. if ( pPhysReplacement )
  114. {
  115. // Use the same model as the replacement
  116. SetModel( pPhysReplacement->GetModelName().ToCStr() );
  117. }
  118. else
  119. {
  120. SetModel( GetAddOnModelName() );
  121. }
  122. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  123. VPhysicsInitNormal( SOLID_VPHYSICS, GetSolidFlags() | FSOLID_TRIGGER, false );
  124. SetMoveType( MOVETYPE_VPHYSICS );
  125. SetThink( &CAI_AddOn::DispatchAddOnThink );
  126. SetNextThink( gpGlobals->curtime + 0.1f );
  127. }
  128. //---------------------------------------------------------
  129. //---------------------------------------------------------
  130. void CAI_AddOn::UpdateOnRemove()
  131. {
  132. Remove();
  133. BaseClass::UpdateOnRemove();
  134. }
  135. int CAI_AddOn::DrawDebugTextOverlays( void )
  136. {
  137. int text_offset = BaseClass::DrawDebugTextOverlays();
  138. // Draw debug text for agent
  139. m_AgentDebugOverlays = m_debugOverlays;
  140. Vector vecLocalCenter;
  141. ICollideable *pCollidable = GetCollideable();
  142. if ( pCollidable )
  143. {
  144. VectorAdd( pCollidable->OBBMins(), pCollidable->OBBMaxs(), vecLocalCenter );
  145. vecLocalCenter *= 0.5f;
  146. if ( ( pCollidable->GetCollisionAngles() == vec3_angle ) || ( vecLocalCenter == vec3_origin ) )
  147. {
  148. VectorAdd( vecLocalCenter, pCollidable->GetCollisionOrigin(), m_vecAgentDebugOverlaysPos );
  149. }
  150. else
  151. {
  152. VectorTransform( vecLocalCenter, pCollidable->CollisionToWorldTransform(), m_vecAgentDebugOverlaysPos );
  153. }
  154. }
  155. else
  156. {
  157. m_vecAgentDebugOverlaysPos = GetAbsOrigin();
  158. }
  159. text_offset = static_cast<CAI_Agent*>( this )->DrawDebugTextOverlays( text_offset );
  160. return text_offset;
  161. }
  162. //---------------------------------------------------------
  163. //---------------------------------------------------------
  164. void CAI_AddOn::GatherConditions()
  165. {
  166. CAI_Agent::GatherConditions();
  167. ClearCondition( COND_ADDON_LOST_HOST );
  168. if( GetNPCHost() )
  169. {
  170. m_bWasAttached = true;
  171. }
  172. else
  173. {
  174. if( m_bWasAttached == true )
  175. {
  176. SetCondition( COND_ADDON_LOST_HOST );
  177. if ( m_flNextAttachTime != 0.0f && m_flNextAttachTime < gpGlobals->curtime )
  178. {
  179. m_flNextAttachTime = 0.0f;
  180. m_bWasAttached = false;
  181. }
  182. }
  183. }
  184. }
  185. //---------------------------------------------------------
  186. //---------------------------------------------------------
  187. int CAI_AddOn::SelectSchedule( void )
  188. {
  189. return SCHED_ADDON_NO_OWNER;
  190. }
  191. //---------------------------------------------------------
  192. //---------------------------------------------------------
  193. void CAI_AddOn::StartTask( const Task_t *pTask )
  194. {
  195. switch( pTask->iTask )
  196. {
  197. case TASK_ADDON_WAIT:
  198. m_flWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  199. break;
  200. case TASK_ADDON_WAIT_RANDOM:
  201. m_flWaitFinished = gpGlobals->curtime + random->RandomFloat( 0.1f, pTask->flTaskData );
  202. break;
  203. default:
  204. CAI_Agent::StartTask( pTask );
  205. break;
  206. }
  207. }
  208. //---------------------------------------------------------
  209. //---------------------------------------------------------
  210. void CAI_AddOn::RunTask( const Task_t *pTask )
  211. {
  212. switch( pTask->iTask )
  213. {
  214. case TASK_ADDON_WAIT:
  215. case TASK_ADDON_WAIT_RANDOM:
  216. if( gpGlobals->curtime > m_flWaitFinished )
  217. TaskComplete();
  218. break;
  219. default:
  220. CAI_Agent::RunTask( pTask );
  221. break;
  222. }
  223. }
  224. void CAI_AddOn::SetPhysReplacement( CBaseEntity *pEntity )
  225. {
  226. m_hPhysReplacement = pEntity;
  227. }
  228. //---------------------------------------------------------
  229. //---------------------------------------------------------
  230. bool CAI_AddOn::Attach( CAI_BaseNPC *pHost )
  231. {
  232. // Make sure we're not already attached to someone else!
  233. Assert( GetAttachmentID() == INVALID_ADDON_ATTACHMENT_ID );
  234. char szAttachmentName[ 256 ];
  235. szAttachmentName[ 0 ] = '\0';
  236. PickAttachment( pHost, szAttachmentName );
  237. if ( szAttachmentName[ 0 ] == '\0' )
  238. {
  239. return false;
  240. }
  241. int iAttachmentIndex = pHost->LookupAttachment( szAttachmentName );
  242. if ( !iAttachmentIndex )
  243. {
  244. return false;
  245. }
  246. Vector vecOrigin;
  247. Vector vecForward, vecRight, vecUp;
  248. QAngle angles;
  249. pHost->GetAttachment( iAttachmentIndex, vecOrigin, angles );
  250. AngleVectors( angles, &vecForward, &vecRight, &vecUp );
  251. SetAbsOrigin( vecOrigin + GetAttachOffset( angles ) );
  252. SetAbsAngles( GetAttachOrientation( angles ) );
  253. m_iAttachmentID = iAttachmentIndex;
  254. SetParent( pHost, iAttachmentIndex );
  255. QAngle angLocalAngles = GetLocalOrientation();
  256. SetLocalAngles( angLocalAngles );
  257. // Stop acting physical
  258. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  259. if ( pPhysicsObject )
  260. {
  261. pPhysicsObject->EnableMotion( false );
  262. pPhysicsObject->EnableCollisions( false );
  263. }
  264. SetMoveType( MOVETYPE_NONE );
  265. // Handle the phys replacement
  266. CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
  267. if ( pPhysReplacement )
  268. {
  269. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  270. pPlayer->ForceDropOfCarriedPhysObjects( pPhysReplacement );
  271. pPhysReplacement->AddEffects( EF_NODRAW );
  272. m_iPhysReplacementSolidFlags = pPhysReplacement->GetSolidFlags();
  273. pPhysReplacement->SetSolidFlags( FSOLID_NOT_SOLID );
  274. m_iPhysReplacementMoveType = pPhysReplacement->GetMoveType();
  275. pPhysReplacement->SetMoveType( MOVETYPE_NONE );
  276. pPhysReplacement->SetAbsOrigin( vecOrigin + GetAttachOffset( angles ) );
  277. pPhysReplacement->SetAbsAngles( GetAttachOrientation( angles ) );
  278. pPhysReplacement->SetParent( pHost, iAttachmentIndex );
  279. pPhysReplacement->SetOwnerEntity( pHost );
  280. m_angPhysReplacementLocalOrientation = pPhysReplacement->GetLocalAngles();
  281. pPhysReplacement->SetLocalAngles( angLocalAngles );
  282. IPhysicsObject *pReplacementPhysObject = pPhysReplacement->VPhysicsGetObject();
  283. if ( pReplacementPhysObject )
  284. {
  285. pReplacementPhysObject->EnableMotion( false );
  286. pReplacementPhysObject->EnableCollisions( false );
  287. SetMoveType( MOVETYPE_NONE );
  288. }
  289. }
  290. return true;
  291. }
  292. void CAI_AddOn::Dettach( void )
  293. {
  294. if ( !m_bWasAttached )
  295. return;
  296. m_flNextAttachTime = gpGlobals->curtime + 2.0f;
  297. m_hNPCHost.Set( NULL );
  298. SetParent( NULL );
  299. IPhysicsObject *pPhysObject = NULL;
  300. CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
  301. if ( pPhysReplacement )
  302. {
  303. // Make the replacement visible
  304. pPhysReplacement->RemoveEffects( EF_NODRAW );
  305. pPhysReplacement->SetSolidFlags( m_iPhysReplacementSolidFlags );
  306. pPhysReplacement->SetMoveType( MoveType_t( m_iPhysReplacementMoveType ) );
  307. pPhysObject = pPhysReplacement->VPhysicsGetObject();
  308. if ( pPhysObject )
  309. {
  310. pPhysReplacement->SetMoveType( MOVETYPE_VPHYSICS );
  311. }
  312. pPhysReplacement->SetParent( NULL );
  313. pPhysReplacement->SetOwnerEntity( NULL );
  314. pPhysReplacement->SetLocalAngles( m_angPhysReplacementLocalOrientation );
  315. // Kill ourselves off because we're being replaced
  316. UTIL_Remove( this );
  317. }
  318. else
  319. {
  320. pPhysObject = VPhysicsGetObject();
  321. if ( pPhysObject )
  322. {
  323. SetMoveType( MOVETYPE_VPHYSICS );
  324. }
  325. }
  326. if ( pPhysObject )
  327. {
  328. // Start acting physical
  329. pPhysObject->EnableCollisions( true );
  330. pPhysObject->EnableMotion( true );
  331. pPhysObject->EnableGravity( true );
  332. pPhysObject->SetPosition( GetAbsOrigin(), GetAbsAngles(), true );
  333. pPhysObject->Wake();
  334. pPhysObject->AddVelocity( &m_vecPhysReplacementDetatchForce, NULL );
  335. }
  336. }
  337. //---------------------------------------------------------
  338. // Return true if I successfully attach to the NPC host.
  339. //
  340. // Return false if I am already attached to an NPC, or
  341. // could not be attached to this host.
  342. //---------------------------------------------------------
  343. bool CAI_AddOn::Install( CAI_BaseNPC *pHost, bool bRemoveOnFail )
  344. {
  345. if( m_bWasAttached )
  346. return false;
  347. // Associate the addon with this host
  348. Assert( m_hNPCHost == NULL ); // For now, prevent slamming from one host to the next.
  349. m_hNPCHost.Set( pHost );
  350. // Parent and
  351. if ( Attach( pHost ) )
  352. {
  353. Bind();
  354. return true;
  355. }
  356. // Failed to attach
  357. m_hNPCHost = NULL;
  358. if ( bRemoveOnFail || m_hPhysReplacement.Get() )
  359. {
  360. UTIL_Remove( this );
  361. }
  362. return false;
  363. }
  364. //---------------------------------------------------------
  365. //---------------------------------------------------------
  366. CAI_BaseNPC *CAI_AddOn::GetNPCHost()
  367. {
  368. return m_hNPCHost.Get();
  369. }
  370. //---------------------------------------------------------
  371. //---------------------------------------------------------
  372. CBaseEntity *CAI_AddOn::GetHostEnemy()
  373. {
  374. if( !GetNPCHost() )
  375. return NULL;
  376. return GetNPCHost()->GetEnemy();
  377. }
  378. //---------------------------------------------------------
  379. //---------------------------------------------------------
  380. void CAI_AddOn::DispatchAddOnThink()
  381. {
  382. if( GetNPCHost() != NULL && !GetNPCHost()->IsAlive() )
  383. {
  384. EjectFromHost();
  385. return;
  386. }
  387. CAI_Agent::Think(); SetNextThink( gpGlobals->curtime + GetThinkInterval() );
  388. }
  389. QAngle CAI_AddOn::GetLocalOrientation( void )
  390. {
  391. CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
  392. if ( pPhysReplacement )
  393. {
  394. CBaseAnimating *pBaseAnimatingReplacement = dynamic_cast<CBaseAnimating *>( pPhysReplacement );
  395. if ( pBaseAnimatingReplacement )
  396. {
  397. int iMuzzle = pBaseAnimatingReplacement->LookupAttachment( "muzzle" );
  398. if ( iMuzzle )
  399. {
  400. // Use the muzzle angles!
  401. Vector vecMuzzleOrigin;
  402. QAngle angMuzzleAngles;
  403. pBaseAnimatingReplacement->GetAttachmentLocal( iMuzzle, vecMuzzleOrigin, angMuzzleAngles );
  404. return angMuzzleAngles;
  405. }
  406. }
  407. // Use the local angles
  408. return pPhysReplacement->GetLocalAngles();
  409. }
  410. // No special angles to use
  411. return QAngle( 0.0f, 0.0f, 0.0f );
  412. }
  413. //---------------------------------------------------------
  414. //---------------------------------------------------------
  415. void CAI_AddOn::EjectFromHost()
  416. {
  417. Unbind();
  418. m_hNPCHost.Set( NULL );
  419. SetThink( NULL );
  420. SetParent( NULL );
  421. SetSize( Vector( 0,0,0), Vector(0,0,0) );
  422. SetMoveType( MOVETYPE_FLYGRAVITY );
  423. SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );
  424. SetSolid( SOLID_BBOX );
  425. Vector vecDir;
  426. GetVectors( NULL, NULL, &vecDir );
  427. SetAbsVelocity( GetAbsVelocity() + vecDir * RandomFloat(50, 200) );
  428. QAngle avelocity( RandomFloat( 10, 60), RandomFloat( 10, 60), 0 );
  429. SetLocalAngularVelocity( avelocity );
  430. SetThink( &CBaseEntity::SUB_FadeOut );
  431. SetNextThink( gpGlobals->curtime + 1.0f );
  432. }
  433. //---------------------------------------------------------
  434. //---------------------------------------------------------
  435. void CAI_AddOn::InputInstall( inputdata_t &data )
  436. {
  437. CAI_BaseNPC *pHost = dynamic_cast<CAI_BaseNPC *>( gEntList.FindEntityByName( NULL, data.value.String() ) );
  438. if( !pHost )
  439. {
  440. DevMsg(" AddOn: %s couldn't find Host %s\n", GetDebugName(), data.value.String() );
  441. }
  442. else
  443. {
  444. Install( pHost );
  445. }
  446. }
  447. //---------------------------------------------------------
  448. //---------------------------------------------------------
  449. void CAI_AddOn::InputRemove( inputdata_t &data )
  450. {
  451. Remove();
  452. m_hNPCHost.Set( NULL );
  453. SetThink( NULL );
  454. SetParent( NULL );
  455. UTIL_Remove( this );
  456. }
  457. AI_BEGIN_AGENT_(CAI_AddOn,CAI_Agent)
  458. DECLARE_TASK( TASK_ADDON_WAIT )
  459. DECLARE_TASK( TASK_ADDON_WAIT_RANDOM )
  460. DECLARE_CONDITION( COND_ADDON_LOST_HOST )
  461. DEFINE_SCHEDULE
  462. (
  463. SCHED_ADDON_NO_OWNER,
  464. " Tasks"
  465. " TASK_ADDON_WAIT 1"
  466. " "
  467. " Interrupts"
  468. " "
  469. )
  470. AI_END_AGENT()