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.

518 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The Halflife Cycler NPCs
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_basenpc.h"
  8. #include "ai_motor.h"
  9. #include "basecombatweapon.h"
  10. #include "animation.h"
  11. #include "vstdlib/random.h"
  12. #include "h_cycler.h"
  13. #include "Sprite.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. #define FCYCLER_NOTSOLID 0x0001
  17. extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
  18. BEGIN_DATADESC( CCycler )
  19. // Fields
  20. DEFINE_FIELD( m_animate, FIELD_INTEGER ),
  21. // Inputs
  22. DEFINE_INPUTFUNC( FIELD_STRING, "SetSequence", InputSetSequence ),
  23. END_DATADESC()
  24. //
  25. // we should get rid of all the other cyclers and replace them with this.
  26. //
  27. class CGenericCycler : public CCycler
  28. {
  29. public:
  30. DECLARE_CLASS( CGenericCycler, CCycler );
  31. void Spawn()
  32. {
  33. GenericCyclerSpawn( (char *)STRING( GetModelName() ), Vector(-16, -16, 0), Vector(16, 16, 72) );
  34. }
  35. };
  36. LINK_ENTITY_TO_CLASS( cycler, CGenericCycler );
  37. LINK_ENTITY_TO_CLASS( model_studio, CGenericCycler ); // For now model_studios build as cyclers.
  38. // Cycler member functions
  39. void CCycler::GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax)
  40. {
  41. if (!szModel || !*szModel)
  42. {
  43. Warning( "cycler at %.0f %.0f %0.f missing modelname\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  44. UTIL_Remove( this );
  45. return;
  46. }
  47. Precache();
  48. SetModel( szModel );
  49. m_bloodColor = DONT_BLEED;
  50. CCycler::Spawn( );
  51. UTIL_SetSize(this, vecMin, vecMax);
  52. }
  53. void CCycler::Precache()
  54. {
  55. PrecacheModel( (const char *)STRING( GetModelName() ) );
  56. }
  57. void CCycler::Spawn( )
  58. {
  59. InitBoneControllers();
  60. SetSolid( SOLID_BBOX );
  61. if ( m_spawnflags & FCYCLER_NOTSOLID )
  62. {
  63. AddSolidFlags( FSOLID_NOT_SOLID );
  64. }
  65. else
  66. {
  67. AddSolidFlags( FSOLID_NOT_STANDABLE );
  68. }
  69. SetMoveType( MOVETYPE_NONE );
  70. m_takedamage = DAMAGE_YES;
  71. m_iHealth = 80000;// no cycler should die
  72. GetMotor()->SetIdealYaw( GetLocalAngles().y );
  73. GetMotor()->SnapYaw();
  74. m_flPlaybackRate = 1.0;
  75. m_flGroundSpeed = 0;
  76. SetNextThink( gpGlobals->curtime + 1.0f );
  77. ResetSequenceInfo( );
  78. if (GetSequence() != 0 || m_flCycle != 0)
  79. {
  80. #ifdef TF2_DLL
  81. m_animate = 1;
  82. #else
  83. m_animate = 0;
  84. m_flPlaybackRate = 0;
  85. #endif
  86. }
  87. else
  88. {
  89. m_animate = 1;
  90. }
  91. }
  92. //
  93. // cycler think
  94. //
  95. void CCycler::Think( void )
  96. {
  97. SetNextThink( gpGlobals->curtime + 0.1f );
  98. if (m_animate)
  99. {
  100. StudioFrameAdvance ( );
  101. DispatchAnimEvents( this );
  102. }
  103. if (IsSequenceFinished() && !SequenceLoops())
  104. {
  105. // ResetSequenceInfo();
  106. // hack to avoid reloading model every frame
  107. m_flAnimTime = gpGlobals->curtime;
  108. m_flPlaybackRate = 1.0;
  109. m_bSequenceFinished = false;
  110. m_flLastEventCheck = 0;
  111. m_flCycle = 0;
  112. if (!m_animate)
  113. m_flPlaybackRate = 0.0; // FIX: don't reset framerate
  114. }
  115. }
  116. //
  117. // CyclerUse - starts a rotation trend
  118. //
  119. void CCycler::Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  120. {
  121. m_animate = !m_animate;
  122. if (m_animate)
  123. m_flPlaybackRate = 1.0;
  124. else
  125. m_flPlaybackRate = 0.0;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose: Changes sequences when hurt.
  129. //-----------------------------------------------------------------------------
  130. int CCycler::OnTakeDamage( const CTakeDamageInfo &info )
  131. {
  132. if (m_animate)
  133. {
  134. int nSequence = GetSequence() + 1;
  135. if ( !IsValidSequence(nSequence) )
  136. {
  137. nSequence = 0;
  138. }
  139. ResetSequence( nSequence );
  140. m_flCycle = 0;
  141. }
  142. else
  143. {
  144. m_flPlaybackRate = 1.0;
  145. StudioFrameAdvance ();
  146. m_flPlaybackRate = 0;
  147. Msg( "sequence: %d, frame %.0f\n", GetSequence(), m_flCycle.Get() );
  148. }
  149. return 0;
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose: Input that sets the sequence of the cycler
  153. //-----------------------------------------------------------------------------
  154. void CCycler::InputSetSequence( inputdata_t &inputdata )
  155. {
  156. if (m_animate)
  157. {
  158. // Legacy support: Try it as a number, and support '0'
  159. const char *sChar = inputdata.value.String();
  160. int iSeqNum = atoi( sChar );
  161. if ( !iSeqNum && sChar[0] != '0' )
  162. {
  163. // Treat it as a sequence name
  164. ResetSequence( LookupSequence( sChar ) );
  165. }
  166. else
  167. {
  168. ResetSequence( iSeqNum );
  169. }
  170. if (m_flPlaybackRate == 0.0)
  171. {
  172. ResetSequence( 0 );
  173. }
  174. m_flCycle = 0;
  175. }
  176. }
  177. // FIXME: this doesn't work anymore, and hasn't for a while now.
  178. class CWeaponCycler : public CBaseCombatWeapon
  179. {
  180. DECLARE_DATADESC();
  181. public:
  182. DECLARE_CLASS( CWeaponCycler, CBaseCombatWeapon );
  183. DECLARE_SERVERCLASS();
  184. void Spawn( void );
  185. void PrimaryAttack( void );
  186. void SecondaryAttack( void );
  187. bool Deploy( void );
  188. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  189. string_t m_iszModel;
  190. int m_iModel;
  191. };
  192. IMPLEMENT_SERVERCLASS_ST(CWeaponCycler, DT_WeaponCycler)
  193. END_SEND_TABLE()
  194. LINK_ENTITY_TO_CLASS( cycler_weapon, CWeaponCycler );
  195. //---------------------------------------------------------
  196. // Save/Restore
  197. //---------------------------------------------------------
  198. BEGIN_DATADESC( CWeaponCycler )
  199. DEFINE_FIELD( m_iszModel, FIELD_STRING ),
  200. DEFINE_FIELD( m_iModel, FIELD_INTEGER ),
  201. END_DATADESC()
  202. void CWeaponCycler::Spawn( )
  203. {
  204. SetSolid( SOLID_BBOX );
  205. AddSolidFlags( FSOLID_NOT_STANDABLE );
  206. SetMoveType( MOVETYPE_NONE );
  207. PrecacheModel( STRING( GetModelName() ) );
  208. SetModel( STRING( GetModelName() ) );
  209. m_iszModel = GetModelName();
  210. m_iModel = GetModelIndex();
  211. UTIL_SetSize(this, Vector(-16, -16, 0), Vector(16, 16, 16));
  212. SetTouch( &CWeaponCycler::DefaultTouch );
  213. }
  214. bool CWeaponCycler::Deploy( )
  215. {
  216. CBaseCombatCharacter *pOwner = GetOwner();
  217. if (pOwner)
  218. {
  219. pOwner->m_flNextAttack = gpGlobals->curtime + 1.0;
  220. SendWeaponAnim( 0 );
  221. m_iClip1 = 0;
  222. m_iClip2 = 0;
  223. return true;
  224. }
  225. return false;
  226. }
  227. bool CWeaponCycler::Holster( CBaseCombatWeapon *pSwitchingTo )
  228. {
  229. CBaseCombatCharacter *pOwner = GetOwner();
  230. if (pOwner)
  231. {
  232. pOwner->m_flNextAttack = gpGlobals->curtime + 0.5;
  233. }
  234. return true;
  235. }
  236. void CWeaponCycler::PrimaryAttack()
  237. {
  238. SendWeaponAnim( GetSequence() );
  239. m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
  240. }
  241. void CWeaponCycler::SecondaryAttack( void )
  242. {
  243. float flFrameRate;
  244. int nSequence = (GetSequence() + 1) % 8;
  245. // BUG: Why do we set this here and then set to zero right after?
  246. SetModelIndex( m_iModel );
  247. flFrameRate = 0.0;
  248. SetModelIndex( 0 );
  249. if (flFrameRate == 0.0)
  250. {
  251. nSequence = 0;
  252. }
  253. SetSequence( nSequence );
  254. SendWeaponAnim( nSequence );
  255. m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
  256. }
  257. // Flaming Wreakage
  258. class CWreckage : public CAI_BaseNPC
  259. {
  260. public:
  261. DECLARE_CLASS( CWreckage, CAI_BaseNPC );
  262. DECLARE_DATADESC();
  263. void Spawn( void );
  264. void Precache( void );
  265. void Think( void );
  266. float m_flStartTime;
  267. float m_flDieTime;
  268. };
  269. BEGIN_DATADESC( CWreckage )
  270. DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
  271. DEFINE_FIELD( m_flDieTime, FIELD_TIME ),
  272. END_DATADESC()
  273. LINK_ENTITY_TO_CLASS( cycler_wreckage, CWreckage );
  274. void CWreckage::Spawn( void )
  275. {
  276. SetSolid( SOLID_NONE );
  277. SetMoveType( MOVETYPE_NONE );
  278. m_takedamage = 0;
  279. SetCycle( 0 );
  280. SetNextThink( gpGlobals->curtime + 0.1f );
  281. if (GetModelName() != NULL_STRING)
  282. {
  283. PrecacheModel( STRING( GetModelName() ) );
  284. SetModel( STRING( GetModelName() ) );
  285. }
  286. m_flStartTime = gpGlobals->curtime;
  287. }
  288. void CWreckage::Precache( )
  289. {
  290. if ( GetModelName() != NULL_STRING )
  291. PrecacheModel( STRING( GetModelName() ) );
  292. }
  293. void CWreckage::Think( void )
  294. {
  295. StudioFrameAdvance( );
  296. SetNextThink( gpGlobals->curtime + 0.2 );
  297. if (m_flDieTime)
  298. {
  299. if (m_flDieTime < gpGlobals->curtime)
  300. {
  301. UTIL_Remove( this );
  302. return;
  303. }
  304. else if (random->RandomFloat( 0, m_flDieTime - m_flStartTime ) > m_flDieTime - gpGlobals->curtime)
  305. {
  306. return;
  307. }
  308. }
  309. Vector vecSrc;
  310. CollisionProp()->RandomPointInBounds( vec3_origin, Vector(1, 1, 1), &vecSrc );
  311. CPVSFilter filter( vecSrc );
  312. te->Smoke( filter, 0.0,
  313. &vecSrc, g_sModelIndexSmoke,
  314. random->RandomFloat(0,4.9) + 5.0,
  315. random->RandomInt(0, 3) + 8 );
  316. }
  317. // BlendingCycler
  318. // Used to demonstrate animation blending
  319. class CBlendingCycler : public CCycler
  320. {
  321. DECLARE_DATADESC();
  322. public:
  323. DECLARE_CLASS( CBlendingCycler, CCycler );
  324. void Spawn( void );
  325. bool KeyValue( const char *szKeyName, const char *szValue );
  326. void Think( void );
  327. virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_DONT_SAVE | FCAP_IMPULSE_USE); }
  328. int m_iLowerBound;
  329. int m_iUpperBound;
  330. int m_iCurrent;
  331. int m_iBlendspeed;
  332. string_t m_iszSequence;
  333. };
  334. LINK_ENTITY_TO_CLASS( cycler_blender, CBlendingCycler );
  335. //---------------------------------------------------------
  336. // Save/Restore
  337. //---------------------------------------------------------
  338. BEGIN_DATADESC( CBlendingCycler )
  339. DEFINE_FIELD( m_iLowerBound, FIELD_INTEGER ),
  340. DEFINE_FIELD( m_iUpperBound, FIELD_INTEGER ),
  341. DEFINE_FIELD( m_iCurrent, FIELD_INTEGER ),
  342. DEFINE_FIELD( m_iBlendspeed, FIELD_INTEGER ),
  343. DEFINE_FIELD( m_iszSequence, FIELD_STRING ),
  344. END_DATADESC()
  345. void CBlendingCycler::Spawn( void )
  346. {
  347. // Remove if it's not blending
  348. if (m_iLowerBound == 0 && m_iUpperBound == 0)
  349. {
  350. UTIL_Remove( this );
  351. return;
  352. }
  353. GenericCyclerSpawn( (char *)STRING( GetModelName() ), Vector(-16,-16,-16), Vector(16,16,16));
  354. if (!m_iBlendspeed)
  355. m_iBlendspeed = 5;
  356. // Initialise Sequence
  357. if (m_iszSequence != NULL_STRING)
  358. {
  359. SetSequence( LookupSequence( STRING(m_iszSequence) ) );
  360. }
  361. m_iCurrent = m_iLowerBound;
  362. }
  363. bool CBlendingCycler::KeyValue( const char *szKeyName, const char *szValue )
  364. {
  365. if (FStrEq(szKeyName, "lowboundary"))
  366. {
  367. m_iLowerBound = atoi(szValue);
  368. }
  369. else if (FStrEq(szKeyName, "highboundary"))
  370. {
  371. m_iUpperBound = atoi(szValue);
  372. }
  373. else if (FStrEq(szKeyName, "blendspeed"))
  374. {
  375. m_iBlendspeed = atoi(szValue);
  376. }
  377. else if (FStrEq(szKeyName, "blendsequence"))
  378. {
  379. m_iszSequence = AllocPooledString(szValue);
  380. }
  381. else
  382. return BaseClass::KeyValue( szKeyName, szValue );
  383. return true;
  384. }
  385. // Blending Cycler think
  386. void CBlendingCycler::Think( void )
  387. {
  388. SetNextThink( gpGlobals->curtime + 0.1f );
  389. // Move
  390. m_iCurrent += m_iBlendspeed;
  391. if ( (m_iCurrent > m_iUpperBound) || (m_iCurrent < m_iLowerBound) )
  392. m_iBlendspeed = m_iBlendspeed * -1;
  393. // Set blend
  394. SetPoseParameter( 0, m_iCurrent );
  395. Msg( "Current Blend: %d\n", m_iCurrent );
  396. if (IsSequenceFinished() && !SequenceLoops())
  397. {
  398. // ResetSequenceInfo();
  399. // hack to avoid reloading model every frame
  400. m_flAnimTime = gpGlobals->curtime;
  401. m_flPlaybackRate = 1.0;
  402. m_bSequenceFinished = false;
  403. m_flLastEventCheck = 0;
  404. m_flCycle = 0;
  405. if (!m_animate)
  406. {
  407. m_flPlaybackRate = 0.0; // FIX: don't reset framerate
  408. }
  409. }
  410. }