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.

576 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Handling for the base world item. Most of this was moved from items.cpp.
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "player.h"
  9. #include "items.h"
  10. #include "gamerules.h"
  11. #include "engine/IEngineSound.h"
  12. #include "iservervehicle.h"
  13. #include "physics_saverestore.h"
  14. #include "world.h"
  15. #ifdef HL2MP
  16. #include "hl2mp_gamerules.h"
  17. #endif
  18. #ifdef TF_DLL
  19. #include "tf_player.h"
  20. #include "entity_healthkit.h"
  21. #include "particle_parse.h"
  22. #endif
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #define ITEM_PICKUP_BOX_BLOAT 24
  26. class CWorldItem : public CBaseAnimating
  27. {
  28. DECLARE_DATADESC();
  29. public:
  30. DECLARE_CLASS( CWorldItem, CBaseAnimating );
  31. bool KeyValue( const char *szKeyName, const char *szValue );
  32. void Spawn( void );
  33. int m_iType;
  34. };
  35. LINK_ENTITY_TO_CLASS(world_items, CWorldItem);
  36. BEGIN_DATADESC( CWorldItem )
  37. DEFINE_FIELD( m_iType, FIELD_INTEGER ),
  38. END_DATADESC()
  39. bool CWorldItem::KeyValue( const char *szKeyName, const char *szValue )
  40. {
  41. if (FStrEq(szKeyName, "type"))
  42. {
  43. m_iType = atoi(szValue);
  44. }
  45. else
  46. return BaseClass::KeyValue( szKeyName, szValue );
  47. return true;
  48. }
  49. void CWorldItem::Spawn( void )
  50. {
  51. CBaseEntity *pEntity = NULL;
  52. switch (m_iType)
  53. {
  54. case 44: // ITEM_BATTERY:
  55. pEntity = CBaseEntity::Create( "item_battery", GetLocalOrigin(), GetLocalAngles() );
  56. break;
  57. case 45: // ITEM_SUIT:
  58. pEntity = CBaseEntity::Create( "item_suit", GetLocalOrigin(), GetLocalAngles() );
  59. break;
  60. }
  61. if (!pEntity)
  62. {
  63. Warning("unable to create world_item %d\n", m_iType );
  64. }
  65. else
  66. {
  67. pEntity->m_target = m_target;
  68. pEntity->SetName( GetEntityName() );
  69. pEntity->ClearSpawnFlags();
  70. pEntity->AddSpawnFlags( m_spawnflags );
  71. }
  72. UTIL_RemoveImmediate( this );
  73. }
  74. BEGIN_DATADESC( CItem )
  75. DEFINE_FIELD( m_bActivateWhenAtRest, FIELD_BOOLEAN ),
  76. DEFINE_FIELD( m_vOriginalSpawnOrigin, FIELD_POSITION_VECTOR ),
  77. DEFINE_FIELD( m_vOriginalSpawnAngles, FIELD_VECTOR ),
  78. DEFINE_PHYSPTR( m_pConstraint ),
  79. // Function Pointers
  80. DEFINE_ENTITYFUNC( ItemTouch ),
  81. DEFINE_THINKFUNC( Materialize ),
  82. DEFINE_THINKFUNC( ComeToRest ),
  83. #if defined( HL2MP ) || defined( TF_DLL )
  84. DEFINE_FIELD( m_flNextResetCheckTime, FIELD_TIME ),
  85. DEFINE_THINKFUNC( FallThink ),
  86. #endif
  87. // Outputs
  88. DEFINE_OUTPUT( m_OnPlayerTouch, "OnPlayerTouch" ),
  89. DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ),
  90. END_DATADESC()
  91. //-----------------------------------------------------------------------------
  92. // Constructor
  93. //-----------------------------------------------------------------------------
  94. CItem::CItem()
  95. {
  96. m_bActivateWhenAtRest = false;
  97. }
  98. bool CItem::CreateItemVPhysicsObject( void )
  99. {
  100. // Create the object in the physics system
  101. int nSolidFlags = GetSolidFlags() | FSOLID_NOT_STANDABLE;
  102. if ( !m_bActivateWhenAtRest )
  103. {
  104. nSolidFlags |= FSOLID_TRIGGER;
  105. }
  106. if ( VPhysicsInitNormal( SOLID_VPHYSICS, nSolidFlags, false ) == NULL )
  107. {
  108. SetSolid( SOLID_BBOX );
  109. AddSolidFlags( nSolidFlags );
  110. // If it's not physical, drop it to the floor
  111. if (UTIL_DropToFloor(this, MASK_SOLID) == 0)
  112. {
  113. Warning( "Item %s fell out of level at %f,%f,%f\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
  114. UTIL_Remove( this );
  115. return false;
  116. }
  117. }
  118. return true;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. void CItem::Spawn( void )
  124. {
  125. if ( g_pGameRules->IsAllowedToSpawn( this ) == false )
  126. {
  127. UTIL_Remove( this );
  128. return;
  129. }
  130. SetMoveType( MOVETYPE_FLYGRAVITY );
  131. SetSolid( SOLID_BBOX );
  132. SetBlocksLOS( false );
  133. AddEFlags( EFL_NO_ROTORWASH_PUSH );
  134. if( IsX360() )
  135. {
  136. AddEffects( EF_ITEM_BLINK );
  137. }
  138. // This will make them not collide with the player, but will collide
  139. // against other items + weapons
  140. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  141. CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT );
  142. SetTouch(&CItem::ItemTouch);
  143. if ( CreateItemVPhysicsObject() == false )
  144. return;
  145. m_takedamage = DAMAGE_EVENTS_ONLY;
  146. #if !defined( CLIENT_DLL )
  147. // Constrained start?
  148. if ( HasSpawnFlags( SF_ITEM_START_CONSTRAINED ) )
  149. {
  150. //Constrain the weapon in place
  151. IPhysicsObject *pReferenceObject, *pAttachedObject;
  152. pReferenceObject = g_PhysWorldObject;
  153. pAttachedObject = VPhysicsGetObject();
  154. if ( pReferenceObject && pAttachedObject )
  155. {
  156. constraint_fixedparams_t fixed;
  157. fixed.Defaults();
  158. fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject );
  159. fixed.constraint.forceLimit = lbs2kg( 10000 );
  160. fixed.constraint.torqueLimit = lbs2kg( 10000 );
  161. m_pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed );
  162. m_pConstraint->SetGameData( (void *) this );
  163. }
  164. }
  165. #endif //CLIENT_DLL
  166. #if defined( HL2MP ) || defined( TF_DLL )
  167. SetThink( &CItem::FallThink );
  168. SetNextThink( gpGlobals->curtime + 0.1f );
  169. #endif
  170. }
  171. unsigned int CItem::PhysicsSolidMaskForEntity( void ) const
  172. {
  173. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
  174. }
  175. void CItem::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  176. {
  177. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  178. if ( pPlayer )
  179. {
  180. pPlayer->PickupObject( this );
  181. }
  182. }
  183. extern int gEvilImpulse101;
  184. //-----------------------------------------------------------------------------
  185. // Activate when at rest, but don't allow pickup until then
  186. //-----------------------------------------------------------------------------
  187. void CItem::ActivateWhenAtRest( float flTime /* = 0.5f */ )
  188. {
  189. RemoveSolidFlags( FSOLID_TRIGGER );
  190. m_bActivateWhenAtRest = true;
  191. SetThink( &CItem::ComeToRest );
  192. SetNextThink( gpGlobals->curtime + flTime );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Become touchable when we are at rest
  196. //-----------------------------------------------------------------------------
  197. void CItem::OnEntityEvent( EntityEvent_t event, void *pEventData )
  198. {
  199. BaseClass::OnEntityEvent( event, pEventData );
  200. switch( event )
  201. {
  202. case ENTITY_EVENT_WATER_TOUCH:
  203. {
  204. // Delay rest for a sec, to avoid changing collision
  205. // properties inside a collision callback.
  206. SetThink( &CItem::ComeToRest );
  207. SetNextThink( gpGlobals->curtime + 0.1f );
  208. }
  209. break;
  210. }
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Become touchable when we are at rest
  214. //-----------------------------------------------------------------------------
  215. void CItem::ComeToRest( void )
  216. {
  217. if ( m_bActivateWhenAtRest )
  218. {
  219. m_bActivateWhenAtRest = false;
  220. AddSolidFlags( FSOLID_TRIGGER );
  221. SetThink( NULL );
  222. }
  223. }
  224. #if defined( HL2MP ) || defined( TF_DLL )
  225. //-----------------------------------------------------------------------------
  226. // Purpose: Items that have just spawned run this think to catch them when
  227. // they hit the ground. Once we're sure that the object is grounded,
  228. // we change its solid type to trigger and set it in a large box that
  229. // helps the player get it.
  230. //-----------------------------------------------------------------------------
  231. void CItem::FallThink ( void )
  232. {
  233. SetNextThink( gpGlobals->curtime + 0.1f );
  234. #if defined( HL2MP )
  235. bool shouldMaterialize = false;
  236. IPhysicsObject *pPhysics = VPhysicsGetObject();
  237. if ( pPhysics )
  238. {
  239. shouldMaterialize = pPhysics->IsAsleep();
  240. }
  241. else
  242. {
  243. shouldMaterialize = (GetFlags() & FL_ONGROUND) ? true : false;
  244. }
  245. if ( shouldMaterialize )
  246. {
  247. SetThink ( NULL );
  248. m_vOriginalSpawnOrigin = GetAbsOrigin();
  249. m_vOriginalSpawnAngles = GetAbsAngles();
  250. HL2MPRules()->AddLevelDesignerPlacedObject( this );
  251. }
  252. #endif // HL2MP
  253. #if defined( TF_DLL )
  254. // We only come here if ActivateWhenAtRest() is never called,
  255. // which is the case when creating currencypacks in MvM
  256. if ( !( GetFlags() & FL_ONGROUND ) )
  257. {
  258. if ( !GetAbsVelocity().Length() && GetMoveType() == MOVETYPE_FLYGRAVITY )
  259. {
  260. // Mr. Game, meet Mr. Hammer. Mr. Hammer, meet the uncooperative Mr. Physics.
  261. // Mr. Physics really doesn't want to give our friend the FL_ONGROUND flag.
  262. // This means our wonderfully helpful radius currency collection code will be sad.
  263. // So in the name of justice, we ask that this flag be delivered unto him.
  264. SetMoveType( MOVETYPE_NONE );
  265. SetGroundEntity( GetWorldEntity() );
  266. }
  267. }
  268. else
  269. {
  270. SetThink( &CItem::ComeToRest );
  271. }
  272. #endif // TF
  273. }
  274. #endif // HL2MP, TF
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Used to tell whether an item may be picked up by the player. This
  277. // accounts for solid obstructions being in the way.
  278. // Input : *pItem - item in question
  279. // *pPlayer - player attempting the pickup
  280. // Output : Returns true on success, false on failure.
  281. //-----------------------------------------------------------------------------
  282. bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer )
  283. {
  284. if ( pItem == NULL || pPlayer == NULL )
  285. return false;
  286. // For now, always allow a vehicle riding player to pick up things they're driving over
  287. if ( pPlayer->IsInAVehicle() )
  288. return true;
  289. // Get our test positions
  290. Vector vecStartPos;
  291. IPhysicsObject *pPhysObj = pItem->VPhysicsGetObject();
  292. if ( pPhysObj != NULL )
  293. {
  294. // Use the physics hull's center
  295. QAngle vecAngles;
  296. pPhysObj->GetPosition( &vecStartPos, &vecAngles );
  297. }
  298. else
  299. {
  300. // Use the generic bbox center
  301. vecStartPos = pItem->CollisionProp()->WorldSpaceCenter();
  302. }
  303. #ifdef TF_DLL
  304. //Plague powerup carrier collects health kits in a radius so we want to skip the occlusion trace
  305. CTFPlayer *pTFPlayer = dynamic_cast<CTFPlayer*>( pPlayer );
  306. if ( pTFPlayer && ( pTFPlayer->m_Shared.GetCarryingRuneType() == RUNE_PLAGUE ) )
  307. {
  308. CHealthKit *pHealthKit = dynamic_cast<CHealthKit*>( pItem );
  309. if ( pHealthKit )
  310. return true;
  311. }
  312. #endif
  313. Vector vecEndPos = pPlayer->EyePosition();
  314. // FIXME: This is the simple first try solution towards the problem. We need to take edges and shape more into account
  315. // for this to be fully robust.
  316. // Trace between to see if we're occluded
  317. trace_t tr;
  318. CTraceFilterSkipTwoEntities filter( pPlayer, pItem, COLLISION_GROUP_PLAYER_MOVEMENT );
  319. UTIL_TraceLine( vecStartPos, vecEndPos, MASK_SOLID, &filter, &tr );
  320. // Occluded
  321. // FIXME: For now, we exclude starting in solid because there are cases where this doesn't matter
  322. if ( tr.fraction < 1.0f )
  323. return false;
  324. return true;
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose: Whether or not the item can be touched and picked up by the player, taking
  328. // into account obstructions and other hinderances
  329. // Output : Returns true on success, false on failure.
  330. //-----------------------------------------------------------------------------
  331. bool CItem::ItemCanBeTouchedByPlayer( CBasePlayer *pPlayer )
  332. {
  333. return UTIL_ItemCanBeTouchedByPlayer( this, pPlayer );
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose:
  337. // Input : pOther -
  338. //-----------------------------------------------------------------------------
  339. void CItem::ItemTouch( CBaseEntity *pOther )
  340. {
  341. // Vehicles can touch items + pick them up
  342. if ( pOther->GetServerVehicle() )
  343. {
  344. pOther = pOther->GetServerVehicle()->GetPassenger();
  345. if ( !pOther )
  346. return;
  347. }
  348. // if it's not a player, ignore
  349. if ( !pOther->IsPlayer() )
  350. return;
  351. CBasePlayer *pPlayer = (CBasePlayer *)pOther;
  352. // Must be a valid pickup scenario (no blocking). Though this is a more expensive
  353. // check than some that follow, this has to be first Obecause it's the only one
  354. // that inhibits firing the output OnCacheInteraction.
  355. if ( ItemCanBeTouchedByPlayer( pPlayer ) == false )
  356. return;
  357. m_OnCacheInteraction.FireOutput(pOther, this);
  358. // Can I even pick stuff up?
  359. if ( !pPlayer->IsAllowedToPickupWeapons() )
  360. return;
  361. // ok, a player is touching this item, but can he have it?
  362. if ( !g_pGameRules->CanHaveItem( pPlayer, this ) )
  363. {
  364. // no? Ignore the touch.
  365. return;
  366. }
  367. if ( MyTouch( pPlayer ) )
  368. {
  369. m_OnPlayerTouch.FireOutput(pOther, this);
  370. #if TF_DLL
  371. CHealthKit *pHealthKit = dynamic_cast<CHealthKit*>( this );
  372. if ( pHealthKit )
  373. {
  374. CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
  375. if ( pTFPlayer && ( pTFPlayer->m_Shared.GetCarryingRuneType() == RUNE_PLAGUE ) )
  376. {
  377. DispatchParticleEffect( "plague_healthkit_pickup", GetAbsOrigin(), GetAbsAngles() );
  378. }
  379. }
  380. #endif
  381. SetTouch( NULL );
  382. SetThink( NULL );
  383. // player grabbed the item.
  384. g_pGameRules->PlayerGotItem( pPlayer, this );
  385. if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES )
  386. {
  387. Respawn();
  388. }
  389. else
  390. {
  391. UTIL_Remove( this );
  392. #ifdef HL2MP
  393. HL2MPRules()->RemoveLevelDesignerPlacedObject( this );
  394. #endif
  395. }
  396. }
  397. else if (gEvilImpulse101)
  398. {
  399. UTIL_Remove( this );
  400. }
  401. }
  402. CBaseEntity* CItem::Respawn( void )
  403. {
  404. SetTouch( NULL );
  405. AddEffects( EF_NODRAW );
  406. VPhysicsDestroyObject();
  407. SetMoveType( MOVETYPE_NONE );
  408. SetSolid( SOLID_BBOX );
  409. AddSolidFlags( FSOLID_TRIGGER );
  410. UTIL_SetOrigin( this, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn.
  411. SetAbsAngles( g_pGameRules->VecItemRespawnAngles( this ) );// set the angles.
  412. #if !defined( TF_DLL )
  413. UTIL_DropToFloor( this, MASK_SOLID );
  414. #endif
  415. RemoveAllDecals(); //remove any decals
  416. SetThink ( &CItem::Materialize );
  417. SetNextThink( gpGlobals->curtime + g_pGameRules->FlItemRespawnTime( this ) );
  418. return this;
  419. }
  420. void CItem::Materialize( void )
  421. {
  422. CreateItemVPhysicsObject();
  423. if ( IsEffectActive( EF_NODRAW ) )
  424. {
  425. // changing from invisible state to visible.
  426. #ifdef HL2MP
  427. EmitSound( "AlyxEmp.Charge" );
  428. #else
  429. EmitSound( "Item.Materialize" );
  430. #endif
  431. RemoveEffects( EF_NODRAW );
  432. DoMuzzleFlash();
  433. }
  434. SetTouch( &CItem::ItemTouch );
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void CItem::Precache()
  440. {
  441. BaseClass::Precache();
  442. PrecacheScriptSound( "Item.Materialize" );
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. // Input : *pPhysGunUser -
  447. // PICKED_UP_BY_CANNON -
  448. //-----------------------------------------------------------------------------
  449. void CItem::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  450. {
  451. m_OnCacheInteraction.FireOutput(pPhysGunUser, this);
  452. if ( reason == PICKED_UP_BY_CANNON )
  453. {
  454. // Expand the pickup box
  455. CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT * 2 );
  456. if( m_pConstraint != NULL )
  457. {
  458. physenv->DestroyConstraint( m_pConstraint );
  459. m_pConstraint = NULL;
  460. }
  461. }
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Purpose:
  465. // Input : *pPhysGunUser -
  466. // reason -
  467. //-----------------------------------------------------------------------------
  468. void CItem::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason )
  469. {
  470. // Restore the pickup box to the original
  471. CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT );
  472. }