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.

1313 lines
41 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The robots for use in the Robot Destruction TF2 game mode.
  4. //
  5. //=========================================================================//
  6. #include "cbase.h"
  7. #include "tf_logic_robot_destruction.h"
  8. #include "tf_shareddefs.h"
  9. #include "particle_parse.h"
  10. #include "tf_gamerules.h"
  11. #include "debugoverlay_shared.h"
  12. #ifdef GAME_DLL
  13. #include "tf_ammo_pack.h"
  14. #include "entity_bonuspack.h"
  15. #include "entity_capture_flag.h"
  16. #include "effect_dispatch_data.h"
  17. #include "te_effect_dispatch.h"
  18. #include "tf_gamestats.h"
  19. #include "eventlist.h"
  20. #else
  21. #include "eventlist.h"
  22. #endif
  23. #ifdef GAME_DLL
  24. extern ConVar tf_obj_gib_velocity_min;
  25. extern ConVar tf_obj_gib_velocity_max;
  26. extern ConVar tf_obj_gib_maxspeed;
  27. #endif
  28. #define ADD_POINTS_CONTEXT "add_points_context"
  29. #define SPEW_BARS_CONTEXT "spew_bars_context"
  30. #define SELF_DESTRUCT_THINK "self_destruct_think"
  31. #define ANIMS_THINK "anims_think"
  32. ConVar tf_rd_robot_repair_rate( "tf_rd_robot_repair_rate", "60", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  33. RobotData_t* g_RobotData[ NUM_ROBOT_TYPES ] =
  34. {
  35. // Model // Busted model // Pain // Death // Collide // Idle // Bar offset
  36. new RobotData_t( "models/bots/bot_worker/bot_worker_A.mdl", "models/bots/bot_worker/bot_worker_A.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -35.f ),
  37. #ifdef STAGING_ONLY
  38. new RobotData_t( "models/bots/bot_worker/bot_worker_b.mdl", "models/bots/bot_worker/bot_worker_b.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -30.f ),
  39. #else
  40. new RobotData_t( "models/bots/bot_worker/bot_worker2.mdl", "models/bots/bot_worker/bot_worker2.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -30.f ),
  41. #endif
  42. new RobotData_t( "models/bots/bot_worker/bot_worker3.mdl", "models/bots/bot_worker/bot_worker3_nohead.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -10.f ),
  43. };
  44. #define ROBOT_DEATH_EXPLOSION "RD.BotDeathExplosion"
  45. #define SCORING_POINTS_PARTICLE_EFFECT "bot_radio_waves"
  46. #define DAMAGED_ROBOT_PARTICLE_EFFECT "sentrydamage_4"
  47. #define DEATH_PARTICLE_EFFECT "rd_robot_explosion"
  48. void RobotData_t::Precache()
  49. {
  50. if ( GetStringData( MODEL_KEY ) )
  51. {
  52. CBaseEntity::PrecacheModel( GetStringData( MODEL_KEY ) );
  53. PrecacheGibsForModel( modelinfo->GetModelIndex( GetStringData( MODEL_KEY ) ) );
  54. PrecachePropsForModel( modelinfo->GetModelIndex( GetStringData( MODEL_KEY ) ), "spawn" );
  55. }
  56. if ( GetStringData( DAMAGED_MODEL_KEY ) ) CBaseEntity::PrecacheModel( GetStringData( DAMAGED_MODEL_KEY ) );
  57. if ( GetStringData( HURT_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( HURT_SOUND_KEY ) );
  58. if ( GetStringData( DEATH_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( DEATH_SOUND_KEY ) );
  59. if ( GetStringData( COLLIDE_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( COLLIDE_SOUND_KEY ) );
  60. if ( GetStringData( IDLE_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( IDLE_SOUND_KEY ) );
  61. }
  62. CTFRobotDestruction_RobotAnimController::CTFRobotDestruction_RobotAnimController( CTFRobotDestruction_Robot *pOuter )
  63. : m_vecOldOrigin( vec3_origin )
  64. , m_vecLean( vec3_origin )
  65. , m_pOuter( pOuter )
  66. , m_vecImpulse( vec3_origin )
  67. {}
  68. void CTFRobotDestruction_RobotAnimController::Update()
  69. {
  70. if( !m_pOuter )
  71. return;
  72. CStudioHdr *pStudioHdr = m_pOuter->GetModelPtr();
  73. if ( !pStudioHdr )
  74. return;
  75. const Vector vecNewOrigin = m_pOuter->GetAbsOrigin();
  76. const Vector vecVelocity = m_vecOldOrigin - vecNewOrigin;
  77. m_vecOldOrigin = vecNewOrigin;
  78. Approach( m_vecLean, vecVelocity + m_vecImpulse, 2.f );
  79. GetPoseParams();
  80. Approach( m_vecImpulse, vec3_origin, 200.f );
  81. Vector vecForward, vecRight;
  82. AngleVectors( m_pOuter->GetAbsAngles(), &vecForward, &vecRight, NULL );
  83. float flRightLean = vecRight.Dot( m_vecLean );
  84. float flForwardLean = vecForward.Dot( m_vecLean );
  85. m_pOuter->SetPoseParameter( m_poseParams.m_nMoveX, flForwardLean );
  86. m_pOuter->SetPoseParameter( m_poseParams.m_nMoveY, flRightLean );
  87. }
  88. void CTFRobotDestruction_RobotAnimController::Impulse( const Vector& vecImpulse )
  89. {
  90. m_vecImpulse += vecImpulse * 5;
  91. }
  92. void CTFRobotDestruction_RobotAnimController::Approach( Vector& vecIn, const Vector& vecTarget, float flRate )
  93. {
  94. Vector vecApproach = ( vecTarget - vecIn ) * flRate * gpGlobals->frametime;
  95. if ( vecApproach.LengthSqr() > ( vecIn - vecTarget ).LengthSqr() )
  96. vecIn = vecTarget;
  97. else
  98. vecIn += vecApproach;
  99. }
  100. void CTFRobotDestruction_RobotAnimController::GetPoseParams()
  101. {
  102. m_poseParams.m_nMoveX = m_pOuter->LookupPoseParameter( "move_x" );
  103. m_poseParams.m_nMoveY = m_pOuter->LookupPoseParameter( "move_y" );
  104. }
  105. IMPLEMENT_NETWORKCLASS_ALIASED( TFRobotDestruction_Robot, DT_TFRobotDestruction_Robot )
  106. BEGIN_NETWORK_TABLE( CTFRobotDestruction_Robot, DT_TFRobotDestruction_Robot )
  107. #ifdef CLIENT_DLL
  108. RecvPropInt( RECVINFO(m_iHealth) ),
  109. RecvPropInt( RECVINFO(m_iMaxHealth) ),
  110. RecvPropInt( RECVINFO(m_eType) ),
  111. #else
  112. SendPropInt(SENDINFO(m_iHealth), -1, SPROP_VARINT ),
  113. SendPropInt(SENDINFO(m_iMaxHealth), -1, SPROP_VARINT ),
  114. SendPropInt(SENDINFO(m_eType), -1, SPROP_VARINT ),
  115. #endif
  116. END_NETWORK_TABLE()
  117. BEGIN_DATADESC( CTFRobotDestruction_Robot )
  118. #ifdef GAME_DLL
  119. DEFINE_INPUTFUNC( FIELD_VOID, "StopAndUseComputer", InputStopAndUseComputer ),
  120. #endif
  121. END_DATADESC()
  122. LINK_ENTITY_TO_CLASS( tf_robot_destruction_robot, CTFRobotDestruction_Robot );
  123. CTFRobotDestruction_Robot::CTFRobotDestruction_Robot()
  124. : m_animController( this )
  125. {
  126. #ifdef GAME_DLL
  127. m_nPointsSpewed = 0;
  128. m_intention = new CRobotIntention( this );
  129. m_locomotor = new CRobotLocomotion( this );
  130. m_body = new CHeadlessHatmanBody( this );
  131. m_bIsPanicked = false;
  132. #else
  133. ListenForGameEvent( "rd_robot_impact" );
  134. #endif
  135. }
  136. CTFRobotDestruction_Robot::~CTFRobotDestruction_Robot()
  137. {
  138. #ifdef CLIENT_DLL
  139. m_hDamagedParticleEffect = NULL;
  140. #else
  141. if ( m_hSpawn )
  142. m_hSpawn->ClearRobot();
  143. if ( m_intention )
  144. delete m_intention;
  145. if ( m_locomotor )
  146. delete m_locomotor;
  147. if ( m_body )
  148. delete m_body;
  149. #endif
  150. }
  151. void CTFRobotDestruction_Robot::StaticPrecache()
  152. {
  153. PrecacheParticleSystem( DEATH_PARTICLE_EFFECT );
  154. PrecacheParticleSystem( SCORING_POINTS_PARTICLE_EFFECT );
  155. PrecacheParticleSystem( DAMAGED_ROBOT_PARTICLE_EFFECT );
  156. PrecacheScriptSound( ROBOT_DEATH_EXPLOSION );
  157. for( int i=0; i < ARRAYSIZE( g_RobotData ); ++i )
  158. {
  159. g_RobotData[i]->Precache();
  160. }
  161. }
  162. void CTFRobotDestruction_Robot::Precache()
  163. {
  164. BaseClass::Precache();
  165. StaticPrecache();
  166. }
  167. void CTFRobotDestruction_Robot::Spawn()
  168. {
  169. // Clear out the gib list and create a new one.
  170. m_aGibs.Purge();
  171. BuildGibList( m_aGibs, GetModelIndex(), 1.0f, COLLISION_GROUP_NONE );
  172. BuildPropList( "spawn", m_aSpawnProps, GetModelIndex(), 1.f, COLLISION_GROUP_NONE );
  173. BaseClass::Spawn();
  174. SetSolid( SOLID_BBOX );
  175. m_takedamage = DAMAGE_YES;
  176. m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
  177. #ifdef GAME_DLL
  178. SetContextThink( &CTFRobotDestruction_Robot::UpdateAnimsThink, gpGlobals->curtime, ANIMS_THINK );
  179. if ( m_hGroup )
  180. {
  181. m_hGroup->UpdateState();
  182. }
  183. m_hNextPath.Set( dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, m_spawnData.m_pszPathName ) ) );
  184. // The path needs to exist
  185. if ( !m_hNextPath )
  186. {
  187. UTIL_Remove( this );
  188. }
  189. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  190. CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotCreated( this );
  191. // Create our dispenser
  192. m_pDispenser = dynamic_cast<CRobotDispenser*>( CreateEntityByName( "rd_robot_dispenser" ) );
  193. Assert( m_pDispenser );
  194. m_pDispenser->SetParent( this );
  195. m_pDispenser->Spawn();
  196. m_pDispenser->ChangeTeam( GetTeamNumber() );
  197. m_pDispenser->OnGoActive();
  198. #endif
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Dont collide with players
  202. //-----------------------------------------------------------------------------
  203. bool CTFRobotDestruction_Robot::ShouldCollide( int collisionGroup, int contentsMask ) const
  204. {
  205. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
  206. {
  207. return false;
  208. }
  209. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  210. }
  211. #ifdef CLIENT_DLL
  212. //-----------------------------------------------------------------------------
  213. // Purpose: Specify where our healthbars should go over our heads
  214. //-----------------------------------------------------------------------------
  215. float CTFRobotDestruction_Robot::GetHealthBarHeightOffset() const
  216. {
  217. return g_RobotData[ m_eType ]->GetFloatData( RobotData_t::HEALTH_BAR_OFFSET_KEY );
  218. }
  219. void CTFRobotDestruction_Robot::OnDataChanged( DataUpdateType_t type )
  220. {
  221. BaseClass::OnDataChanged( type );
  222. if ( type == DATA_UPDATE_CREATED )
  223. {
  224. SetNextClientThink( CLIENT_THINK_ALWAYS );
  225. }
  226. UpdateDamagedEffects();
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose: Play damaged effects, similar to sentries
  230. //-----------------------------------------------------------------------------
  231. void CTFRobotDestruction_Robot::UpdateDamagedEffects()
  232. {
  233. // Start playing our damaged particle if we're damaged
  234. bool bLowHealth = GetHealth() <= (GetMaxHealth() * 0.5);
  235. if ( bLowHealth && !m_hDamagedParticleEffect )
  236. {
  237. m_hDamagedParticleEffect = ParticleProp()->Create( DAMAGED_ROBOT_PARTICLE_EFFECT,
  238. PATTACH_ABSORIGIN_FOLLOW,
  239. INVALID_PARTICLE_ATTACHMENT,
  240. Vector(0,0,50) );
  241. }
  242. else if ( !bLowHealth && m_hDamagedParticleEffect )
  243. {
  244. ParticleProp()->StopEmission( m_hDamagedParticleEffect );
  245. m_hDamagedParticleEffect = NULL;
  246. }
  247. }
  248. void CTFRobotDestruction_Robot::UpdateClientSideAnimation( void )
  249. {
  250. m_animController.Update();
  251. BaseClass::UpdateClientSideAnimation();
  252. }
  253. void CTFRobotDestruction_Robot::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
  254. {
  255. if ( event == AE_RD_ROBOT_POP_PANELS_OFF )
  256. {
  257. CUtlVector<breakmodel_t> vecProp;
  258. FOR_EACH_VEC( m_aSpawnProps, i )
  259. {
  260. char pstrLowerName[ MAX_PATH ];
  261. memset( pstrLowerName, 0, sizeof(pstrLowerName) );
  262. Q_snprintf( pstrLowerName, sizeof(pstrLowerName), "%s", options );
  263. Q_strlower( pstrLowerName );
  264. if ( Q_strstr( m_aSpawnProps[i].modelName, pstrLowerName ) )
  265. {
  266. vecProp.AddToTail( m_aSpawnProps[i] );
  267. }
  268. }
  269. if ( vecProp.Count() )
  270. {
  271. Vector vForward, vRight, vUp;
  272. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  273. Vector vecBreakVelocity = Vector(0,0,200);
  274. AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
  275. Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10;
  276. QAngle vecAngles = GetAbsAngles();
  277. breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse );
  278. breakParams.impactEnergyScale = 1.0f;
  279. breakParams.defBurstScale = 3.f;
  280. int nModelIndex = GetModelIndex();
  281. CreateGibsFromList( vecProp, nModelIndex, NULL, breakParams, this, -1 , false, true );
  282. }
  283. }
  284. BaseClass::FireEvent( origin, angles, event, options );
  285. }
  286. void CTFRobotDestruction_Robot::FireGameEvent( IGameEvent *pEvent )
  287. {
  288. const char *pszName = pEvent->GetName();
  289. if ( FStrEq( pszName, "rd_robot_impact" ) )
  290. {
  291. const int index_ = pEvent->GetInt( "entindex" );
  292. if ( index_ == entindex() )
  293. {
  294. Vector vecImpulse( pEvent->GetFloat( "impulse_x" ), pEvent->GetFloat( "impulse_y" ), pEvent->GetFloat( "impulse_z" ) );
  295. m_animController.Impulse( vecImpulse.Normalized() * 20 );
  296. }
  297. }
  298. }
  299. CStudioHdr* CTFRobotDestruction_Robot::OnNewModel()
  300. {
  301. CStudioHdr *hdr = BaseClass::OnNewModel();
  302. BuildPropList( "spawn", m_aSpawnProps, GetModelIndex(), 1.f, COLLISION_GROUP_NONE );
  303. return hdr;
  304. }
  305. #endif
  306. #ifdef GAME_DLL
  307. void CTFRobotDestruction_Robot::HandleAnimEvent( animevent_t *pEvent )
  308. {
  309. if ((pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER))
  310. {
  311. /* if ( pEvent->event == AE_RD_ROBOT_POP_PANELS_OFF )
  312. {
  313. CUtlVector<breakmodel_t> vecProp;
  314. FOR_EACH_VEC( m_aSpawnProps, i )
  315. {
  316. char pstrLowerName[ MAX_PATH ];
  317. memset( pstrLowerName, 0, sizeof(pstrLowerName) );
  318. Q_snprintf( pstrLowerName, sizeof(pstrLowerName), "%s", pEvent->options );
  319. Q_strlower( pstrLowerName );
  320. if ( Q_strstr( m_aSpawnProps[i].modelName, pstrLowerName ) )
  321. {
  322. vecProp.AddToTail( m_aSpawnProps[i] );
  323. }
  324. }
  325. if ( vecProp.Count() )
  326. {
  327. Vector vForward, vRight, vUp;
  328. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  329. Vector vecBreakVelocity = Vector(0,0,200);
  330. AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
  331. Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10;
  332. QAngle vecAngles = GetAbsAngles();
  333. breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse );
  334. breakParams.impactEnergyScale = 1.0f;
  335. int nModelIndex = modelinfo->GetModelIndex( STRING(GetModelName()) );
  336. CreateGibsFromList( vecProp, nModelIndex, NULL, breakParams, NULL, -1 , false, true );
  337. }
  338. }*/
  339. }
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Tell the game logic we're gone
  343. //-----------------------------------------------------------------------------
  344. void CTFRobotDestruction_Robot::UpdateOnRemove( void )
  345. {
  346. BaseClass::UpdateOnRemove();
  347. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  348. {
  349. CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotRemoved( this );
  350. }
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose: Play our death visual and audio effects
  354. //-----------------------------------------------------------------------------
  355. void CTFRobotDestruction_Robot::PlayDeathEffects()
  356. {
  357. EmitSound( g_RobotData[ GetRobotSpawnData().m_eType ]->GetStringData( RobotData_t::DEATH_SOUND_KEY ) );
  358. EmitSound( ROBOT_DEATH_EXPLOSION );
  359. DispatchParticleEffect( DEATH_PARTICLE_EFFECT, GetAbsOrigin(), QAngle( 0,0,0 ) );
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: Handle getting killed
  363. //-----------------------------------------------------------------------------
  364. void CTFRobotDestruction_Robot::Event_Killed( const CTakeDamageInfo &info )
  365. {
  366. // Let the game logic know that we died
  367. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  368. {
  369. CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotRemoved( this );
  370. }
  371. PlayDeathEffects();
  372. // Find the killer & the scorer
  373. CBaseEntity *pInflictor = info.GetInflictor();
  374. CBaseEntity *pKiller = info.GetAttacker();
  375. CTFPlayer *pScorer = ToTFPlayer( TFGameRules()->GetDeathScorer( pKiller, pInflictor, this ) );
  376. CTFPlayer *pAssister = NULL;
  377. if ( pScorer )
  378. {
  379. // If a player is healing the scorer, give that player credit for the assist
  380. CTFPlayer *pHealer = ToTFPlayer( static_cast<CBaseEntity *>( pScorer->m_Shared.GetFirstHealer() ) );
  381. // Must be a medic to receive a healing assist, otherwise engineers get credit for assists from dispensers doing healing.
  382. // Also don't give an assist for healing if the inflictor was a sentry gun, otherwise medics healing engineers get assists for the engineer's sentry kills.
  383. if ( pHealer && ( pHealer->GetPlayerClass()->GetClassIndex() == TF_CLASS_MEDIC ) )
  384. {
  385. pAssister = pHealer;
  386. }
  387. // Work out what killed the player, and send a message to all clients about it
  388. int iWeaponID;
  389. const char *killer_weapon_name = TFGameRules()->GetKillingWeaponName( info, NULL, &iWeaponID );
  390. const char *killer_weapon_log_name = killer_weapon_name;
  391. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( pScorer->Weapon_OwnsThisID( iWeaponID ) );
  392. if ( pWeapon )
  393. {
  394. CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem();
  395. if ( pItem )
  396. {
  397. if ( pItem->GetStaticData()->GetIconClassname() )
  398. {
  399. killer_weapon_name = pItem->GetStaticData()->GetIconClassname();
  400. }
  401. if ( pItem->GetStaticData()->GetLogClassname() )
  402. {
  403. killer_weapon_log_name = pItem->GetStaticData()->GetLogClassname();
  404. }
  405. }
  406. }
  407. IGameEvent *event = gameeventmanager->CreateEvent( "rd_robot_killed" );
  408. if ( event )
  409. {
  410. if ( pAssister && ( pAssister != pScorer ) )
  411. {
  412. event->SetInt( "assister", pAssister->GetUserID() );
  413. }
  414. event->SetInt( "attacker", pScorer->GetUserID() ); // attacker
  415. event->SetString( "weapon", killer_weapon_name );
  416. event->SetString( "weapon_logclassname", killer_weapon_log_name );
  417. event->SetInt( "weaponid", iWeaponID );
  418. event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
  419. gameeventmanager->FireEvent( event );
  420. }
  421. }
  422. if ( m_hSpawn )
  423. {
  424. m_hSpawn->OnRobotKilled();
  425. }
  426. if ( m_hGroup )
  427. {
  428. m_hGroup->OnRobotKilled();
  429. }
  430. // Kings dont die right away. Their head cracks open and they spew points out over time, then self-destruct
  431. if ( m_spawnData.m_eType == ROBOT_TYPE_KING )
  432. {
  433. // Change our model to be the damage king model
  434. SetModel( g_RobotData[ m_spawnData.m_eType ]->GetStringData( RobotData_t::DAMAGED_MODEL_KEY ) );
  435. ResetSequence( LookupSequence("idle") );
  436. m_takedamage = DAMAGE_NO;
  437. SetContextThink( &CTFRobotDestruction_Robot::SpewBarsThink, gpGlobals->curtime, SPEW_BARS_CONTEXT );
  438. SetContextThink( &CTFRobotDestruction_Robot::SelfDestructThink, gpGlobals->curtime + 5.f, SELF_DESTRUCT_THINK );
  439. return;
  440. }
  441. // Spew our points
  442. SpewBars( m_spawnData.m_nNumGibs );
  443. // Spew ammo gibs out
  444. SpewGibs();
  445. CBaseAnimating::Event_Killed( info );
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Purpose: Spew out robot gibs that give ammo
  449. //-----------------------------------------------------------------------------
  450. void CTFRobotDestruction_Robot::SpewGibs()
  451. {
  452. for ( int i=0; i<m_aGibs.Count(); i++ )
  453. {
  454. CTFAmmoPack *pAmmoPack = CTFAmmoPack::Create( GetAbsOrigin() + m_aGibs[i].offset, GetAbsAngles(), this, m_aGibs[i].modelName );
  455. Assert( pAmmoPack );
  456. if ( pAmmoPack )
  457. {
  458. pAmmoPack->ActivateWhenAtRest();
  459. // Calculate the initial impulse on the weapon.
  460. Vector vecImpulse( random->RandomFloat( -0.5f, 0.5f ), random->RandomFloat( -0.5f, 0.5f ), random->RandomFloat( 0.75f, 1.25f ) );
  461. VectorNormalize( vecImpulse );
  462. // Detect the head model
  463. bool bIsTheHead = FStrEq( "models/bots/bot_worker/bot_worker_head_gib.mdl", m_aGibs[i].modelName );
  464. if ( bIsTheHead )
  465. {
  466. // Pop more up than anything
  467. vecImpulse[2] = 3.f;
  468. vecImpulse *= random->RandomFloat( tf_obj_gib_velocity_max.GetFloat() * 0.75, tf_obj_gib_velocity_max.GetFloat() );
  469. }
  470. else
  471. {
  472. vecImpulse *= random->RandomFloat( tf_obj_gib_velocity_min.GetFloat(), tf_obj_gib_velocity_max.GetFloat() );
  473. }
  474. // Cap the impulse.
  475. float flSpeed = vecImpulse.Length();
  476. if ( flSpeed > tf_obj_gib_maxspeed.GetFloat() )
  477. {
  478. VectorScale( vecImpulse, tf_obj_gib_maxspeed.GetFloat() / flSpeed, vecImpulse );
  479. }
  480. if ( pAmmoPack->VPhysicsGetObject() )
  481. {
  482. AngularImpulse angImpulse( 0.f, random->RandomFloat( 0.f, 100.f ), 0.f );
  483. if ( bIsTheHead )
  484. {
  485. // Make the head spin around like a top
  486. angImpulse = AngularImpulse( RandomFloat(-60.f,60.f), RandomFloat(-60.f,60.f), 100000.f );
  487. }
  488. pAmmoPack->VPhysicsGetObject()->SetVelocityInstantaneous( &vecImpulse, &angImpulse );
  489. }
  490. pAmmoPack->SetInitialVelocity( vecImpulse );
  491. pAmmoPack->m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
  492. // Give the ammo pack some health, so that trains can destroy it.
  493. pAmmoPack->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  494. pAmmoPack->m_takedamage = DAMAGE_YES;
  495. pAmmoPack->SetHealth( 900 );
  496. pAmmoPack->m_bObjGib = true;
  497. }
  498. }
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose: Do some special effects when we take damage
  502. //-----------------------------------------------------------------------------
  503. int CTFRobotDestruction_Robot::OnTakeDamage( const CTakeDamageInfo &info )
  504. {
  505. // Check teams
  506. if ( info.GetAttacker() )
  507. {
  508. if ( InSameTeam(info.GetAttacker()) )
  509. return 0;
  510. CBasePlayer *pAttacker = ToBasePlayer( info.GetAttacker() );
  511. if ( pAttacker )
  512. {
  513. pAttacker->SetLastObjectiveTime( gpGlobals->curtime );
  514. }
  515. }
  516. SetContextThink( &CTFRobotDestruction_Robot::RepairSelfThink, gpGlobals->curtime + 5.f, "RepairSelfThink" );
  517. if ( m_bShielded )
  518. {
  519. return 0;
  520. }
  521. CTakeDamageInfo newInfo;
  522. newInfo = info;
  523. ModifyDamage( &newInfo );
  524. // Get our attack vectors
  525. Vector vecDamagePos = newInfo.GetDamagePosition();
  526. QAngle vecDamageAngles;
  527. VectorAngles( -newInfo.GetDamageForce(), vecDamageAngles );
  528. // Use worldspace center if no damage position (happens with flamethrowers)
  529. if ( vecDamagePos == vec3_origin )
  530. {
  531. vecDamagePos = WorldSpaceCenter();
  532. }
  533. // Play a spark effect
  534. DispatchParticleEffect( "rd_bot_impact_sparks", vecDamagePos, vecDamageAngles );
  535. // Send an impulse event to the client for this bot
  536. Vector vecImpulse( newInfo.GetDamageForce() );
  537. m_animController.Impulse( vecImpulse.Normalized() * 20.f );
  538. IGameEvent *event = gameeventmanager->CreateEvent( "rd_robot_impact" );
  539. if ( event )
  540. {
  541. event->SetInt( "entindex", entindex() );
  542. event->SetInt( "impulse_x", vecImpulse.x );
  543. event->SetInt( "impulse_y", vecImpulse.y );
  544. event->SetInt( "impulse_z", vecImpulse.z );
  545. gameeventmanager->FireEvent( event );
  546. }
  547. if( m_hGroup )
  548. {
  549. m_hGroup->OnRobotAttacked();
  550. }
  551. int nBaseResult = BaseClass::OnTakeDamage( newInfo );
  552. // Let the game logic know that we got hurt
  553. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  554. CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotAttacked( this );
  555. return nBaseResult;
  556. }
  557. void CTFRobotDestruction_Robot::ModifyDamage( CTakeDamageInfo *info ) const
  558. {
  559. CTFPlayer *pAttacker = ToTFPlayer( info->GetAttacker() );
  560. if ( pAttacker )
  561. {
  562. float flScale = 1.f;
  563. if ( pAttacker->IsPlayerClass( TF_CLASS_SCOUT ) )
  564. flScale = 1.5f;
  565. else if( pAttacker->IsPlayerClass( TF_CLASS_SNIPER ) )
  566. flScale = 2.25f;
  567. else if ( pAttacker->IsPlayerClass( TF_CLASS_SPY ) )
  568. flScale = 2.f;
  569. else if ( pAttacker->IsPlayerClass( TF_CLASS_PYRO ) )
  570. flScale = 0.75;
  571. else if ( pAttacker->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  572. flScale = 0.75;
  573. else if ( pAttacker->IsPlayerClass( TF_CLASS_MEDIC ) )
  574. flScale = 2.f;
  575. info->SetDamage( info->GetDamage() * flScale );
  576. }
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose: Override base traceattack to prevent visible effects from team members shooting me
  580. //-----------------------------------------------------------------------------
  581. void CTFRobotDestruction_Robot::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  582. {
  583. // Prevent team damage here so blood doesn't appear
  584. if ( inputInfo.GetAttacker() && InSameTeam(inputInfo.GetAttacker()) )
  585. return;
  586. AddMultiDamage( inputInfo, this );
  587. }
  588. void CTFRobotDestruction_Robot::UpdateAnimsThink( void )
  589. {
  590. m_animController.Update();
  591. SetContextThink( &CTFRobotDestruction_Robot::UpdateAnimsThink, gpGlobals->curtime, ANIMS_THINK );
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Purpose: Change our AI state to use a computer
  595. //-----------------------------------------------------------------------------
  596. void CTFRobotDestruction_Robot::InputStopAndUseComputer( inputdata_t &inputdata )
  597. {
  598. // TODO: Fire off into the AI
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose: Shoot bars out as we die
  602. //-----------------------------------------------------------------------------
  603. void CTFRobotDestruction_Robot::SpewBars( int nNumToSpew )
  604. {
  605. for( int i=0; i < nNumToSpew; ++i )
  606. {
  607. CBonusPack *pBonusPack = assert_cast< CBonusPack* >( CreateEntityByName( "item_bonuspack" ) );
  608. if ( pBonusPack )
  609. {
  610. pBonusPack->ChangeTeam( GetEnemyTeam( GetTeamNumber() ) );
  611. pBonusPack->SetDisabled( false );
  612. pBonusPack->SetAbsOrigin( GetAbsOrigin() + Vector(0,0,20) );
  613. pBonusPack->SetAbsAngles( QAngle( 0.f, RandomFloat( 0, 360.f ), 0.f ) );
  614. // Calculate the initial impulse on the cores
  615. Vector vecImpulse( random->RandomFloat( -0.5, 0.5 ), random->RandomFloat( -0.5, 0.5 ), random->RandomFloat( 1.0, 1.25 ) );
  616. VectorNormalize( vecImpulse );
  617. vecImpulse *= random->RandomFloat( 125.f, 150.f );
  618. // Cap the impulse.
  619. float flSpeed = vecImpulse.Length();
  620. if ( flSpeed > tf_obj_gib_maxspeed.GetFloat() )
  621. {
  622. VectorScale( vecImpulse, tf_obj_gib_maxspeed.GetFloat() / flSpeed, vecImpulse );
  623. }
  624. pBonusPack->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  625. pBonusPack->AddSpawnFlags( SF_NORESPAWN );
  626. pBonusPack->m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1;
  627. DispatchSpawn( pBonusPack );
  628. pBonusPack->DropSingleInstance( vecImpulse, NULL, 0, 0 );
  629. pBonusPack->SetCycle( RandomFloat( 0.f, 1.f ) );
  630. pBonusPack->SetGravity( 0.2f );
  631. }
  632. }
  633. }
  634. void CTFRobotDestruction_Robot::SpewBarsThink()
  635. {
  636. int nNumToSpew = 1;
  637. m_nPointsSpewed += nNumToSpew;
  638. SpewBars( nNumToSpew );
  639. if ( m_nPointsSpewed >= m_spawnData.m_nNumGibs )
  640. {
  641. SelfDestructThink();
  642. }
  643. else
  644. {
  645. SetContextThink( &CTFRobotDestruction_Robot::SpewBarsThink, gpGlobals->curtime + 0.1f, SPEW_BARS_CONTEXT );
  646. }
  647. }
  648. void CTFRobotDestruction_Robot::SelfDestructThink()
  649. {
  650. SpewGibs();
  651. SpewBars( m_spawnData.m_nNumGibs - m_nPointsSpewed );
  652. PlayDeathEffects();
  653. UTIL_Remove( this );
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Purpose: Repair ourselves!
  657. //-----------------------------------------------------------------------------
  658. void CTFRobotDestruction_Robot::RepairSelfThink()
  659. {
  660. // Heal!
  661. int nHealth = GetHealth();
  662. if ( tf_rd_robot_repair_rate.GetFloat() != 0.f )
  663. {
  664. nHealth += GetMaxHealth() / tf_rd_robot_repair_rate.GetFloat();
  665. }
  666. nHealth = Min( nHealth, GetMaxHealth() );
  667. SetHealth( nHealth );
  668. // Continue to heal if we're still hurt
  669. if ( GetHealth() != GetMaxHealth() )
  670. {
  671. SetContextThink( &CTFRobotDestruction_Robot::RepairSelfThink, gpGlobals->curtime + 1.f, "RepairSelfThink" );
  672. }
  673. }
  674. void CTFRobotDestruction_Robot::ArriveAtPath()
  675. {
  676. m_hNextPath->AcceptInput( "InPass", this, this, variant_t(), 0 );
  677. m_hNextPath = m_hNextPath->GetNext();
  678. }
  679. void CTFRobotDestruction_Robot::EnableUber()
  680. {
  681. m_bShielded = true;
  682. m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 2 : 3;
  683. if ( m_hGroup )
  684. {
  685. m_hGroup->UpdateState();
  686. }
  687. }
  688. void CTFRobotDestruction_Robot::DisableUber()
  689. {
  690. m_bShielded = false;
  691. m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1;
  692. if ( m_hGroup )
  693. {
  694. m_hGroup->UpdateState();
  695. }
  696. }
  697. void CTFRobotDestruction_Robot::SetNewActivity( Activity activity )
  698. {
  699. int nSequence = SelectWeightedSequence( activity );
  700. if ( nSequence )
  701. {
  702. SetSequence( nSequence );
  703. SetPlaybackRate( 1.0f );
  704. SetCycle( 0 );
  705. ResetSequenceInfo();
  706. }
  707. }
  708. #define CLOSE_ENOUGH_TO_PATH 50.f
  709. //---------------------------------------------------------------------------------------------
  710. class CRobotPatrol : public Action< CTFRobotDestruction_Robot >
  711. {
  712. public:
  713. void PlayIdleActivity( CTFRobotDestruction_Robot *pMe )
  714. {
  715. pMe->SetNewActivity( ACT_BOT_PRIMARY_MOVEMENT );
  716. }
  717. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  718. {
  719. PlayIdleActivity( pMe );
  720. return Continue();
  721. }
  722. virtual ActionResult< CTFRobotDestruction_Robot > OnResume( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *interruptingAction )
  723. {
  724. PlayIdleActivity( pMe );
  725. return Continue();
  726. }
  727. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval )
  728. {
  729. CPathTrack* pNextPath = pMe->GetNextPath();
  730. if ( pMe->IsRangeGreaterThan( pNextPath, CLOSE_ENOUGH_TO_PATH ) )
  731. {
  732. if ( m_path.GetAge() > 0.5f )
  733. {
  734. CRobotPathCost cost( pMe );
  735. m_path.Compute( pMe, pNextPath->GetAbsOrigin(), cost );
  736. }
  737. m_path.Update( pMe );
  738. }
  739. else
  740. {
  741. pMe->ArriveAtPath();
  742. }
  743. return Continue();
  744. }
  745. virtual const char *GetName( void ) const { return "Patrol"; } // return name of this action
  746. PathFollower m_path;
  747. };
  748. class CRobotSpawn : public Action< CTFRobotDestruction_Robot >
  749. {
  750. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  751. {
  752. pMe->SetNewActivity( ACT_BOT_SPAWN );
  753. return Continue();
  754. }
  755. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval )
  756. {
  757. if ( pMe->IsActivityFinished() )
  758. {
  759. return ChangeTo( new CRobotPatrol, "I've finished my spawn sequence" );
  760. }
  761. return Continue();
  762. }
  763. EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info )
  764. {
  765. return TryToSustain( RESULT_CRITICAL, "I'm spawning and being attacked" );
  766. }
  767. virtual void OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction )
  768. {
  769. pMe->SetBodygroup( pMe->FindBodygroupByName( "head_shell" ), 1 );
  770. pMe->SetBodygroup( pMe->FindBodygroupByName( "body_shell" ), 1 );
  771. }
  772. virtual const char *GetName( void ) const { return "Spawn"; } // return name of this action
  773. };
  774. class CRobotMaterialize : public Action< CTFRobotDestruction_Robot >
  775. {
  776. public:
  777. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  778. {
  779. // TODO: Play the materialize anim and effects
  780. /*int nSequence = pMe->SelectWeightedSequence( ACT_BOT_MATERIALIZE );
  781. pMe->ResetSequence( nSequence );*/
  782. return Continue();
  783. }
  784. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval )
  785. {
  786. // TODO: Check if materialize activity is finished
  787. //if ( pMe->IsActivityFinished() )
  788. {
  789. return ChangeTo( new CRobotSpawn, "I've fully materialized" );
  790. }
  791. // TODO: Control the materialize anim
  792. return Continue();
  793. }
  794. virtual const char *GetName( void ) const { return "Materialize"; } // return name of this action
  795. PathFollower m_path;
  796. };
  797. class CRobotPanic : public Action< CTFRobotDestruction_Robot >
  798. {
  799. public:
  800. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction );
  801. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval );
  802. virtual EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info );
  803. virtual void OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction );
  804. virtual const char *GetName( void ) const { return "Panic"; }
  805. private:
  806. CountdownTimer m_SpeakTimer;
  807. CountdownTimer m_attackedTimer;
  808. CountdownTimer m_spinTimer;
  809. bool m_bSpinRight;
  810. };
  811. class CRobotEnterPanic : public Action< CTFRobotDestruction_Robot >
  812. {
  813. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  814. {
  815. pMe->SetNewActivity( ACT_BOT_PANIC_START );
  816. return Continue();
  817. }
  818. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval )
  819. {
  820. if ( pMe->IsActivityFinished() )
  821. {
  822. return ChangeTo( new CRobotPanic, "I've finished my enter panic sequence" );
  823. }
  824. return Continue();
  825. }
  826. EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info )
  827. {
  828. return TryToSustain( RESULT_CRITICAL, "I'm entering panic and being attacked" );
  829. }
  830. virtual const char *GetName( void ) const { return "Enter Panic"; } // return name of this action
  831. };
  832. class CRobotLeavePanic : public Action< CTFRobotDestruction_Robot >
  833. {
  834. virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  835. {
  836. pMe->SetNewActivity( ACT_BOT_PANIC_END );
  837. return Continue();
  838. }
  839. virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval )
  840. {
  841. if ( pMe->IsActivityFinished() )
  842. {
  843. return Done( "I've finished my leave panic sequence" );
  844. }
  845. return Continue();
  846. }
  847. EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info )
  848. {
  849. return TryToSustain( RESULT_CRITICAL, "I'm leaving panic and being attacked" );
  850. }
  851. virtual const char *GetName( void ) const { return "Leave Panic"; } // return name of this action
  852. };
  853. //---------------------------------------------------------------------------------------------
  854. ActionResult< CTFRobotDestruction_Robot > CRobotPanic::OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  855. {
  856. m_bSpinRight = RandomInt(0,1) == 1; // Randomly pick which way to spin
  857. pMe->SetIsPanicked( true ); // Let our bot know he's panicked
  858. m_attackedTimer.Start( 5.f ); // We panic for 5 seconds
  859. m_spinTimer.Start( RandomFloat( 0.75f, 1.25f ) ); // Spin for a little bit
  860. pMe->GetLocomotionInterface()->SetDesiredSpeed( 150.f ); // We go fast when panicked
  861. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pMe, "wheel" ); // Smoke trails on our tire when panicked
  862. pMe->SetNewActivity( ACT_BOT_PANIC ); // Play panicked activity
  863. m_SpeakTimer.Start( 3.f );
  864. const RobotSpawnData_t & data = pMe->GetRobotSpawnData();
  865. pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::HURT_SOUND_KEY ) );
  866. return Continue();
  867. }
  868. ActionResult< CTFRobotDestruction_Robot > CRobotPanic::Update( CTFRobotDestruction_Robot *pMe, float interval )
  869. {
  870. // If we haven't been attacked in awhile, then we're done panicking
  871. if ( m_attackedTimer.IsElapsed() )
  872. {
  873. return ChangeTo( new CRobotLeavePanic, "I'm done panicking" );
  874. }
  875. QAngle angles = pMe->GetLocalAngles();
  876. // If our spin timer is still going, then spin!
  877. if ( m_spinTimer.GetRemainingTime() < ( m_spinTimer.GetCountdownDuration() * 0.5f ) )
  878. {
  879. float flSpinAmt = 2500.f * gpGlobals->frametime;
  880. flSpinAmt *= m_bSpinRight ? 1.f : -1.f;
  881. angles.y += flSpinAmt;
  882. pMe->SetLocalAngles( angles );
  883. }
  884. // We just drive forard
  885. Vector vForward;
  886. AngleVectors( angles, &vForward );
  887. Vector vArrivePosition = pMe->GetAbsOrigin() + vForward * 30;
  888. trace_t tr;
  889. // See if we hit anything solid a little bit below the robot. We dont want to jump off cliffs
  890. UTIL_TraceLine( vArrivePosition, vArrivePosition + Vector(0,0,-30), MASK_PLAYERSOLID, pMe, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
  891. if ( tr.fraction < 1.0f )
  892. {
  893. pMe->GetLocomotionInterface()->Approach( vArrivePosition );
  894. }
  895. // It's time change our spin direction, choose when to spin next, and reapply our smoke particle
  896. if ( m_spinTimer.IsElapsed() )
  897. {
  898. m_bSpinRight = RandomInt(0,1) == 1;
  899. m_spinTimer.Start( RandomFloat( 0.75f, 1.25f ) );
  900. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pMe, "wheel" );
  901. }
  902. return Continue();
  903. }
  904. EventDesiredResult< CTFRobotDestruction_Robot > CRobotPanic::OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info )
  905. {
  906. if ( m_SpeakTimer.IsElapsed() )
  907. {
  908. m_SpeakTimer.Start( RandomFloat( 1.5f, 2.f ) );
  909. const RobotSpawnData_t & data = pMe->GetRobotSpawnData();
  910. pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::HURT_SOUND_KEY ) );
  911. }
  912. m_attackedTimer.Start( m_attackedTimer.GetCountdownDuration() );
  913. return TryToSustain( RESULT_IMPORTANT, "I'm panicking and getting attacked" );
  914. }
  915. void CRobotPanic::OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction )
  916. {
  917. pMe->SetIsPanicked( false );
  918. pMe->GetLocomotionInterface()->SetDesiredSpeed( 80.f );
  919. }
  920. //---------------------------------------------------------------------------------------------
  921. Action< CTFRobotDestruction_Robot > *CRobotBehavior::InitialContainedAction( CTFRobotDestruction_Robot *pMe )
  922. {
  923. return new CRobotMaterialize;
  924. }
  925. ActionResult< CTFRobotDestruction_Robot > CRobotBehavior::OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction )
  926. {
  927. return Continue();
  928. }
  929. #ifdef STAGING_ONLY
  930. ConVar sv_rd_bots_STFU( "sv_rd_bots_STFU", "0", FCVAR_ARCHIVE );
  931. #endif
  932. ActionResult< CTFRobotDestruction_Robot > CRobotBehavior::Update( CTFRobotDestruction_Robot *pMe, float interval )
  933. {
  934. //const CKnownEntity *pThreat = pMe->GetVisionInterface()->GetPrimaryKnownThreat();
  935. //if ( pThreat )
  936. //{
  937. // return SuspendFor( new CRobotAttackEnemy, "I see an enemy!" );
  938. //}
  939. #ifdef STAGING_ONLY
  940. if ( !sv_rd_bots_STFU.GetBool() )
  941. #endif
  942. {
  943. // We've been wandering for a bit. Speak!
  944. if ( m_IdleSpeakTimer.IsElapsed() && m_SpeakTimer.IsElapsed() )
  945. {
  946. m_SpeakTimer.Start( 1.f );
  947. m_IdleSpeakTimer.Start( RandomFloat( 6.f, 10.f ) );
  948. const RobotSpawnData_t & data = pMe->GetRobotSpawnData();
  949. pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::IDLE_SOUND_KEY ) );
  950. }
  951. }
  952. // Do stuff!
  953. return Continue();
  954. }
  955. EventDesiredResult< CTFRobotDestruction_Robot > CRobotBehavior::OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info )
  956. {
  957. return TrySuspendFor( new CRobotEnterPanic, RESULT_TRY, "I've been attacked" );
  958. }
  959. EventDesiredResult< CTFRobotDestruction_Robot > CRobotBehavior::OnContact( CTFRobotDestruction_Robot *pMe, CBaseEntity *pOther, CGameTrace *result )
  960. {
  961. if ( m_SpeakTimer.IsElapsed() && ( pOther->IsPlayer() || dynamic_cast< CTFRobotDestruction_Robot * >( pOther ) ) )
  962. {
  963. m_SpeakTimer.Start( 3.f );
  964. #ifdef STAGING_ONLY
  965. if ( !sv_rd_bots_STFU.GetBool() )
  966. #endif
  967. {
  968. const RobotSpawnData_t & data = pMe->GetRobotSpawnData();
  969. pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::COLLIDE_SOUND_KEY ) );
  970. }
  971. }
  972. return TryContinue( RESULT_TRY );
  973. }
  974. //---------------------------------------------------------------------------------------------
  975. CRobotIntention::CRobotIntention( CTFRobotDestruction_Robot *pMe ) : IIntention( pMe )
  976. {
  977. m_behavior = new Behavior< CTFRobotDestruction_Robot >( new CRobotBehavior );
  978. }
  979. CRobotIntention::~CRobotIntention()
  980. {
  981. delete m_behavior;
  982. }
  983. void CRobotIntention::Reset( void )
  984. {
  985. delete m_behavior;
  986. m_behavior = new Behavior< CTFRobotDestruction_Robot >( new CRobotBehavior );
  987. }
  988. void CRobotIntention::Update( void )
  989. {
  990. m_behavior->Update( static_cast< CTFRobotDestruction_Robot * >( GetBot() ), GetUpdateInterval() );
  991. }
  992. // is this a place we can be?
  993. QueryResultType CRobotIntention::IsPositionAllowed( const INextBot *pMeBot, const Vector &pos ) const
  994. {
  995. return ANSWER_YES;
  996. }
  997. //---------------------------------------------------------------------------------------------
  998. float CRobotLocomotion::GetRunSpeed( void ) const
  999. {
  1000. CTFRobotDestruction_Robot *pRobotMe = static_cast< CTFRobotDestruction_Robot *>( GetBot()->GetEntity() );
  1001. return pRobotMe->GetIsPanicked() ? 150.f : 80.f;
  1002. }
  1003. float CRobotLocomotion::GetGroundSpeed() const
  1004. {
  1005. CTFRobotDestruction_Robot *pRobotMe = static_cast< CTFRobotDestruction_Robot *>( GetBot()->GetEntity() );
  1006. return pRobotMe->GetIsPanicked() ? 150.f : 80.f;
  1007. }
  1008. //---------------------------------------------------------------------------------------------
  1009. // if delta Z is greater than this, we have to jump to get up
  1010. float CRobotLocomotion::GetStepHeight( void ) const
  1011. {
  1012. return 24.0f;
  1013. }
  1014. //---------------------------------------------------------------------------------------------
  1015. // return maximum height of a jump
  1016. float CRobotLocomotion::GetMaxJumpHeight( void ) const
  1017. {
  1018. return 40.0f;
  1019. }
  1020. //---------------------------------------------------------------------------------------------
  1021. // Return max rate of yaw rotation
  1022. float CRobotLocomotion::GetMaxYawRate( void ) const
  1023. {
  1024. return 200.0f;
  1025. }
  1026. //---------------------------------------------------------------------------------------------
  1027. bool CRobotLocomotion::ShouldCollideWith( const CBaseEntity *object ) const
  1028. {
  1029. return false;
  1030. }
  1031. #endif
  1032. //-----------------------------------------------------------------------------
  1033. // Robot Dispenser
  1034. //-----------------------------------------------------------------------------
  1035. BEGIN_DATADESC( CRobotDispenser )
  1036. END_DATADESC()
  1037. IMPLEMENT_NETWORKCLASS_ALIASED( RobotDispenser, DT_RobotDispenser )
  1038. LINK_ENTITY_TO_CLASS( rd_robot_dispenser, CRobotDispenser );
  1039. BEGIN_NETWORK_TABLE( CRobotDispenser, DT_RobotDispenser )
  1040. END_NETWORK_TABLE()
  1041. #ifdef GAME_DLL
  1042. //-----------------------------------------------------------------------------
  1043. // Purpose:
  1044. //-----------------------------------------------------------------------------
  1045. CRobotDispenser::CRobotDispenser()
  1046. {
  1047. m_bUseGenerateMetalSound = false;
  1048. }
  1049. //-----------------------------------------------------------------------------
  1050. // Purpose:
  1051. //-----------------------------------------------------------------------------
  1052. void CRobotDispenser::Spawn( void )
  1053. {
  1054. // This cast is for the benefit of GCC
  1055. m_fObjectFlags |= (int)OF_DOESNT_HAVE_A_MODEL;
  1056. m_takedamage = DAMAGE_NO;
  1057. m_iUpgradeLevel = 1;
  1058. TFGameRules()->OnDispenserBuilt( this );
  1059. }
  1060. //-----------------------------------------------------------------------------
  1061. // Purpose: Finished building
  1062. //-----------------------------------------------------------------------------
  1063. void CRobotDispenser::OnGoActive( void )
  1064. {
  1065. BaseClass::OnGoActive();
  1066. if ( m_hTouchTrigger )
  1067. {
  1068. m_hTouchTrigger->SetParent( GetParent() );
  1069. }
  1070. SetModel( "" );
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Spawn the vgui control screens on the object
  1074. //-----------------------------------------------------------------------------
  1075. void CRobotDispenser::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
  1076. {
  1077. // no panels
  1078. return;
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Purpose:
  1082. //-----------------------------------------------------------------------------
  1083. void CRobotDispenser::SetModel( const char *pModel )
  1084. {
  1085. CBaseObject::SetModel( pModel );
  1086. }
  1087. #endif