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.

599 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_player.h"
  8. #include "tf_gamerules.h"
  9. #include "tf_team.h"
  10. #include "nav_mesh/tf_nav_area.h"
  11. #include "NextBot/Path/NextBotChasePath.h"
  12. #include "particle_parse.h"
  13. #include "zombie.h"
  14. #include "zombie_behavior/zombie_spawn.h"
  15. #include "halloween/tf_weapon_spellbook.h"
  16. #define SKELETON_MODEL "models/bots/skeleton_sniper/skeleton_sniper.mdl"
  17. #define SKELETON_KING_MODEL "models/bots/skeleton_sniper_boss/skeleton_sniper_boss.mdl"
  18. #define SKELETON_KING_CROWN_MODEL "models/player/items/demo/crown.mdl"
  19. ConVar tf_max_active_zombie( "tf_max_active_zombie", "30", FCVAR_CHEAT );
  20. #ifdef STAGING_ONLY
  21. ConVar tf_halloween_skeleton_test_hat( "tf_halloween_skeleton_test_hat", "-1", FCVAR_CHEAT );
  22. #endif // STAGING_ONLY
  23. //-----------------------------------------------------------------------------------------------------
  24. // NPC Zombie versions of the players
  25. //-----------------------------------------------------------------------------------------------------
  26. LINK_ENTITY_TO_CLASS( tf_zombie, CZombie );
  27. IMPLEMENT_SERVERCLASS_ST( CZombie, DT_Zombie )
  28. SendPropFloat( SENDINFO( m_flHeadScale ) ),
  29. END_SEND_TABLE()
  30. IMPLEMENT_AUTO_LIST( IZombieAutoList );
  31. static const char *s_skeletonHatModels[] =
  32. {
  33. "models/player/items/all_class/skull_scout.mdl",
  34. "models/workshop/player/items/scout/hw2013_boston_bandy_mask/hw2013_boston_bandy_mask.mdl",
  35. "models/workshop/player/items/demo/hw2013_blackguards_bicorn/hw2013_blackguards_bicorn.mdl",
  36. "models/player/items/heavy/heavy_big_chief.mdl",
  37. };
  38. //-----------------------------------------------------------------------------------------------------
  39. CZombie::CZombie()
  40. {
  41. m_intention = new CZombieIntention( this );
  42. m_locomotor = new CZombieLocomotion( this );
  43. m_body = new CHeadlessHatmanBody( this );
  44. m_nType = SKELETON_NORMAL;
  45. m_flHeadScale = 1.f;
  46. m_flAttackRange = 50.f;
  47. m_flAttackDamage = 30.f;
  48. m_bSpy = false;
  49. m_bForceSuicide = false;
  50. }
  51. //-----------------------------------------------------------------------------------------------------
  52. CZombie::~CZombie()
  53. {
  54. if ( m_intention )
  55. delete m_intention;
  56. if ( m_locomotor )
  57. delete m_locomotor;
  58. if ( m_body )
  59. delete m_body;
  60. }
  61. void CZombie::PrecacheZombie()
  62. {
  63. /*PrecacheModel( "models/player/items/scout/scout_zombie.mdl" );
  64. PrecacheModel( "models/player/items/sniper/sniper_zombie.mdl" );
  65. PrecacheModel( "models/player/items/soldier/soldier_zombie.mdl" );
  66. PrecacheModel( "models/player/items/demo/demo_zombie.mdl" );
  67. PrecacheModel( "models/player/items/medic/medic_zombie.mdl" );
  68. PrecacheModel( "models/player/items/heavy/heavy_zombie.mdl" );
  69. PrecacheModel( "models/player/items/pyro/pyro_zombie.mdl" );
  70. PrecacheModel( "models/player/items/spy/spy_zombie.mdl" );
  71. PrecacheModel( "models/player/items/engineer/engineer_zombie.mdl" );*/
  72. int nSkeletonModel = PrecacheModel( SKELETON_MODEL );
  73. PrecacheGibsForModel( nSkeletonModel );
  74. int nSkeletonKingModel = PrecacheModel( SKELETON_KING_MODEL );
  75. PrecacheGibsForModel( nSkeletonKingModel );
  76. PrecacheModel( SKELETON_KING_CROWN_MODEL );
  77. if( TFGameRules()->GetHalloweenScenario() == CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY )
  78. {
  79. for ( int i=0; i<ARRAYSIZE( s_skeletonHatModels ) ; ++i )
  80. {
  81. PrecacheModel( s_skeletonHatModels[i] );
  82. }
  83. }
  84. PrecacheParticleSystem( "bomibomicon_ring" );
  85. PrecacheParticleSystem( "spell_pumpkin_mirv_goop_red" );
  86. PrecacheParticleSystem( "spell_pumpkin_mirv_goop_blue" );
  87. PrecacheParticleSystem( "spell_skeleton_goop_green" );
  88. PrecacheScriptSound( "Halloween.skeleton_break" );
  89. PrecacheScriptSound( "Halloween.skeleton_laugh_small" );
  90. PrecacheScriptSound( "Halloween.skeleton_laugh_medium" );
  91. PrecacheScriptSound( "Halloween.skeleton_laugh_giant" );
  92. }
  93. //-----------------------------------------------------------------------------------------------------
  94. void CZombie::Precache()
  95. {
  96. BaseClass::Precache();
  97. // These are player models which are already precached...
  98. bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
  99. CBaseEntity::SetAllowPrecache( true );
  100. PrecacheZombie();
  101. CBaseEntity::SetAllowPrecache( bAllowPrecache );
  102. }
  103. //-----------------------------------------------------------------------------------------------------
  104. void CZombie::Spawn( void )
  105. {
  106. Precache();
  107. /*int which = RandomInt( TF_CLASS_SCOUT, TF_CLASS_ENGINEER );
  108. const char *name = g_aRawPlayerClassNamesShort[ which ];
  109. if ( FStrEq( name, "spy" ) )
  110. {
  111. m_bSpy = true;
  112. }*/
  113. //SetModel( CFmtStr( "models/player/%s.mdl", name ) );
  114. SetModel( SKELETON_MODEL );
  115. BaseClass::Spawn();
  116. const int health = 50;
  117. SetHealth( health );
  118. SetMaxHealth( health );
  119. AddFlag( FL_NPC );
  120. QAngle qAngle = vec3_angle;
  121. qAngle[YAW] = RandomFloat( 0, 360 );
  122. SetAbsAngles( qAngle );
  123. // Spawn Pos
  124. GetBodyInterface()->StartActivity( ACT_TRANSITION );
  125. //int iSkinIndex = GetTeamNumber() == TF_TEAM_RED ? 0 : 1;
  126. //m_zombieParts = (CBaseAnimating *)CreateEntityByName( "prop_dynamic" );
  127. //if ( m_zombieParts )
  128. //{
  129. // m_zombieParts->SetModel( CFmtStr( "models/player/items/%s/%s_zombie.mdl", name, name ) );
  130. // m_zombieParts->m_nSkin = iSkinIndex;
  131. // // bonemerge into our model
  132. // m_zombieParts->FollowEntity( this, true );
  133. //}
  134. //if ( m_bSpy )
  135. //{
  136. // // Spy has a bunch of extra skins used to adjust the mask
  137. // iSkinIndex += 22;
  138. //}
  139. //else
  140. //{
  141. // // 4: red zombie
  142. // // 5: blue zombie
  143. // // 6: red zombie invuln
  144. // // 7: blue zombie invuln
  145. // iSkinIndex += 4;
  146. //}
  147. switch ( GetTeamNumber() )
  148. {
  149. case TF_TEAM_RED:
  150. m_nSkin = 0;
  151. break;
  152. case TF_TEAM_BLUE:
  153. m_nSkin = 1;
  154. break;
  155. default:
  156. {
  157. m_nSkin = 2;
  158. // make sure I'm on TF_TEAM_HALLOWEEN
  159. ChangeTeam( TF_TEAM_HALLOWEEN );
  160. }
  161. }
  162. // force kill oldest skeletons in the level (except skeleton king) to keep the number of skeletons under the max active
  163. int nForceKill = IZombieAutoList::AutoList().Count() - tf_max_active_zombie.GetInt();
  164. for ( int i=0; i<IZombieAutoList::AutoList().Count() && nForceKill > 0; ++i )
  165. {
  166. CZombie *pZombie = static_cast< CZombie* >( IZombieAutoList::AutoList()[i] );
  167. if ( pZombie->GetSkeletonType() != SKELETON_KING )
  168. {
  169. pZombie->ForceSuicide();
  170. nForceKill--;
  171. }
  172. }
  173. Assert( nForceKill <= 0 );
  174. }
  175. //-----------------------------------------------------------------------------------------------------
  176. int CZombie::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  177. {
  178. if ( info.GetAttacker() && info.GetAttacker()->GetTeamNumber() == GetTeamNumber() )
  179. return 0;
  180. if ( !IsPlayingGesture( ACT_MP_GESTURE_FLINCH_CHEST ) )
  181. {
  182. AddGesture( ACT_MP_GESTURE_FLINCH_CHEST );
  183. }
  184. const char* pszEffectName;
  185. if ( GetTeamNumber() == TF_TEAM_HALLOWEEN )
  186. {
  187. pszEffectName = "spell_skeleton_goop_green";
  188. }
  189. else
  190. {
  191. pszEffectName = GetTeamNumber() == TF_TEAM_RED ? "spell_pumpkin_mirv_goop_red" : "spell_pumpkin_mirv_goop_blue";
  192. }
  193. DispatchParticleEffect( pszEffectName, info.GetDamagePosition(), GetAbsAngles() );
  194. return BaseClass::OnTakeDamage_Alive( info );
  195. }
  196. //-----------------------------------------------------------------------------------------------------
  197. void CZombie::Event_Killed( const CTakeDamageInfo &info )
  198. {
  199. EmitSound( "Halloween.skeleton_break" );
  200. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  201. {
  202. CTFPlayer *pPlayerAttacker = NULL;
  203. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
  204. {
  205. pPlayerAttacker = ToTFPlayer( info.GetAttacker() );
  206. if ( pPlayerAttacker )
  207. {
  208. pPlayerAttacker->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_SKELETON_GRIND );
  209. IGameEvent *pEvent = gameeventmanager->CreateEvent( "halloween_skeleton_killed" );
  210. if ( pEvent )
  211. {
  212. pEvent->SetInt( "player", pPlayerAttacker->GetUserID() );
  213. gameeventmanager->FireEvent( pEvent, true );
  214. }
  215. }
  216. }
  217. }
  218. BaseClass::Event_Killed( info );
  219. }
  220. //-----------------------------------------------------------------------------------------------------
  221. void CZombie::UpdateOnRemove()
  222. {
  223. CPVSFilter filter( GetAbsOrigin() );
  224. UserMessageBegin( filter, "BreakModel" );
  225. WRITE_SHORT( GetModelIndex() );
  226. WRITE_VEC3COORD( GetAbsOrigin() );
  227. WRITE_ANGLES( GetAbsAngles() );
  228. WRITE_SHORT( m_nSkin );
  229. MessageEnd();
  230. UTIL_Remove( m_hHat );
  231. BaseClass::UpdateOnRemove();
  232. }
  233. //---------------------------------------------------------------------------------------------
  234. /*static*/ CZombie* CZombie::SpawnAtPos( const Vector& vSpawnPos, float flLifeTime /*= 0.f*/, int nTeam /*= TF_TEAM_HALLOWEEN*/, CBaseEntity *pOwner /*= NULL*/, SkeletonType_t nSkeletonType /*= SKELETON_NORMAL*/ )
  235. {
  236. CZombie *pZombie = (CZombie *)CreateEntityByName( "tf_zombie" );
  237. if ( pZombie )
  238. {
  239. pZombie->ChangeTeam( nTeam );
  240. DispatchSpawn( pZombie );
  241. pZombie->SetAbsOrigin( vSpawnPos );
  242. pZombie->SetOwnerEntity( pOwner );
  243. if ( flLifeTime > 0.f )
  244. {
  245. pZombie->StartLifeTimer( flLifeTime );
  246. }
  247. pZombie->SetSkeletonType( nSkeletonType );
  248. }
  249. return pZombie;
  250. }
  251. bool CZombie::ShouldSuicide() const
  252. {
  253. // out of life time
  254. if ( m_lifeTimer.HasStarted() && m_lifeTimer.IsElapsed() )
  255. return true;
  256. // owner changed team
  257. if ( GetOwnerEntity() && GetOwnerEntity()->GetTeamNumber() != GetTeamNumber() )
  258. return true;
  259. return m_bForceSuicide;
  260. }
  261. //-----------------------------------------------------------------------------------------------------
  262. void CZombie::SetSkeletonType( SkeletonType_t nType )
  263. {
  264. m_nType = nType;
  265. // Skeleton King?
  266. if ( nType == SKELETON_KING )
  267. {
  268. SetModel( SKELETON_KING_MODEL );
  269. SetModelScale( 2.f );
  270. const int health = 1000;
  271. SetHealth( health );
  272. SetMaxHealth( health );
  273. m_flAttackRange = 100.f;
  274. m_flAttackDamage = 100.f;
  275. AddHat( SKELETON_KING_CROWN_MODEL );
  276. }
  277. else if ( nType == SKELETON_MINI )
  278. {
  279. SetModel( SKELETON_MODEL );
  280. SetModelScale( 0.5f );
  281. m_flHeadScale = 3.f;
  282. if( TFGameRules()->GetHalloweenScenario() == CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY )
  283. {
  284. int iModelIndex = RandomInt( 0, ARRAYSIZE( s_skeletonHatModels ) - 1 );
  285. #ifdef STAGING_ONLY
  286. iModelIndex = tf_halloween_skeleton_test_hat.GetInt() > 0 ? tf_halloween_skeleton_test_hat.GetInt() : iModelIndex;
  287. #endif // STAGING_ONLY
  288. const char *pszHat = s_skeletonHatModels[ iModelIndex ];
  289. AddHat( pszHat );
  290. }
  291. m_flAttackRange = 40.f;
  292. m_flAttackDamage = 20.f;
  293. }
  294. }
  295. //-----------------------------------------------------------------------------------------------------
  296. void CZombie::AddHat( const char *pszModel )
  297. {
  298. if ( !m_hHat )
  299. {
  300. int iHead = LookupBone( "bip_head" );
  301. Assert( iHead != -1 );
  302. if ( iHead != -1 )
  303. {
  304. m_hHat = (CBaseAnimating *)CreateEntityByName( "prop_dynamic" );
  305. if ( m_hHat )
  306. {
  307. m_hHat->SetModel( pszModel );
  308. Vector pos;
  309. QAngle angles;
  310. GetBonePosition( iHead, pos, angles );
  311. m_hHat->SetAbsOrigin( pos );
  312. m_hHat->SetAbsAngles( angles );
  313. m_hHat->FollowEntity( this, true );
  314. }
  315. }
  316. }
  317. }
  318. //---------------------------------------------------------------------------------------------
  319. //---------------------------------------------------------------------------------------------
  320. class CZombieBehavior : public Action< CZombie >
  321. {
  322. public:
  323. virtual Action< CZombie > *InitialContainedAction( CZombie *me )
  324. {
  325. return new CZombieSpawn;
  326. }
  327. virtual ActionResult< CZombie > OnStart( CZombie *me, Action< CZombie > *priorAction )
  328. {
  329. return Continue();
  330. }
  331. virtual ActionResult< CZombie > Update( CZombie *me, float interval )
  332. {
  333. if ( !me->IsAlive() || me->ShouldSuicide() )
  334. {
  335. UTIL_Remove( me );
  336. return Done();
  337. }
  338. if ( ShouldLaugh( me ) )
  339. {
  340. Laugh( me );
  341. }
  342. return Continue();
  343. }
  344. virtual EventDesiredResult< CZombie > OnKilled( CZombie *me, const CTakeDamageInfo &info )
  345. {
  346. // bonemerged models don't ragdoll
  347. //UTIL_Remove( me->m_zombieParts );
  348. if ( info.GetAttacker() && dynamic_cast< CBaseCombatCharacter* >( info.GetAttacker() ) )
  349. {
  350. if ( me->GetSkeletonType() == CZombie::SKELETON_NORMAL )
  351. {
  352. // normal skeleton spawns 3 mini skeletons
  353. CBaseCombatCharacter* pOwner = dynamic_cast< CBaseCombatCharacter* >( me->GetOwnerEntity() );
  354. pOwner = pOwner ? pOwner : me;
  355. for ( int i=0; i<3; ++i )
  356. {
  357. CreateSpellSpawnZombie( pOwner, me->GetAbsOrigin(), 2 );
  358. }
  359. }
  360. else if ( me->GetSkeletonType() == CZombie::SKELETON_KING )
  361. {
  362. // skeleton king drops rare spell
  363. TFGameRules()->DropSpellPickup( me->GetAbsOrigin(), 1 );
  364. }
  365. }
  366. UTIL_Remove( me );
  367. return TryDone();
  368. }
  369. virtual const char *GetName( void ) const { return "ZombieBehavior"; } // return name of this action
  370. private:
  371. bool ShouldLaugh( CZombie *me )
  372. {
  373. if ( !m_laughTimer.HasStarted() )
  374. {
  375. switch ( me->GetSkeletonType() )
  376. {
  377. case CZombie::SKELETON_KING:
  378. {
  379. m_laughTimer.Start( RandomFloat( 6.f, 7.f ) );
  380. break;
  381. }
  382. case CZombie::SKELETON_MINI:
  383. {
  384. m_laughTimer.Start( RandomFloat( 2.f, 3.f ) );
  385. break;
  386. }
  387. default:
  388. {
  389. m_laughTimer.Start( RandomFloat( 4.f, 5.f ) );
  390. }
  391. }
  392. return false;
  393. }
  394. if ( m_laughTimer.HasStarted() && m_laughTimer.IsElapsed() )
  395. {
  396. m_laughTimer.Invalidate();
  397. return true;
  398. }
  399. return false;
  400. }
  401. void Laugh( CZombie *me )
  402. {
  403. const char *pszSoundName;
  404. switch ( me->GetSkeletonType() )
  405. {
  406. case CZombie::SKELETON_KING:
  407. {
  408. pszSoundName = "Halloween.skeleton_laugh_giant";
  409. break;
  410. }
  411. case CZombie::SKELETON_MINI:
  412. {
  413. pszSoundName = "Halloween.skeleton_laugh_small";
  414. break;
  415. }
  416. default:
  417. {
  418. pszSoundName = "Halloween.skeleton_laugh_medium";
  419. }
  420. }
  421. me->EmitSound( pszSoundName );
  422. }
  423. CountdownTimer m_laughTimer;
  424. };
  425. //---------------------------------------------------------------------------------------------
  426. //---------------------------------------------------------------------------------------------
  427. CZombieIntention::CZombieIntention( CZombie *me ) : IIntention( me )
  428. {
  429. m_behavior = new Behavior< CZombie >( new CZombieBehavior );
  430. }
  431. CZombieIntention::~CZombieIntention()
  432. {
  433. delete m_behavior;
  434. }
  435. void CZombieIntention::Reset( void )
  436. {
  437. delete m_behavior;
  438. m_behavior = new Behavior< CZombie >( new CZombieBehavior );
  439. }
  440. void CZombieIntention::Update( void )
  441. {
  442. m_behavior->Update( static_cast< CZombie * >( GetBot() ), GetUpdateInterval() );
  443. }
  444. // is this a place we can be?
  445. QueryResultType CZombieIntention::IsPositionAllowed( const INextBot *meBot, const Vector &pos ) const
  446. {
  447. return ANSWER_YES;
  448. }
  449. //---------------------------------------------------------------------------------------------
  450. //---------------------------------------------------------------------------------------------
  451. float CZombieLocomotion::GetRunSpeed( void ) const
  452. {
  453. return 300.f;
  454. }
  455. //---------------------------------------------------------------------------------------------
  456. // if delta Z is greater than this, we have to jump to get up
  457. float CZombieLocomotion::GetStepHeight( void ) const
  458. {
  459. return 18.0f;
  460. }
  461. //---------------------------------------------------------------------------------------------
  462. // return maximum height of a jump
  463. float CZombieLocomotion::GetMaxJumpHeight( void ) const
  464. {
  465. return 18.0f;
  466. }
  467. //---------------------------------------------------------------------------------------------
  468. // Return max rate of yaw rotation
  469. float CZombieLocomotion::GetMaxYawRate( void ) const
  470. {
  471. return 200.0f;
  472. }
  473. //---------------------------------------------------------------------------------------------
  474. bool CZombieLocomotion::ShouldCollideWith( const CBaseEntity *object ) const
  475. {
  476. return false;
  477. }