|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Handling for the base world item. Most of this was moved from items.cpp.
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "player.h"
#include "items.h"
#include "gamerules.h"
#include "engine/IEngineSound.h"
#include "iservervehicle.h"
#include "physics_saverestore.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define ITEM_PICKUP_BOX_BLOAT 24
class CWorldItem : public CBaseAnimating { DECLARE_DATADESC(); public: DECLARE_CLASS( CWorldItem, CBaseAnimating );
bool KeyValue( const char *szKeyName, const char *szValue ); void Spawn( void );
int m_iType; };
LINK_ENTITY_TO_CLASS(world_items, CWorldItem);
BEGIN_DATADESC( CWorldItem )
DEFINE_FIELD( m_iType, FIELD_INTEGER ),
END_DATADESC()
bool CWorldItem::KeyValue( const char *szKeyName, const char *szValue ) { if (FStrEq(szKeyName, "type")) { m_iType = atoi(szValue); } else return BaseClass::KeyValue( szKeyName, szValue );
return true; }
void CWorldItem::Spawn( void ) { CBaseEntity *pEntity = NULL;
switch (m_iType) { case 44: // ITEM_BATTERY:
pEntity = CBaseEntity::Create( "item_battery", GetLocalOrigin(), GetLocalAngles() ); break; case 45: // ITEM_SUIT:
pEntity = CBaseEntity::Create( "item_suit", GetLocalOrigin(), GetLocalAngles() ); break; }
if (!pEntity) { Warning("unable to create world_item %d\n", m_iType ); } else { pEntity->m_target = m_target; pEntity->SetName( GetEntityName() ); pEntity->ClearSpawnFlags(); pEntity->AddSpawnFlags( m_spawnflags ); }
UTIL_RemoveImmediate( this ); }
BEGIN_DATADESC( CItem )
DEFINE_FIELD( m_bActivateWhenAtRest, FIELD_BOOLEAN ), DEFINE_FIELD( m_vOriginalSpawnOrigin, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_vOriginalSpawnAngles, FIELD_VECTOR ), DEFINE_PHYSPTR( m_pConstraint ),
// Function Pointers
DEFINE_ENTITYFUNC( ItemTouch ), DEFINE_THINKFUNC( Materialize ), DEFINE_THINKFUNC( ComeToRest ),
// Outputs
DEFINE_OUTPUT( m_OnPlayerTouch, "OnPlayerTouch" ), DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CItem::CItem() { m_bActivateWhenAtRest = false; }
CItem::~CItem() { if ( m_pConstraint ) { physenv->DestroyConstraint( m_pConstraint ); m_pConstraint = NULL; } }
bool CItem::CreateItemVPhysicsObject( void ) { // Create the object in the physics system
int nSolidFlags = GetSolidFlags() | FSOLID_NOT_STANDABLE; if ( !m_bActivateWhenAtRest ) { nSolidFlags |= FSOLID_TRIGGER; }
if ( VPhysicsInitNormal( SOLID_VPHYSICS, nSolidFlags, false ) == NULL ) { SetSolid( SOLID_BBOX ); AddSolidFlags( nSolidFlags );
// If it's not physical, drop it to the floor
if (UTIL_DropToFloor(this, MASK_SOLID) == 0) { Warning( "Item %s fell out of level at %f,%f,%f\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z); UTIL_Remove( this ); return false; } }
return true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CItem::Spawn( void ) { SetNetworkQuantizeOriginAngAngles( true );
if ( g_pGameRules->IsAllowedToSpawn( this ) == false ) { UTIL_Remove( this ); return; }
SetMoveType( MOVETYPE_FLYGRAVITY ); SetSolid( SOLID_BBOX ); SetBlocksLOS( false ); AddEFlags( EFL_NO_ROTORWASH_PUSH ); if( IsGameConsole() ) { AddEffects( EF_ITEM_BLINK ); }
// This will make them not collide with the player, but will collide
// against other items + weapons
SetCollisionGroup( COLLISION_GROUP_WEAPON ); if( HasBloatedCollision() ) { CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT ); } SetTouch(&CItem::ItemTouch);
if ( CreateItemVPhysicsObject() == false ) return;
m_takedamage = DAMAGE_EVENTS_ONLY;
#if !defined( CLIENT_DLL )
// Constrained start?
if ( HasSpawnFlags( SF_ITEM_START_CONSTRAINED ) ) { //Constrain the weapon in place
IPhysicsObject *pReferenceObject, *pAttachedObject;
pReferenceObject = g_PhysWorldObject; pAttachedObject = VPhysicsGetObject();
if ( pReferenceObject && pAttachedObject ) { constraint_fixedparams_t fixed; fixed.Defaults(); fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject );
fixed.constraint.forceLimit = lbs2kg( 10000 ); fixed.constraint.torqueLimit = lbs2kg( 10000 );
m_pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed );
m_pConstraint->SetGameData( (void *) this ); } } #endif //CLIENT_DLL
Vector origin = GetAbsOrigin(); QAngle angles = GetAbsAngles(); NetworkQuantize( origin, angles ); SetAbsOrigin( origin ); SetAbsAngles( angles ); }
void CItem::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBasePlayer *pPlayer = ToBasePlayer( pActivator );
if ( pPlayer ) { pPlayer->PickupObject( this ); } }
extern int gEvilImpulse101;
//-----------------------------------------------------------------------------
// Activate when at rest, but don't allow pickup until then
//-----------------------------------------------------------------------------
void CItem::ActivateWhenAtRest( float flTime /* = 0.5f */ ) { RemoveSolidFlags( FSOLID_TRIGGER ); m_bActivateWhenAtRest = true; SetThink( &CItem::ComeToRest ); SetNextThink( gpGlobals->curtime + flTime ); }
//-----------------------------------------------------------------------------
// Become touchable when we are at rest
//-----------------------------------------------------------------------------
void CItem::OnEntityEvent( EntityEvent_t event, void *pEventData ) { BaseClass::OnEntityEvent( event, pEventData );
if( event == ENTITY_EVENT_WATER_TOUCH && m_bActivateWhenAtRest ) { // Check if we are stomping on someone else's think function
Assert( m_pfnThink == nullptr || m_pfnThink == static_cast <void (CBaseEntity::*)(void)>(&CItem::ComeToRest) );
// Delay rest for a sec, to avoid changing collision
// properties inside a collision callback.
SetThink( &CItem::ComeToRest ); SetNextThink( gpGlobals->curtime + 0.1f ); } }
//-----------------------------------------------------------------------------
// Become touchable when we are at rest
//-----------------------------------------------------------------------------
void CItem::ComeToRest( void ) { if ( m_bActivateWhenAtRest ) { m_bActivateWhenAtRest = false; AddSolidFlags( FSOLID_TRIGGER ); SetThink( NULL ); } }
//-----------------------------------------------------------------------------
// Purpose: Used to tell whether an item may be picked up by the player. This
// accounts for solid obstructions being in the way.
// Input : *pItem - item in question
// *pPlayer - player attempting the pickup
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer ) { if ( pItem == NULL || pPlayer == NULL ) return false;
// For now, always allow a vehicle riding player to pick up things they're driving over
if ( pPlayer->IsInAVehicle() ) return true;
// Get our test positions
Vector vecStartPos; IPhysicsObject *pPhysObj = pItem->VPhysicsGetObject(); if ( pPhysObj != NULL ) { // Use the physics hull's center
QAngle vecAngles; pPhysObj->GetPosition( &vecStartPos, &vecAngles ); } else { // Use the generic bbox center
vecStartPos = pItem->CollisionProp()->WorldSpaceCenter(); }
Vector vecEndPos = pPlayer->EyePosition();
// FIXME: This is the simple first try solution towards the problem. We need to take edges and shape more into account
// for this to be fully robust.
// Trace between to see if we're occluded
trace_t tr; CTraceFilterSkipTwoEntities filter( pPlayer, pItem, COLLISION_GROUP_PLAYER_MOVEMENT ); UTIL_TraceLine( vecStartPos, vecEndPos, MASK_SOLID, &filter, &tr );
// Occluded
// FIXME: For now, we exclude starting in solid because there are cases where this doesn't matter
if ( tr.fraction < 1.0f ) return false;
return true; }
//-----------------------------------------------------------------------------
// Purpose: Whether or not the item can be touched and picked up by the player, taking
// into account obstructions and other hinderances
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CItem::ItemCanBeTouchedByPlayer( CBasePlayer *pPlayer ) { return UTIL_ItemCanBeTouchedByPlayer( this, pPlayer ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pOther -
//-----------------------------------------------------------------------------
void CItem::ItemTouch( CBaseEntity *pOther ) { ItemTouchInternal( pOther, false ); }
void CItem::ItemForceTouch( CBaseEntity *pOther ) { ItemTouchInternal( pOther, true ); }
void CItem::ItemTouchInternal( CBaseEntity *pOther, bool bForceTouch ) { // Vehicles can touch items + pick them up
if ( pOther->GetServerVehicle() ) { pOther = pOther->GetServerVehicle()->GetPassenger(); if ( !pOther ) return; }
// if it's not a player, ignore
if ( !pOther->IsPlayer() ) return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
// Must be a valid pickup scenario (no blocking). Though this is a more expensive
// check than some that follow, this has to be first Obecause it's the only one
// that inhibits firing the output OnCacheInteraction.
if ( !bForceTouch && ItemCanBeTouchedByPlayer( pPlayer ) == false ) return;
m_OnCacheInteraction.FireOutput(pOther, this);
// Can I even pick stuff up?
if ( !pPlayer->IsAllowedToPickupWeapons() ) return;
// ok, a player is touching this item, but can he have it?
if ( !g_pGameRules->CanHaveItem( pPlayer, this ) ) { // no? Ignore the touch.
return; }
if ( MyTouch( pPlayer ) ) { m_OnPlayerTouch.FireOutput(pOther, this);
SetTouch( NULL ); SetThink( NULL );
// player grabbed the item.
g_pGameRules->PlayerGotItem( pPlayer, this ); if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES ) { Respawn(); } else { UTIL_Remove( this );
} } else if (gEvilImpulse101) { UTIL_Remove( this ); } }
CBaseEntity* CItem::Respawn( void ) { SetTouch( NULL ); AddEffects( EF_NODRAW );
VPhysicsDestroyObject();
SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_TRIGGER );
UTIL_SetOrigin( this, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn.
SetAbsAngles( g_pGameRules->VecItemRespawnAngles( this ) );// set the angles.
#if !defined( TF_DLL )
UTIL_DropToFloor( this, MASK_SOLID ); #endif
RemoveAllDecals(); //remove any decals
SetThink ( &CItem::Materialize ); SetNextThink( gpGlobals->curtime + g_pGameRules->FlItemRespawnTime( this ) ); return this; }
void CItem::Materialize( void ) { CreateItemVPhysicsObject();
if ( IsEffectActive( EF_NODRAW ) ) { // changing from invisible state to visible.
// this sound no longer exists
// EmitSound( "Item.Materialize" );
RemoveEffects( EF_NODRAW ); DoMuzzleFlash(); }
SetTouch( &CItem::ItemTouch ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CItem::Precache() { BaseClass::Precache();
// this sound no longer exists
// PrecacheScriptSound( "Item.Materialize" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPhysGunUser -
// PICKED_UP_BY_CANNON -
//-----------------------------------------------------------------------------
void CItem::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) { m_OnCacheInteraction.FireOutput(pPhysGunUser, this);
if ( reason == PICKED_UP_BY_CANNON ) { // Expand the pickup box
CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT * 2 );
if( m_pConstraint != NULL ) { physenv->DestroyConstraint( m_pConstraint ); m_pConstraint = NULL; } } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPhysGunUser -
// reason -
//-----------------------------------------------------------------------------
void CItem::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason ) { // Restore the pickup box to the original
CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT ); }
|