347 lines
9.9 KiB
C++
347 lines
9.9 KiB
C++
|
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "c_baseentity.h"
|
||
|
#ifdef WIN32
|
||
|
#include <typeinfo.h>
|
||
|
#endif
|
||
|
#include "tier0/vprof.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// helper method for trace hull as used by physics...
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static void Physics_TraceHull( C_BaseEntity* pBaseEntity, const Vector &vecStart,
|
||
|
const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax,
|
||
|
unsigned int mask, trace_t *ptr )
|
||
|
{
|
||
|
// FIXME: I really am not sure the best way of doing this
|
||
|
// The TraceHull code below for shots will make sure the object passes
|
||
|
// through shields which do not block that damage type. It will also
|
||
|
// send messages to the shields that they've been hit.
|
||
|
#if 0
|
||
|
if (pBaseEntity->GetDamageType() != DMG_GENERIC)
|
||
|
{
|
||
|
GameRules()->WeaponTraceHull( vecStart, vecEnd, hullMin, hullMax,
|
||
|
mask, pBaseEntity, pBaseEntity->GetCollisionGroup(),
|
||
|
pBaseEntity, ptr );
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, mask,
|
||
|
pBaseEntity, pBaseEntity->GetCollisionGroup(), ptr );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Does not change the entities velocity at all
|
||
|
// Input : push -
|
||
|
// Output : trace_t
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsCheckSweep( const Vector& vecAbsStart, const Vector &vecAbsDelta, trace_t *pTrace )
|
||
|
{
|
||
|
unsigned int mask = PhysicsSolidMaskForEntity();
|
||
|
|
||
|
Vector vecAbsEnd;
|
||
|
VectorAdd( vecAbsStart, vecAbsDelta, vecAbsEnd );
|
||
|
|
||
|
// Set collision type
|
||
|
if ( !IsSolid() || IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
|
||
|
{
|
||
|
// don't collide with monsters
|
||
|
mask &= ~CONTENTS_MONSTER;
|
||
|
}
|
||
|
|
||
|
Physics_TraceHull( this, vecAbsStart, vecAbsEnd, WorldAlignMins(), WorldAlignMaxs(), mask, pTrace );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : push -
|
||
|
// Output : trace_t
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsPushEntity( const Vector& push, trace_t *pTrace )
|
||
|
{
|
||
|
/*
|
||
|
if ( m_pMoveParent )
|
||
|
{
|
||
|
Warning( "pushing entity (%s) that has m_pMoveParent!\n", STRING( pev->classname ) );
|
||
|
Assert(0);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// NOTE: absorigin and origin must be equal because there is no moveparent
|
||
|
Vector prevOrigin;
|
||
|
VectorCopy( GetAbsOrigin(), prevOrigin );
|
||
|
|
||
|
trace_t trace;
|
||
|
PhysicsCheckSweep( prevOrigin, push, pTrace );
|
||
|
|
||
|
if ( pTrace->fraction )
|
||
|
{
|
||
|
SetAbsOrigin( pTrace->endpos );
|
||
|
}
|
||
|
|
||
|
// CLIENT DLL HACKS
|
||
|
m_vecNetworkOrigin = GetLocalOrigin();
|
||
|
m_angNetworkAngles = GetLocalAngles();
|
||
|
|
||
|
// InvalidatePhysicsRecursive( POSITION_CHANGED | ANGLES_CHANGED );
|
||
|
|
||
|
if ( pTrace->m_pEnt )
|
||
|
{
|
||
|
PhysicsImpact( pTrace->m_pEnt, *pTrace );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pNewPosition -
|
||
|
// *pNewVelocity -
|
||
|
// *pNewAngles -
|
||
|
// *pNewAngVelocity -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
|
||
|
{
|
||
|
// If you're going to use custom physics, you need to implement this!
|
||
|
Assert(0);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsCustom()
|
||
|
{
|
||
|
PhysicsCheckWater();
|
||
|
|
||
|
// regular thinking
|
||
|
if ( !PhysicsRunThink() )
|
||
|
return;
|
||
|
|
||
|
// Moving upward, off the ground, or resting on something that isn't ground
|
||
|
if ( m_vecVelocity[2] > 0 || !GetGroundEntity() || !GetGroundEntity()->IsStandable() )
|
||
|
{
|
||
|
SetGroundEntity( NULL );
|
||
|
}
|
||
|
|
||
|
// NOTE: The entity must set the position, angles, velocity in its custom movement
|
||
|
Vector vecNewPosition = GetAbsOrigin();
|
||
|
|
||
|
if ( vecNewPosition == vec3_origin )
|
||
|
{
|
||
|
// Shouldn't be at world origin
|
||
|
Assert( 0 );
|
||
|
}
|
||
|
|
||
|
Vector vecNewVelocity = m_vecVelocity;
|
||
|
QAngle angNewAngles = GetAbsAngles();
|
||
|
QAngle angNewAngVelocity = m_vecAngVelocity;
|
||
|
|
||
|
PerformCustomPhysics( &vecNewPosition, &vecNewVelocity, &angNewAngles, &angNewAngVelocity );
|
||
|
|
||
|
// Store off all of the new state information...
|
||
|
m_vecVelocity = vecNewVelocity;
|
||
|
SetAbsAngles( angNewAngles );
|
||
|
m_vecAngVelocity = angNewAngVelocity;
|
||
|
|
||
|
Vector move;
|
||
|
VectorSubtract( vecNewPosition, GetAbsOrigin(), move );
|
||
|
|
||
|
// move origin
|
||
|
trace_t trace;
|
||
|
PhysicsPushEntity( move, &trace );
|
||
|
|
||
|
PhysicsCheckVelocity();
|
||
|
|
||
|
if (trace.allsolid)
|
||
|
{
|
||
|
// entity is trapped in another solid
|
||
|
// UNDONE: does this entity needs to be removed?
|
||
|
VectorCopy (vec3_origin, m_vecVelocity);
|
||
|
VectorCopy (vec3_angle, m_vecAngVelocity);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if !defined( CLIENT_DLL )
|
||
|
if (pev->free)
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
// check for in water
|
||
|
PhysicsCheckWaterTransition();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsStep()
|
||
|
{
|
||
|
// Run all but the base think function
|
||
|
PhysicsRunThink( THINK_FIRE_ALL_BUT_BASE );
|
||
|
PhysicsRunThink( THINK_FIRE_BASE_ONLY );
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsNoclip( void )
|
||
|
{
|
||
|
PhysicsRunThink();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsNone( void )
|
||
|
{
|
||
|
PhysicsRunThink();
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsPusher( void )
|
||
|
{
|
||
|
PhysicsRunThink();
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsParent( void )
|
||
|
{
|
||
|
PhysicsRunThink();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Not yet supported on client .dll
|
||
|
// Input : *pOther -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::StartTouch( C_BaseEntity *pOther )
|
||
|
{
|
||
|
// notify parent
|
||
|
// if ( m_pParent != NULL )
|
||
|
// m_pParent->StartTouch( pOther );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Call touch function if one is set
|
||
|
// Input : *pOther -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::Touch( C_BaseEntity *pOther )
|
||
|
{
|
||
|
if ( m_pfnTouch )
|
||
|
(this->*m_pfnTouch)( pOther );
|
||
|
|
||
|
// notify parent of touch
|
||
|
// if ( m_pParent != NULL )
|
||
|
// m_pParent->Touch( pOther );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Call end touch
|
||
|
// Input : *pOther -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::EndTouch( C_BaseEntity *pOther )
|
||
|
{
|
||
|
// notify parent
|
||
|
// if ( m_pParent != NULL )
|
||
|
// {
|
||
|
// m_pParent->EndTouch( pOther );
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : check -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::SetCheckUntouch( bool check )
|
||
|
{
|
||
|
// Invalidate touchstamp
|
||
|
if ( check )
|
||
|
{
|
||
|
touchStamp++;
|
||
|
if ( !IsEFlagSet( EFL_CHECK_UNTOUCH ) )
|
||
|
{
|
||
|
AddEFlags( EFL_CHECK_UNTOUCH );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RemoveEFlags( EFL_CHECK_UNTOUCH );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_BaseEntity::GetCheckUntouch() const
|
||
|
{
|
||
|
return IsEFlagSet( EFL_CHECK_UNTOUCH );
|
||
|
}
|
||
|
|
||
|
extern ConVar think_limit;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called when it's time for a physically moved objects (plats, doors, etc)
|
||
|
// to run it's game code.
|
||
|
// All other entity thinking is done during worldspawn's think
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseEntity::PhysicsDispatchThink( BASEPTR thinkFunc )
|
||
|
{
|
||
|
float thinkLimit = think_limit.GetFloat();
|
||
|
float startTime = 0.0;
|
||
|
|
||
|
/*
|
||
|
// This doesn't apply on the client, really
|
||
|
if ( IsDormant() )
|
||
|
{
|
||
|
Warning( "Dormant entity %s is thinking!!\n", GetClassname() );
|
||
|
Assert(0);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if ( thinkLimit )
|
||
|
{
|
||
|
startTime = Plat_FloatTime();
|
||
|
}
|
||
|
|
||
|
if ( thinkFunc )
|
||
|
{
|
||
|
(this->*thinkFunc)();
|
||
|
}
|
||
|
|
||
|
if ( thinkLimit )
|
||
|
{
|
||
|
// calculate running time of the AI in milliseconds
|
||
|
float time = ( Plat_FloatTime() - startTime ) * 1000.0f;
|
||
|
if ( time > thinkLimit )
|
||
|
{
|
||
|
#if 0
|
||
|
// If its an NPC print out the shedule/task that took so long
|
||
|
CAI_BaseNPC *pNPC = MyNPCPointer();
|
||
|
if (pNPC && pNPC->GetCurSchedule())
|
||
|
{
|
||
|
pNPC->ReportOverThinkLimit( time );
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
Msg( "CLIENT: %s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).raw_name(), time );
|
||
|
#else
|
||
|
Msg( "CLIENT: %s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).name(), time );
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|