game_shared/util_shared.cpp
1314151617181920212223
#include "vphysics/object_hash.h"
#include "IceKey.H"
#include "checksum_crc.h"
#ifdef CLIENT_DLL
#include "c_te_effect_dispatch.h"
#else
#include "te_effect_dispatch.h"
bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush );
#endif
131415161718192021222324252627282930
#include "vphysics/object_hash.h"
#include "IceKey.H"
#include "checksum_crc.h"
#include "ff_triggerclip.h"
#include "ff_gamerules.h"
#ifdef CLIENT_DLL
#include "c_ff_team.h"
#include "c_ff_player.h"
#include "c_te_effect_dispatch.h"
#else
#include "ff_team.h"
#include "ff_player.h"
#include "te_effect_dispatch.h"
#include "ff_item_flag.h"
bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush );
#endif
293031323334
ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT );
ConVar developer("developer", "0", 0, "Set developer message level" ); // developer mode
float UTIL_VecToYaw( const Vector &vec )
{
if (vec.y == 0 && vec.x == 0)
3637383940414243444546474849
ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT );
ConVar developer("developer", "0", 0, "Set developer message level" ); // developer mode
//Adding ConVars to toggle grenades/pipes colliding with the enemy
//ConVar ffdev_grenade_collidewithenemy("ffdev_grenade_collidewithenemy", "1", FCVAR_FF_FFDEV_REPLICATED, "If set to 0, grenades pass through enemies" );
#define GRENADE_COLLIDEWITHENEMY true
//ConVar ffdev_softclip_alwaysclippercent("ffdev_softclip_alwaysclippercent", "1.25", FCVAR_FF_FFDEV_REPLICATED, "Above this percentage of max class speed, teammates will always clip (as opposed to being able to stand on a teammates head)");
#define SOFTCLIP_ALWAYSCLIPPERCENT 1.25f
//ConVar ffdev_softclip_asdisguisedteam("ffdev_softclip_asdisguisedteam", "0", FCVAR_FF_FFDEV_REPLICATED, "If set to 1, spies soft clip as though they were on their disguised team");
#define SOFTCLIP_ASDISGUISEDTEAM false
float UTIL_VecToYaw( const Vector &vec )
{
if (vec.y == 0 && vec.x == 0)
212213214215216217218219220
return false;
// don't clip against owner
if ( pEntPass->GetOwnerEntity() == pEntTouch )
return false;
return true;
}
227228229230231232233234235236237238239240
return false;
// don't clip against owner
if(!pEntPass->CanClipOwnerEntity())
{
if(pEntPass->GetOwnerEntity() == pEntTouch)
return false;
}
if(!pEntPass->CanClipPlayer() && pEntTouch->IsPlayer())
return false;
return true;
}
256257258259260261262
}
//-----------------------------------------------------------------------------
// Simple trace filter
//-----------------------------------------------------------------------------
276277278279280281
}
//-----------------------------------------------------------------------------
// Simple trace filter
//-----------------------------------------------------------------------------
272273274275276277
//-----------------------------------------------------------------------------
bool CTraceFilterSimple::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
{
if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
return false;
291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
//-----------------------------------------------------------------------------
bool CTraceFilterSimple::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
{
CBaseEntity *pHandle = EntityFromEntityHandle( pHandleEntity );
const CBaseEntity *pPassEnt = NULL;
if( m_pPassEnt )
pPassEnt = EntityFromEntityHandle( m_pPassEnt );
// --> hlstriker: No team collisions
if( pPassEnt && pHandle )
{
if( pHandle->IsPlayer() )
{
////Team orient this collision group. Includes things like rockets, and blue pipes -Green Mushy
////Confusing: these projectiles use the interactive collision group, not the projectile collision group
////they are things like rockets/blue pipes, or other entities that will collide with most anything
//if( m_collisionGroup == COLLISION_GROUP_INTERACTIVE )
//{
// //Make sure u dont collide the object with the owner
// if( ToFFPlayer(pHandle) == ToFFPlayer(pPassEnt->GetOwnerEntity()) )
// {
// return false;
// }
// //Now dont collide if the team numbers are the same
// else if( ToFFPlayer(pHandle)->GetTeamNumber() == ToFFPlayer(pPassEnt->GetOwnerEntity())->GetTeamNumber())
// {
// return false;
// }
// //Now collide normally after the owner and teammate checks are over
// else
// {
// //Do checks here for other exceptions and return true for collision
// return true;
// }
//}
//This collision group is for grenades and yellow pipes even though its called "projectile"
//They pass through the player collision group normally
//adding an exception to collide with enemies here -Green Mushy
if( m_collisionGroup == COLLISION_GROUP_PROJECTILE )
{
//Make sure u dont collide with the owner
if( ToFFPlayer(pHandle) == ToFFPlayer(pPassEnt->GetOwnerEntity()) )
{
return false;
}
//Now check teams and dont collide if the team numbers are the same
else if( ToFFPlayer(pHandle)->GetTeamNumber() == ToFFPlayer(pPassEnt->GetOwnerEntity())->GetTeamNumber())
{
return false;
}
//Now collide normally after the owner and teammate checks are over
else
{
//Utilize the convar to toggle enemy collisions
if( GRENADE_COLLIDEWITHENEMY )
return true;
else
return false;
}
}
// need to also check for IsPlayer for things that don't send a collision group (like moveable brushes/doors)
if( m_collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT || (pPassEnt->IsPlayer() && contentsMask == MASK_PLAYERSOLID && m_collisionGroup == COLLISION_GROUP_PLAYER) )
{
if (pPassEnt == pHandle)
return false;
// This allows players to pass through any team object (another player or entity), includes allies
int iPassTeam = pPassEnt->GetTeamNumber();
int iHandleTeam = pHandle->GetTeamNumber();
// get disguised team if we should
if (SOFTCLIP_ASDISGUISEDTEAM)
{
if (pPassEnt->IsPlayer())
{
CFFPlayer *pPassPlayer = static_cast< CFFPlayer * >( const_cast< CBaseEntity * >( pPassEnt ) );
if (pPassPlayer && pPassPlayer->IsDisguised())
iPassTeam = pPassPlayer->GetDisguisedTeam();
}
if (pHandle->IsPlayer())
{
CFFPlayer *pHandlePlayer = ToFFPlayer(pHandle);
if (pHandlePlayer && pHandlePlayer->IsDisguised())
iHandleTeam = pHandlePlayer->GetDisguisedTeam();
}
}
if( pHandle->IsPlayer() && FFGameRules()->IsTeam1AlliedToTeam2( iPassTeam, iHandleTeam ) == GR_TEAMMATE )
{
// If either of the entities are moving fast enough, then just ignore teammates entirely
if (pPassEnt->IsPlayer())
{
CFFPlayer *pPlayer = ToFFPlayer( const_cast< CBaseEntity * >(pPassEnt) );
if (pPlayer)
{
float speed = pPlayer->GetMovementSpeed();
if (speed > pPlayer->MaxSpeed() * SOFTCLIP_ALWAYSCLIPPERCENT)
return false;
}
}
if (pHandle->IsPlayer())
{
CFFPlayer *pPlayer = ToFFPlayer( const_cast< CBaseEntity * >(pPassEnt) );
if (pPlayer)
{
float speed = pPlayer->GetMovementSpeed();
if (speed > pPlayer->MaxSpeed() * SOFTCLIP_ALWAYSCLIPPERCENT)
return false;
}
}
// If player lands on top of a team entity make sure they hit
Vector vecOrigin = pPassEnt->GetAbsOrigin();
Vector vecTeamOrigin = pHandle->GetAbsOrigin();
Vector vecMin;
if( pPassEnt->IsPlayer() )
{
CFFPlayer *pPlayer = static_cast< CFFPlayer * >( const_cast< CBaseEntity * >( pPassEnt ) );
vecMin = pPlayer->GetPlayerMins();
}
else
vecMin = pPassEnt->WorldAlignMins();
Vector vecTeamMax;
if( pHandle->IsPlayer() )
{
CFFPlayer *pPlayer = static_cast< CFFPlayer * >( pHandle );
vecTeamMax = pPlayer->GetPlayerMaxs();
}
else
vecTeamMax = pHandle->WorldAlignMaxs();
float fMinZ = vecMin[2];
VectorAdd( vecMin, vecOrigin, vecMin );
VectorAdd( vecTeamMax, vecTeamOrigin, vecTeamMax );
// If players mins are greater than teams maxs - 2
if( vecMin[2] >= vecTeamMax[2] )
return true;
if( vecMin[2] > vecTeamMax[2] - 5.0f )
{
// If a player is jumping through an entity to the top make sure they don't get stuck
CBaseEntity *pEnt = const_cast< CBaseEntity * >( pPassEnt );
pEnt->SetAbsOrigin( Vector( vecOrigin[0], vecOrigin[1], vecTeamMax[2] - fMinZ ) );
}
return false;
}
}
}
}
// <--
if( pHandle )
{
if( pHandle->Classify() == CLASS_TRIGGER_CLIP )
{
CFFTriggerClip *pTriggerClip = dynamic_cast< CFFTriggerClip * >( pHandle );
if( pTriggerClip && pTriggerClip->GetClipMask() )
{
if( pPassEnt )
{
// Players
if ( pPassEnt->IsPlayer() )
{
// Bullets from players
if ( contentsMask == MASK_SHOT )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pPassEnt->GetTeamNumber(),
LUA_CLIP_FLAG_BULLETS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BULLETSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Buildables trying to be built
else if ( m_collisionGroup == COLLISION_GROUP_BUILDABLE_BUILDING )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pPassEnt->GetTeamNumber(),
LUA_CLIP_FLAG_BUILDABLES, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BUILDABLESBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Players themselves
return ShouldFFTriggerClipBlock( pTriggerClip, pPassEnt->GetTeamNumber(),
LUA_CLIP_FLAG_PLAYERS,
LUA_CLIP_FLAG_PLAYERSBYTEAM );
}
// Buildables
else if ( FF_IsBuildableObject(const_cast< CBaseEntity * >( pPassEnt )) )
{
CFFBuildableObject *pBuildable = FF_ToBuildableObject( const_cast< CBaseEntity * >( pPassEnt ) );
if (!pBuildable)
return false;
// Bullets from buildables
if ( contentsMask == MASK_SHOT )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pBuildable->GetTeamNumber(),
LUA_CLIP_FLAG_BUILDABLEWEAPONS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BUILDABLEWEAPONSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Buildables getting built don't send a buildable pointer (they send a player), so
// any buildable getting sent is assumed to be doing damage of some sort
// For sure, though, detpacks trace with MASK_SOLID for their explosion damage
return ShouldFFTriggerClipBlock( pTriggerClip, pBuildable->GetTeamNumber(),
LUA_CLIP_FLAG_BUILDABLEWEAPONS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BUILDABLEWEAPONSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Grenades
else if ( pPassEnt->GetFlags() & FL_GRENADE )
{
// Get owner so we can get its team number
CBaseEntity *pOwner = pPassEnt->GetOwnerEntity();
if( !pOwner )
return false;
return ShouldFFTriggerClipBlock( pTriggerClip, pOwner->GetTeamNumber(),
LUA_CLIP_FLAG_GRENADES, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_GRENADESBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Backpacks
else if ( const_cast< CBaseEntity * >( pPassEnt )->Classify() == CLASS_BACKPACK )
{
// Get owner so we can get its team number
CBaseEntity *pOwner = pPassEnt->GetOwnerEntity();
if( !pOwner )
return false;
// exploding backpack from an EMP
if ( contentsMask == MASK_SHOT )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pOwner->GetTeamNumber(),
LUA_CLIP_FLAG_GRENADES, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_GRENADESBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
return ShouldFFTriggerClipBlock( pTriggerClip, pOwner->GetTeamNumber(),
LUA_CLIP_FLAG_BACKPACKS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BACKPACKSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Info_ff_scripts
else if ( const_cast< CBaseEntity * >( pPassEnt )->Classify() == CLASS_INFOSCRIPT )
{
// Info scripts by team is a complicated mess that I can't be bothered to
// deal with right now, so it's all-or-nothing for now - squeek
// TODO: Block info_ff_scripts by team
return ShouldFFTriggerClipBlock( pTriggerClip, 0,
LUA_CLIP_FLAG_INFOSCRIPTS, LUA_CLIP_FLAG_NONPLAYERS,
0 /*LUA_CLIP_FLAG_INFOSCRIPTSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM*/ );
}
// Respawn turrets
else if ( const_cast< CBaseEntity * >( pPassEnt )->Classify() == CLASS_TURRET )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pPassEnt->GetTeamNumber(),
LUA_CLIP_FLAG_SPAWNTURRETS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_SPAWNTURRETSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Projectiles or any other player-owned entities
else
{
// Get owner so we can get its team number
CBaseEntity *pOwner = pPassEnt->GetOwnerEntity();
if( !pOwner )
return false;
// Projectiles from players
if( pOwner->IsPlayer() )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pOwner->GetTeamNumber(),
LUA_CLIP_FLAG_PROJECTILES, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_PROJECTILESBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
// Projectiles from buildables
else if ( FF_IsBuildableObject(pOwner) )
{
return ShouldFFTriggerClipBlock( pTriggerClip, pOwner->GetTeamNumber(),
LUA_CLIP_FLAG_BUILDABLEWEAPONS, LUA_CLIP_FLAG_NONPLAYERS,
LUA_CLIP_FLAG_BUILDABLEWEAPONSBYTEAM | LUA_CLIP_FLAG_NONPLAYERSBYTEAM );
}
}
return false;
}
}
}
}
if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
return false;
278279280281282283284285286287288289290291292293
if ( m_pPassEnt )
{
if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
{
return false;
}
}
// Don't test if the game code tells us we should ignore this collision...
CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
return false;
if ( pEntity && !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
return false;
return true;
587588589590591592593594595596597598599
if ( m_pPassEnt )
{
if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
return false;
}
// Don't test if the game code tells us we should ignore this collision...
if ( !pHandle->ShouldCollide( m_collisionGroup, contentsMask ) )
return false;
if ( pHandle && !g_pGameRules->ShouldCollide( m_collisionGroup, pHandle->GetCollisionGroup() ) )
return false;
return true;
503504505506507508
const IHandleEntity *m_pIgnoreOther;
};
//-----------------------------------------------------------------------------
// Sweeps a particular entity through the world
//-----------------------------------------------------------------------------
809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
const IHandleEntity *m_pIgnoreOther;
};
//// Just something handy for testing
//class CTraceFilterInfoScriptEntity : public CTraceFilterSimple
//{
// DECLARE_CLASS( CTraceFilterEntity, CTraceFilterSimple );
//
//public:
// CTraceFilterInfoScriptEntity( CBaseEntity *pEntity, int nCollisionGroup )
// : CTraceFilterSimple( pEntity, nCollisionGroup )
// {
// m_pEntity = pEntity;
// }
//
// bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
// {
//#ifdef GAME_DLL
// Assert( dynamic_cast( pHandleEntity ) );
// CBaseEntity *pTestEntity = static_cast( pHandleEntity );
//
// DevMsg( "Info Script hitting: %s\n", pTestEntity->GetClassname() );
//#endif
//
// return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
// }
//
//private:
// CBaseEntity *m_pEntity;
//};
//-----------------------------------------------------------------------------
// Sweeps a particular entity through the world
//-----------------------------------------------------------------------------
514515516517518519
// because one day, rotated collideables will work!
Assert( pCollision->GetCollisionAngles() == vec3_angle );
CTraceFilterEntity traceFilter( pEntity, pCollision->GetCollisionGroup() );
enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
}
848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
// because one day, rotated collideables will work!
Assert( pCollision->GetCollisionAngles() == vec3_angle );
if( pEntity->Classify() == CLASS_INFOSCRIPT )
{
//// Hey, this is some cool shit and shows just how often the shit is tested
//#ifdef GAME_DLL
//#include "ndebugoverlay.h"
// static bool bFlipFlop = false;
// if( bFlipFlop )
// NDebugOverlay::Line( vecAbsStart, vecAbsEnd, 255, 0, 0, true, 60.0f );
// else
// NDebugOverlay::Line( vecAbsStart, vecAbsEnd, 0, 0, 255, true, 60.0f );
// bFlipFlop = !bFlipFlop;
//#endif
mask |= CONTENTS_PLAYERCLIP;
// CTraceFilterInfoScriptEntity traceFilter( pEntity, iCollisionGroup );
// enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
//#ifdef GAME_DLL
// if( ptr && ptr->m_pEnt )
// {
// DevMsg( "Collide Ent: %s\n", ptr->m_pEnt->GetClassname() );
// }
//#endif
}
CTraceFilterEntity traceFilter( pEntity, pCollision->GetCollisionGroup() );
enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
}
748749750751752753
DispatchEffect( "bloodimpact", data );
}
bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax )
{
Vector vHalfDims = ( vMax - vMin ) * 0.5f;
1108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
DispatchEffect( "bloodimpact", data );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void UTIL_BloodSpray( const Vector &pos, const Vector &dir, int color, int amount, int flags )
{
if( color == DONT_BLEED )
return;
CEffectData data;
data.m_vOrigin = pos;
data.m_vNormal = dir;
data.m_flScale = (float)amount;
data.m_fFlags = flags;
data.m_nColor = (unsigned char)color;
DispatchEffect( "bloodspray", data );
}
bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax )
{
Vector vHalfDims = ( vMax - vMin ) * 0.5f;