diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/portal | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/portal')
40 files changed, 14236 insertions, 0 deletions
diff --git a/game/shared/portal/PortalSimulation.cpp b/game/shared/portal/PortalSimulation.cpp new file mode 100644 index 0000000..233c98f --- /dev/null +++ b/game/shared/portal/PortalSimulation.cpp @@ -0,0 +1,3322 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=====================================================================================// + + +#include "cbase.h" +#include "PortalSimulation.h" +#include "vphysics_interface.h" +#include "physics.h" +#include "portal_shareddefs.h" +#include "StaticCollisionPolyhedronCache.h" +#include "model_types.h" +#include "filesystem.h" +#include "collisionutils.h" +#include "tier1/callqueue.h" + +#ifndef CLIENT_DLL + +#include "world.h" +#include "portal_player.h" //TODO: Move any portal mod specific code to callback functions or something +#include "physicsshadowclone.h" +#include "portal/weapon_physcannon.h" +#include "player_pickup.h" +#include "isaverestore.h" +#include "hierarchy.h" +#include "env_debughistory.h" + +#else + +#include "c_world.h" + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CCallQueue *GetPortalCallQueue(); + +extern IPhysicsConstraintEvent *g_pConstraintEvents; + +static ConVar sv_portal_collision_sim_bounds_x( "sv_portal_collision_sim_bounds_x", "200", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); +static ConVar sv_portal_collision_sim_bounds_y( "sv_portal_collision_sim_bounds_y", "200", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); +static ConVar sv_portal_collision_sim_bounds_z( "sv_portal_collision_sim_bounds_z", "252", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); + +//#define DEBUG_PORTAL_SIMULATION_CREATION_TIMES //define to output creation timings to developer 2 +//#define DEBUG_PORTAL_COLLISION_ENVIRONMENTS //define this to allow for glview collision dumps of portal simulators + +#if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) || defined( DEBUG_PORTAL_SIMULATION_CREATION_TIMES ) +# if !defined( PORTAL_SIMULATORS_EMBED_GUID ) +# pragma message( __FILE__ "(" __LINE__AS_STRING ") : error custom: Portal simulators require a GUID to debug, enable the GUID in PortalSimulation.h ." ) +# endif +#endif + +#if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) +void DumpActiveCollision( const CPortalSimulator *pPortalSimulator, const char *szFileName ); //appends to the existing file if it exists +#endif + +#define PORTAL_WALL_FARDIST 200.0f +#define PORTAL_WALL_TUBE_DEPTH 1.0f +#define PORTAL_WALL_TUBE_OFFSET 0.01f +#define PORTAL_WALL_MIN_THICKNESS 0.1f +#define PORTAL_POLYHEDRON_CUT_EPSILON (1.0f/1099511627776.0f) // 1 / (1<<40) +#define PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT 0.1f //separating the world collision from wall collision by a small amount gets rid of extremely thin erroneous collision at the separating plane + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS +static ConVar sv_dump_portalsimulator_collision( "sv_dump_portalsimulator_collision", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); //whether to actually dump out the data now that the possibility exists +static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ); +static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ); +#endif + +#ifdef DEBUG_PORTAL_SIMULATION_CREATION_TIMES +#define STARTDEBUGTIMER(x) { x.Start(); } +#define STOPDEBUGTIMER(x) { x.End(); } +#define DEBUGTIMERONLY(x) x +#define CREATEDEBUGTIMER(x) CFastTimer x; +static const char *s_szTabSpacing[] = { "", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t" }; +static int s_iTabSpacingIndex = 0; +static int s_iPortalSimulatorGUID = 0; //used in standalone function that have no idea what a portal simulator is +#define INCREMENTTABSPACING() ++s_iTabSpacingIndex; +#define DECREMENTTABSPACING() --s_iTabSpacingIndex; +#define TABSPACING (s_szTabSpacing[s_iTabSpacingIndex]) +#else +#define STARTDEBUGTIMER(x) +#define STOPDEBUGTIMER(x) +#define DEBUGTIMERONLY(x) +#define CREATEDEBUGTIMER(x) +#define INCREMENTTABSPACING() +#define DECREMENTTABSPACING() +#define TABSPACING +#endif + +#define PORTAL_HOLE_HALF_HEIGHT (PORTAL_HALF_HEIGHT + 0.1f) +#define PORTAL_HOLE_HALF_WIDTH (PORTAL_HALF_WIDTH + 0.1f) + + +static void ConvertBrushListToClippedPolyhedronList( const int *pBrushes, int iBrushCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector<CPolyhedron *> *pPolyhedronList ); +static void ClipPolyhedrons( CPolyhedron * const *pExistingPolyhedrons, int iPolyhedronCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector<CPolyhedron *> *pPolyhedronList ); +static inline CPolyhedron *TransformAndClipSinglePolyhedron( CPolyhedron *pExistingPolyhedron, const VMatrix &Transform, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fCutEpsilon, bool bUseTempMemory ); +static int GetEntityPhysicsObjects( IPhysicsEnvironment *pEnvironment, CBaseEntity *pEntity, IPhysicsObject **pRetList, int iRetListArraySize ); +static CPhysCollide *ConvertPolyhedronsToCollideable( CPolyhedron **pPolyhedrons, int iPolyhedronCount ); + +#ifndef CLIENT_DLL +static void UpdateShadowClonesPortalSimulationFlags( const CBaseEntity *pSourceEntity, unsigned int iFlags, int iSourceFlags ); +static bool g_bPlayerIsInSimulator = false; +#endif + +static CUtlVector<CPortalSimulator *> s_PortalSimulators; +CUtlVector<CPortalSimulator *> const &g_PortalSimulators = s_PortalSimulators; + +static CPortalSimulator *s_OwnedEntityMap[MAX_EDICTS] = { NULL }; +static CPortalSimulatorEventCallbacks s_DummyPortalSimulatorCallback; + +const char *PS_SD_Static_World_StaticProps_ClippedProp_t::szTraceSurfaceName = "**studio**"; +const int PS_SD_Static_World_StaticProps_ClippedProp_t::iTraceSurfaceFlags = 0; +CBaseEntity *PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = NULL; + + + +CPortalSimulator::CPortalSimulator( void ) +: m_bLocalDataIsReady(false), + m_bGenerateCollision(true), + m_bSimulateVPhysics(true), + m_bSharedCollisionConfiguration(false), + m_pLinkedPortal(NULL), + m_bInCrossLinkedFunction(false), + m_pCallbacks(&s_DummyPortalSimulatorCallback), + m_DataAccess(m_InternalData) +{ + s_PortalSimulators.AddToTail( this ); + +#ifdef CLIENT_DLL + m_bGenerateCollision = (GameRules() && GameRules()->IsMultiplayer()); +#endif + + m_CreationChecklist.bPolyhedronsGenerated = false; + m_CreationChecklist.bLocalCollisionGenerated = false; + m_CreationChecklist.bLinkedCollisionGenerated = false; + m_CreationChecklist.bLocalPhysicsGenerated = false; + m_CreationChecklist.bLinkedPhysicsGenerated = false; + +#ifdef PORTAL_SIMULATORS_EMBED_GUID + static int s_iPortalSimulatorGUIDAllocator = 0; + m_iPortalSimulatorGUID = s_iPortalSimulatorGUIDAllocator++; +#endif + +#ifndef CLIENT_DLL + PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = GetWorldEntity(); //will overinitialize, but it's cheap + + m_InternalData.Simulation.pCollisionEntity = (CPSCollisionEntity *)CreateEntityByName( "portalsimulator_collisionentity" ); + Assert( m_InternalData.Simulation.pCollisionEntity != NULL ); + if( m_InternalData.Simulation.pCollisionEntity ) + { + m_InternalData.Simulation.pCollisionEntity->m_pOwningSimulator = this; + MarkAsOwned( m_InternalData.Simulation.pCollisionEntity ); + m_InternalData.Simulation.Dynamic.EntFlags[m_InternalData.Simulation.pCollisionEntity->entindex()] |= PSEF_OWNS_PHYSICS; + DispatchSpawn( m_InternalData.Simulation.pCollisionEntity ); + } +#else + PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = GetClientWorldEntity(); +#endif +} + + + +CPortalSimulator::~CPortalSimulator( void ) +{ + //go assert crazy here + DetachFromLinked(); + ClearEverything(); + + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + { + if( s_PortalSimulators[i] == this ) + { + s_PortalSimulators.FastRemove( i ); + break; + } + } + + if( m_InternalData.Placement.pHoleShapeCollideable ) + physcollision->DestroyCollide( m_InternalData.Placement.pHoleShapeCollideable ); + +#ifndef CLIENT_DLL + if( m_InternalData.Simulation.pCollisionEntity ) + { + m_InternalData.Simulation.pCollisionEntity->m_pOwningSimulator = NULL; + m_InternalData.Simulation.Dynamic.EntFlags[m_InternalData.Simulation.pCollisionEntity->entindex()] &= ~PSEF_OWNS_PHYSICS; + MarkAsReleased( m_InternalData.Simulation.pCollisionEntity ); + UTIL_Remove( m_InternalData.Simulation.pCollisionEntity ); + m_InternalData.Simulation.pCollisionEntity = NULL; + } +#endif +} + + + +void CPortalSimulator::MoveTo( const Vector &ptCenter, const QAngle &angles ) +{ + if( (m_InternalData.Placement.ptCenter == ptCenter) && (m_InternalData.Placement.qAngles == angles) ) //not actually moving at all + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::MoveTo() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + +#ifndef CLIENT_DLL + //create a list of all entities that are actually within the portal hole, they will likely need to be moved out of solid space when the portal moves + CBaseEntity **pFixEntities = (CBaseEntity **)stackalloc( sizeof( CBaseEntity * ) * m_InternalData.Simulation.Dynamic.OwnedEntities.Count() ); + int iFixEntityCount = 0; + for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + { + CBaseEntity *pEntity = m_InternalData.Simulation.Dynamic.OwnedEntities[i]; + if( CPhysicsShadowClone::IsShadowClone( pEntity ) || + CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) + continue; + + if( EntityIsInPortalHole( pEntity) ) + { + pFixEntities[iFixEntityCount] = pEntity; + ++iFixEntityCount; + } + } + VPlane OldPlane = m_InternalData.Placement.PortalPlane; //used in fixing code +#endif + + //update geometric data + { + m_InternalData.Placement.ptCenter = ptCenter; + m_InternalData.Placement.qAngles = angles; + AngleVectors( angles, &m_InternalData.Placement.vForward, &m_InternalData.Placement.vRight, &m_InternalData.Placement.vUp ); + + m_InternalData.Placement.PortalPlane.Init( m_InternalData.Placement.vForward, m_InternalData.Placement.vForward.Dot( m_InternalData.Placement.ptCenter ) ); + } + + //Clear(); +#ifndef CLIENT_DLL + ClearLinkedPhysics(); + ClearLocalPhysics(); +#endif + ClearLinkedCollision(); + ClearLocalCollision(); + ClearPolyhedrons(); + + m_bLocalDataIsReady = true; + UpdateLinkMatrix(); + + //update hole shape - used to detect if an entity is within the portal hole bounds + { + if( m_InternalData.Placement.pHoleShapeCollideable ) + physcollision->DestroyCollide( m_InternalData.Placement.pHoleShapeCollideable ); + + float fHolePlanes[6*4]; + + //first and second planes are always forward and backward planes + fHolePlanes[(0*4) + 0] = m_InternalData.Placement.PortalPlane.m_Normal.x; + fHolePlanes[(0*4) + 1] = m_InternalData.Placement.PortalPlane.m_Normal.y; + fHolePlanes[(0*4) + 2] = m_InternalData.Placement.PortalPlane.m_Normal.z; + fHolePlanes[(0*4) + 3] = m_InternalData.Placement.PortalPlane.m_Dist - 0.5f; + + fHolePlanes[(1*4) + 0] = -m_InternalData.Placement.PortalPlane.m_Normal.x; + fHolePlanes[(1*4) + 1] = -m_InternalData.Placement.PortalPlane.m_Normal.y; + fHolePlanes[(1*4) + 2] = -m_InternalData.Placement.PortalPlane.m_Normal.z; + fHolePlanes[(1*4) + 3] = (-m_InternalData.Placement.PortalPlane.m_Dist) + 500.0f; + + + //the remaining planes will always have the same ordering of normals, with different distances plugged in for each convex we're creating + //normal order is up, down, left, right + + fHolePlanes[(2*4) + 0] = m_InternalData.Placement.vUp.x; + fHolePlanes[(2*4) + 1] = m_InternalData.Placement.vUp.y; + fHolePlanes[(2*4) + 2] = m_InternalData.Placement.vUp.z; + fHolePlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HALF_HEIGHT * 0.98f)) ); + + fHolePlanes[(3*4) + 0] = -m_InternalData.Placement.vUp.x; + fHolePlanes[(3*4) + 1] = -m_InternalData.Placement.vUp.y; + fHolePlanes[(3*4) + 2] = -m_InternalData.Placement.vUp.z; + fHolePlanes[(3*4) + 3] = -m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vUp * (PORTAL_HALF_HEIGHT * 0.98f)) ); + + fHolePlanes[(4*4) + 0] = -m_InternalData.Placement.vRight.x; + fHolePlanes[(4*4) + 1] = -m_InternalData.Placement.vRight.y; + fHolePlanes[(4*4) + 2] = -m_InternalData.Placement.vRight.z; + fHolePlanes[(4*4) + 3] = -m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vRight * (PORTAL_HALF_WIDTH * 0.98f)) ); + + fHolePlanes[(5*4) + 0] = m_InternalData.Placement.vRight.x; + fHolePlanes[(5*4) + 1] = m_InternalData.Placement.vRight.y; + fHolePlanes[(5*4) + 2] = m_InternalData.Placement.vRight.z; + fHolePlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vRight * (PORTAL_HALF_WIDTH * 0.98f)) ); + + CPolyhedron *pPolyhedron = GeneratePolyhedronFromPlanes( fHolePlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON, true ); + Assert( pPolyhedron != NULL ); + CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pPolyhedron ); + pPolyhedron->Release(); + Assert( pConvex != NULL ); + m_InternalData.Placement.pHoleShapeCollideable = physcollision->ConvertConvexToCollide( &pConvex, 1 ); + } + +#ifndef CLIENT_DLL + for( int i = 0; i != iFixEntityCount; ++i ) + { + if( !EntityIsInPortalHole( pFixEntities[i] ) ) + { + //this entity is most definitely stuck in a solid wall right now + //pFixEntities[i]->SetAbsOrigin( pFixEntities[i]->GetAbsOrigin() + (OldPlane.m_Normal * 50.0f) ); + FindClosestPassableSpace( pFixEntities[i], OldPlane.m_Normal ); + continue; + } + + //entity is still in the hole, but it's possible the hole moved enough where they're in part of the wall + { + //TODO: figure out if that's the case and fix it + } + } +#endif + + CreatePolyhedrons(); + CreateAllCollision(); +#ifndef CLIENT_DLL + CreateAllPhysics(); +#endif + +#if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) && !defined( CLIENT_DLL ) + if( sv_dump_portalsimulator_collision.GetBool() ) + { + const char *szFileName = "pscd.txt"; + filesystem->RemoveFile( szFileName ); + DumpActiveCollision( this, szFileName ); + if( m_pLinkedPortal ) + { + szFileName = "pscd_linked.txt"; + filesystem->RemoveFile( szFileName ); + DumpActiveCollision( m_pLinkedPortal, szFileName ); + } + } +#endif + +#ifndef CLIENT_DLL + Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); +#endif + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::MoveTo() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::UpdateLinkMatrix( void ) +{ + if( m_pLinkedPortal && m_pLinkedPortal->m_bLocalDataIsReady ) + { + Vector vLocalLeft = -m_InternalData.Placement.vRight; + VMatrix matLocalToWorld( m_InternalData.Placement.vForward, vLocalLeft, m_InternalData.Placement.vUp ); + matLocalToWorld.SetTranslation( m_InternalData.Placement.ptCenter ); + + VMatrix matLocalToWorldInverse; + MatrixInverseTR( matLocalToWorld,matLocalToWorldInverse ); + + //180 degree rotation about up + VMatrix matRotation; + matRotation.Identity(); + matRotation.m[0][0] = -1.0f; + matRotation.m[1][1] = -1.0f; + + Vector vRemoteLeft = -m_pLinkedPortal->m_InternalData.Placement.vRight; + VMatrix matRemoteToWorld( m_pLinkedPortal->m_InternalData.Placement.vForward, vRemoteLeft, m_pLinkedPortal->m_InternalData.Placement.vUp ); + matRemoteToWorld.SetTranslation( m_pLinkedPortal->m_InternalData.Placement.ptCenter ); + + //final + m_InternalData.Placement.matThisToLinked = matRemoteToWorld * matRotation * matLocalToWorldInverse; + } + else + { + m_InternalData.Placement.matThisToLinked.Identity(); + } + + m_InternalData.Placement.matThisToLinked.InverseTR( m_InternalData.Placement.matLinkedToThis ); + + MatrixAngles( m_InternalData.Placement.matThisToLinked.As3x4(), m_InternalData.Placement.ptaap_ThisToLinked.qAngleTransform, m_InternalData.Placement.ptaap_ThisToLinked.ptOriginTransform ); + MatrixAngles( m_InternalData.Placement.matLinkedToThis.As3x4(), m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform ); + + if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) + { + Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions + m_bInCrossLinkedFunction = true; + m_pLinkedPortal->UpdateLinkMatrix(); + m_bInCrossLinkedFunction = false; + } +} + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS +static ConVar sv_debug_dumpportalhole_nextcheck( "sv_debug_dumpportalhole_nextcheck", "0", FCVAR_CHEAT | FCVAR_REPLICATED ); +#endif + +bool CPortalSimulator::EntityIsInPortalHole( CBaseEntity *pEntity ) const +{ + if( m_bLocalDataIsReady == false ) + return false; + + Assert( m_InternalData.Placement.pHoleShapeCollideable != NULL ); + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + const char *szDumpFileName = "ps_entholecheck.txt"; + if( sv_debug_dumpportalhole_nextcheck.GetBool() ) + { + filesystem->RemoveFile( szDumpFileName ); + + DumpActiveCollision( this, szDumpFileName ); + PortalSimulatorDumps_DumpCollideToGlView( m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, 1.0f, szDumpFileName ); + } +#endif + + trace_t Trace; + + switch( pEntity->GetSolid() ) + { + case SOLID_VPHYSICS: + { + ICollideable *pCollideable = pEntity->GetCollideable(); + vcollide_t *pVCollide = modelinfo->GetVCollide( pCollideable->GetCollisionModel() ); + + //Assert( pVCollide != NULL ); //brush models? + if( pVCollide != NULL ) + { + Vector ptEntityPosition = pCollideable->GetCollisionOrigin(); + QAngle qEntityAngles = pCollideable->GetCollisionAngles(); + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + if( sv_debug_dumpportalhole_nextcheck.GetBool() ) + { + for( int i = 0; i != pVCollide->solidCount; ++i ) + PortalSimulatorDumps_DumpCollideToGlView( m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, 0.4f, szDumpFileName ); + + sv_debug_dumpportalhole_nextcheck.SetValue( false ); + } +#endif + + for( int i = 0; i != pVCollide->solidCount; ++i ) + { + physcollision->TraceCollide( ptEntityPosition, ptEntityPosition, pVCollide->solids[i], qEntityAngles, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); + + if( Trace.startsolid ) + return true; + } + } + else + { + //energy balls lack a vcollide + Vector vMins, vMaxs, ptCenter; + pCollideable->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); + ptCenter = (vMins + vMaxs) * 0.5f; + vMins -= ptCenter; + vMaxs -= ptCenter; + physcollision->TraceBox( ptCenter, ptCenter, vMins, vMaxs, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); + + return Trace.startsolid; + } + break; + } + + case SOLID_BBOX: + { + Vector ptEntityPosition = pEntity->GetAbsOrigin(); + CCollisionProperty *pCollisionProp = pEntity->CollisionProp(); + + physcollision->TraceBox( ptEntityPosition, ptEntityPosition, pCollisionProp->OBBMins(), pCollisionProp->OBBMaxs(), m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + if( sv_debug_dumpportalhole_nextcheck.GetBool() ) + { + Vector vMins = ptEntityPosition + pCollisionProp->OBBMins(); + Vector vMaxs = ptEntityPosition + pCollisionProp->OBBMaxs(); + PortalSimulatorDumps_DumpBoxToGlView( vMins, vMaxs, 1.0f, 1.0f, 1.0f, szDumpFileName ); + + sv_debug_dumpportalhole_nextcheck.SetValue( false ); + } +#endif + + if( Trace.startsolid ) + return true; + + break; + } + case SOLID_NONE: +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + if( sv_debug_dumpportalhole_nextcheck.GetBool() ) + sv_debug_dumpportalhole_nextcheck.SetValue( false ); +#endif + + return false; + + default: + Assert( false ); //make a handler + }; + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + if( sv_debug_dumpportalhole_nextcheck.GetBool() ) + sv_debug_dumpportalhole_nextcheck.SetValue( false ); +#endif + + return false; +} + +bool CPortalSimulator::EntityHitBoxExtentIsInPortalHole( CBaseAnimating *pBaseAnimating ) const +{ + if( m_bLocalDataIsReady == false ) + return false; + + bool bFirstVert = true; + Vector vMinExtent; + Vector vMaxExtent; + + CStudioHdr *pStudioHdr = pBaseAnimating->GetModelPtr(); + if ( !pStudioHdr ) + return false; + + mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pBaseAnimating->m_nHitboxSet ); + if ( !set ) + return false; + + Vector position; + QAngle angles; + + for ( int i = 0; i < set->numhitboxes; i++ ) + { + mstudiobbox_t *pbox = set->pHitbox( i ); + + pBaseAnimating->GetBonePosition( pbox->bone, position, angles ); + + // Build a rotation matrix from orientation + matrix3x4_t fRotateMatrix; + AngleMatrix( angles, fRotateMatrix ); + + //Vector pVerts[8]; + Vector vecPos; + for ( int i = 0; i < 8; ++i ) + { + vecPos[0] = ( i & 0x1 ) ? pbox->bbmax[0] : pbox->bbmin[0]; + vecPos[1] = ( i & 0x2 ) ? pbox->bbmax[1] : pbox->bbmin[1]; + vecPos[2] = ( i & 0x4 ) ? pbox->bbmax[2] : pbox->bbmin[2]; + + Vector vRotVec; + + VectorRotate( vecPos, fRotateMatrix, vRotVec ); + vRotVec += position; + + if ( bFirstVert ) + { + vMinExtent = vRotVec; + vMaxExtent = vRotVec; + bFirstVert = false; + } + else + { + vMinExtent = vMinExtent.Min( vRotVec ); + vMaxExtent = vMaxExtent.Max( vRotVec ); + } + } + } + + Vector ptCenter = (vMinExtent + vMaxExtent) * 0.5f; + vMinExtent -= ptCenter; + vMaxExtent -= ptCenter; + + trace_t Trace; + physcollision->TraceBox( ptCenter, ptCenter, vMinExtent, vMaxExtent, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); + + if( Trace.startsolid ) + return true; + + return false; +} + +void CPortalSimulator::RemoveEntityFromPortalHole( CBaseEntity *pEntity ) +{ + if( EntityIsInPortalHole( pEntity ) ) + { + FindClosestPassableSpace( pEntity, m_InternalData.Placement.PortalPlane.m_Normal ); + } +} + +bool CPortalSimulator::RayIsInPortalHole( const Ray_t &ray ) const +{ + trace_t Trace; + physcollision->TraceBox( ray, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); + return Trace.DidHit(); +} + +void CPortalSimulator::ClearEverything( void ) +{ + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::Clear() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + +#ifndef CLIENT_DLL + ClearAllPhysics(); +#endif + ClearAllCollision(); + ClearPolyhedrons(); + +#ifndef CLIENT_DLL + ReleaseAllEntityOwnership(); + + Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); +#endif + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::Clear() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::AttachTo( CPortalSimulator *pLinkedPortalSimulator ) +{ + Assert( pLinkedPortalSimulator ); + + if( pLinkedPortalSimulator == m_pLinkedPortal ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::AttachTo() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + DetachFromLinked(); + + m_pLinkedPortal = pLinkedPortalSimulator; + pLinkedPortalSimulator->m_pLinkedPortal = this; + + if( m_bLocalDataIsReady && m_pLinkedPortal->m_bLocalDataIsReady ) + { + UpdateLinkMatrix(); + CreateLinkedCollision(); +#ifndef CLIENT_DLL + CreateLinkedPhysics(); +#endif + } + +#if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) && !defined( CLIENT_DLL ) + if( sv_dump_portalsimulator_collision.GetBool() ) + { + const char *szFileName = "pscd.txt"; + filesystem->RemoveFile( szFileName ); + DumpActiveCollision( this, szFileName ); + if( m_pLinkedPortal ) + { + szFileName = "pscd_linked.txt"; + filesystem->RemoveFile( szFileName ); + DumpActiveCollision( m_pLinkedPortal, szFileName ); + } + } +#endif + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::AttachTo() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + +#ifndef CLIENT_DLL +void CPortalSimulator::TakeOwnershipOfEntity( CBaseEntity *pEntity ) +{ + AssertMsg( m_bLocalDataIsReady, "Tell the portal simulator where it is with MoveTo() before using it in any other way." ); + + Assert( pEntity != NULL ); + if( pEntity == NULL ) + return; + + if( pEntity->IsWorld() ) + return; + + if( CPhysicsShadowClone::IsShadowClone( pEntity ) ) + return; + + if( pEntity->GetServerVehicle() != NULL ) //we don't take kindly to vehicles in these here parts. Their physics controllers currently don't migrate properly and cause a crash + return; + + if( OwnsEntity( pEntity ) ) + return; + + Assert( GetSimulatorThatOwnsEntity( pEntity ) == NULL ); + MarkAsOwned( pEntity ); + Assert( GetSimulatorThatOwnsEntity( pEntity ) == this ); + + if( EntityIsInPortalHole( pEntity ) ) + m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] |= PSEF_IS_IN_PORTAL_HOLE; + else + m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_IS_IN_PORTAL_HOLE; + + UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] ); + + m_pCallbacks->PortalSimulator_TookOwnershipOfEntity( pEntity ); + + if( IsSimulatingVPhysics() ) + TakePhysicsOwnership( pEntity ); + + pEntity->CollisionRulesChanged(); //absolutely necessary in single-environment mode, possibly expendable in multi-environment moder + //pEntity->SetGroundEntity( NULL ); + IPhysicsObject *pObject = pEntity->VPhysicsGetObject(); + if( pObject ) + { + pObject->Wake(); + pObject->RecheckContactPoints(); + } + + CUtlVector<CBaseEntity *> childrenList; + GetAllChildren( pEntity, childrenList ); + for ( int i = childrenList.Count(); --i >= 0; ) + { + CBaseEntity *pEnt = childrenList[i]; + CPortalSimulator *pOwningSimulator = GetSimulatorThatOwnsEntity( pEnt ); + if( pOwningSimulator != this ) + { + if( pOwningSimulator != NULL ) + pOwningSimulator->ReleaseOwnershipOfEntity( pEnt, (pOwningSimulator == m_pLinkedPortal) ); + + TakeOwnershipOfEntity( childrenList[i] ); + } + } +} + + + +void CPortalSimulator::TakePhysicsOwnership( CBaseEntity *pEntity ) +{ + if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) + return; + + if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) + return; + + Assert( CPhysicsShadowClone::IsShadowClone( pEntity ) == false ); + Assert( OwnsEntity( pEntity ) ); //taking physics ownership happens AFTER general ownership + + if( OwnsPhysicsForEntity( pEntity ) ) + return; + + int iEntIndex = pEntity->entindex(); + m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_OWNS_PHYSICS; + + + //physics cloning + { +#ifdef _DEBUG + { + int iDebugIndex; + for( iDebugIndex = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) + { + if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) + break; + } + AssertMsg( iDebugIndex < 0, "Trying to own an entity, when a clone from the linked portal already exists" ); + + if( m_pLinkedPortal ) + { + for( iDebugIndex = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) + { + if( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) + break; + } + AssertMsg( iDebugIndex < 0, "Trying to own an entity, when we're already exporting a clone to the linked portal" ); + } + + //Don't require a copy from main to already exist + } +#endif + + EHANDLE hEnt = pEntity; + + //To linked portal + if( m_pLinkedPortal && m_pLinkedPortal->m_InternalData.Simulation.pPhysicsEnvironment ) + { + + DBG_CODE( + for( int i = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) + AssertMsg( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]->GetClonedEntity() != pEntity, "Already cloning to linked portal." ); + ); + + CPhysicsShadowClone *pClone = CPhysicsShadowClone::CreateShadowClone( m_pLinkedPortal->m_InternalData.Simulation.pPhysicsEnvironment, hEnt, "CPortalSimulator::TakePhysicsOwnership(): To Linked Portal", &m_InternalData.Placement.matThisToLinked.As3x4() ); + if( pClone ) + { + //bool bHeldByPhyscannon = false; + CBaseEntity *pHeldEntity = NULL; + CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pEntity ); + + if ( !pPlayer && pEntity->IsPlayer() ) + { + pPlayer = (CPortal_Player *)pEntity; + } + + if ( pPlayer ) + { + pHeldEntity = GetPlayerHeldEntity( pPlayer ); + /*if ( !pHeldEntity ) + { + pHeldEntity = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() ); + bHeldByPhyscannon = true; + }*/ + } + + if( pHeldEntity ) + { + //player is holding the entity, force them to pick it back up again + bool bIsHeldObjectOnOppositeSideOfPortal = pPlayer->IsHeldObjectOnOppositeSideOfPortal(); + pPlayer->m_bSilentDropAndPickup = true; + pPlayer->ForceDropOfCarriedPhysObjects( pHeldEntity ); + pPlayer->SetHeldObjectOnOppositeSideOfPortal( bIsHeldObjectOnOppositeSideOfPortal ); + } + + m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.AddToTail( pClone ); + m_pLinkedPortal->MarkAsOwned( pClone ); + m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= PSEF_OWNS_PHYSICS; + m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_IS_IN_PORTAL_HOLE; + pClone->CollisionRulesChanged(); //adding the clone to the portal simulator changes how it collides + + if( pHeldEntity ) + { + /*if ( bHeldByPhyscannon ) + { + PhysCannonPickupObject( pPlayer, pHeldEntity ); + } + else*/ + { + PlayerPickupObject( pPlayer, pHeldEntity ); + } + pPlayer->m_bSilentDropAndPickup = false; + } + } + } + } + + m_pCallbacks->PortalSimulator_TookPhysicsOwnershipOfEntity( pEntity ); +} + +void RecheckEntityCollision( CBaseEntity *pEntity ) +{ + CCallQueue *pCallQueue; + if ( (pCallQueue = GetPortalCallQueue()) != NULL ) + { + pCallQueue->QueueCall( RecheckEntityCollision, pEntity ); + return; + } + + pEntity->CollisionRulesChanged(); //absolutely necessary in single-environment mode, possibly expendable in multi-environment mode + //pEntity->SetGroundEntity( NULL ); + IPhysicsObject *pObject = pEntity->VPhysicsGetObject(); + if( pObject ) + { + pObject->Wake(); + pObject->RecheckContactPoints(); + } +} + + +void CPortalSimulator::ReleaseOwnershipOfEntity( CBaseEntity *pEntity, bool bMovingToLinkedSimulator /*= false*/ ) +{ + if( pEntity == NULL ) + return; + + if( pEntity->IsWorld() ) + return; + + if( !OwnsEntity( pEntity ) ) + return; + + if( m_InternalData.Simulation.pPhysicsEnvironment ) + ReleasePhysicsOwnership( pEntity, true, bMovingToLinkedSimulator ); + + m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_IS_IN_PORTAL_HOLE; + UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] ); + + Assert( GetSimulatorThatOwnsEntity( pEntity ) == this ); + MarkAsReleased( pEntity ); + Assert( GetSimulatorThatOwnsEntity( pEntity ) == NULL ); + + for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + { + if( m_InternalData.Simulation.Dynamic.OwnedEntities[i] == pEntity ) + { + m_InternalData.Simulation.Dynamic.OwnedEntities.FastRemove(i); + break; + } + } + + if( bMovingToLinkedSimulator == false ) + { + RecheckEntityCollision( pEntity ); + } + + m_pCallbacks->PortalSimulator_ReleasedOwnershipOfEntity( pEntity ); + + CUtlVector<CBaseEntity *> childrenList; + GetAllChildren( pEntity, childrenList ); + for ( int i = childrenList.Count(); --i >= 0; ) + ReleaseOwnershipOfEntity( childrenList[i] ); +} + +void CPortalSimulator::ReleaseAllEntityOwnership( void ) +{ + //Assert( m_bLocalDataIsReady || (m_InternalData.Simulation.Dynamic.OwnedEntities.Count() == 0) ); + int iSkippedObjects = 0; + while( m_InternalData.Simulation.Dynamic.OwnedEntities.Count() != iSkippedObjects ) //the release function changes OwnedEntities + { + CBaseEntity *pEntity = m_InternalData.Simulation.Dynamic.OwnedEntities[iSkippedObjects]; + if( CPhysicsShadowClone::IsShadowClone( pEntity ) || + CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) + { + ++iSkippedObjects; + continue; + } + RemoveEntityFromPortalHole( pEntity ); //assume that whenever someone wants to release all entities, it's because the portal is going away + ReleaseOwnershipOfEntity( pEntity ); + } + + Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); +} + + +void CPortalSimulator::ReleasePhysicsOwnership( CBaseEntity *pEntity, bool bContinuePhysicsCloning /*= true*/, bool bMovingToLinkedSimulator /*= false*/ ) +{ + if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) + return; + + Assert( OwnsEntity( pEntity ) ); //releasing physics ownership happens BEFORE releasing general ownership + Assert( CPhysicsShadowClone::IsShadowClone( pEntity ) == false ); + + if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) + return; + + if( !OwnsPhysicsForEntity( pEntity ) ) + return; + + if( IsSimulatingVPhysics() == false ) + bContinuePhysicsCloning = false; + + int iEntIndex = pEntity->entindex(); + m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_OWNS_PHYSICS; + + //physics cloning + { +#ifdef _DEBUG + { + int iDebugIndex; + for( iDebugIndex = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) + { + if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) + break; + } + AssertMsg( iDebugIndex < 0, "Trying to release an entity, when a clone from the linked portal already exists." ); + } +#endif + + //clear exported clones + { + DBG_CODE_NOSCOPE( bool bFoundAlready = false; ); + DBG_CODE_NOSCOPE( const char *szLastFoundMarker = NULL; ); + + //to linked portal + if( m_pLinkedPortal ) + { + DBG_CODE_NOSCOPE( bFoundAlready = false; ); + for( int i = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) + { + if( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]->GetClonedEntity() == pEntity ) + { + CPhysicsShadowClone *pClone = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; + AssertMsg( bFoundAlready == false, "Multiple clones to linked portal found." ); + DBG_CODE_NOSCOPE( bFoundAlready = true; ); + DBG_CODE_NOSCOPE( szLastFoundMarker = pClone->m_szDebugMarker ); + + //bool bHeldByPhyscannon = false; + CBaseEntity *pHeldEntity = NULL; + CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pEntity ); + + if ( !pPlayer && pEntity->IsPlayer() ) + { + pPlayer = (CPortal_Player *)pEntity; + } + + if ( pPlayer ) + { + pHeldEntity = GetPlayerHeldEntity( pPlayer ); + /*if ( !pHeldEntity ) + { + pHeldEntity = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() ); + bHeldByPhyscannon = true; + }*/ + } + + if( pHeldEntity ) + { + //player is holding the entity, force them to pick it back up again + bool bIsHeldObjectOnOppositeSideOfPortal = pPlayer->IsHeldObjectOnOppositeSideOfPortal(); + pPlayer->m_bSilentDropAndPickup = true; + pPlayer->ForceDropOfCarriedPhysObjects( pHeldEntity ); + pPlayer->SetHeldObjectOnOppositeSideOfPortal( bIsHeldObjectOnOppositeSideOfPortal ); + } + else + { + pHeldEntity = NULL; + } + + m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; + m_pLinkedPortal->MarkAsReleased( pClone ); + pClone->Free(); + m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.FastRemove(i); + + if( pHeldEntity ) + { + /*if ( bHeldByPhyscannon ) + { + PhysCannonPickupObject( pPlayer, pHeldEntity ); + } + else*/ + { + PlayerPickupObject( pPlayer, pHeldEntity ); + } + pPlayer->m_bSilentDropAndPickup = false; + } + + DBG_CODE_NOSCOPE( continue; ); + break; + } + } + } + } + } + + m_pCallbacks->PortalSimulator_ReleasedPhysicsOwnershipOfEntity( pEntity ); +} + +void CPortalSimulator::StartCloningEntity( CBaseEntity *pEntity ) +{ + if( CPhysicsShadowClone::IsShadowClone( pEntity ) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) + return; + + if( (m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) != 0 ) + return; //already cloned, no work to do + +#ifdef _DEBUG + for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Count(); --i >= 0; ) + Assert( m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain[i] != pEntity ); +#endif + + //NDebugOverlay::EntityBounds( pEntity, 0, 255, 0, 50, 5.0f ); + + m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.AddToTail( pEntity ); + m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] |= PSEF_CLONES_ENTITY_FROM_MAIN; +} + +void CPortalSimulator::StopCloningEntity( CBaseEntity *pEntity ) +{ + if( (m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) == 0 ) + { + Assert( m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Find( pEntity ) == -1 ); + return; //not cloned, no work to do + } + + //NDebugOverlay::EntityBounds( pEntity, 255, 0, 0, 50, 5.0f ); + + m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.FastRemove(m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Find( pEntity )); + m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_CLONES_ENTITY_FROM_MAIN; +} + + +/*void CPortalSimulator::TeleportEntityToLinkedPortal( CBaseEntity *pEntity ) +{ + //TODO: migrate teleportation code from CProp_Portal::Touch to here + + +}*/ + + +void CPortalSimulator::MarkAsOwned( CBaseEntity *pEntity ) +{ + Assert( pEntity != NULL ); + int iEntIndex = pEntity->entindex(); + Assert( s_OwnedEntityMap[iEntIndex] == NULL ); +#ifdef _DEBUG + for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + Assert( m_InternalData.Simulation.Dynamic.OwnedEntities[i] != pEntity ); +#endif + Assert( (m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY) == 0 ); + + m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_OWNS_ENTITY; + s_OwnedEntityMap[iEntIndex] = this; + m_InternalData.Simulation.Dynamic.OwnedEntities.AddToTail( pEntity ); + + if ( pEntity->IsPlayer() ) + { + g_bPlayerIsInSimulator = true; + } +} + +void CPortalSimulator::MarkAsReleased( CBaseEntity *pEntity ) +{ + Assert( pEntity != NULL ); + int iEntIndex = pEntity->entindex(); + Assert( s_OwnedEntityMap[iEntIndex] == this ); + Assert( ((m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY) != 0) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity(pEntity) ); + + s_OwnedEntityMap[iEntIndex] = NULL; + m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_OWNS_ENTITY; + int i; + for( i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + { + if( m_InternalData.Simulation.Dynamic.OwnedEntities[i] == pEntity ) + { + m_InternalData.Simulation.Dynamic.OwnedEntities.FastRemove(i); + break; + } + } + Assert( i >= 0 ); + + + if ( pEntity->IsPlayer() ) + { + g_bPlayerIsInSimulator = false; + } +} + + + +void CPortalSimulator::CreateAllPhysics( void ) +{ + if( IsSimulatingVPhysics() == false ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + CreateMinimumPhysics(); + CreateLocalPhysics(); + CreateLinkedPhysics(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::CreateMinimumPhysics( void ) +{ + if( IsSimulatingVPhysics() == false ) + return; + + if( m_InternalData.Simulation.pPhysicsEnvironment != NULL ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateMinimumPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + m_InternalData.Simulation.pPhysicsEnvironment = physenv_main; + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateMinimumPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::CreateLocalPhysics( void ) +{ + if( IsSimulatingVPhysics() == false ) + return; + + AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create local physics before being placed." ); + + if( m_CreationChecklist.bLocalPhysicsGenerated ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + CreateMinimumPhysics(); + + //int iDefaultSurfaceIndex = physprops->GetSurfaceIndex( "default" ); + objectparams_t params = g_PhysDefaultObjectParams; + + // Any non-moving object can point to world safely-- Make sure we dont use 'params' for something other than that beyond this point. + if( m_InternalData.Simulation.pCollisionEntity ) + params.pGameData = m_InternalData.Simulation.pCollisionEntity; + else + GetWorldEntity(); + + //World + { + Assert( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.World.Brushes.pCollideable != NULL ) + { + m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.World.Brushes.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); + + if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) + m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); + + m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + + //Assert( m_InternalData.Simulation.Static.World.StaticProps.PhysicsObjects.Count() == 0 ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation +#ifdef _DEBUG + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + } +#endif + + if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0 ) + { + Assert( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists ); + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; + Assert( Representation.pCollide != NULL ); + Assert( Representation.pPhysicsObject == NULL ); + + Representation.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( Representation.pCollide, Representation.iTraceSurfaceProps, vec3_origin, vec3_angle, ¶ms ); + Assert( Representation.pPhysicsObject != NULL ); + Representation.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + } + m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists = true; + } + + //Wall + { + Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable != NULL ) + { + m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); + + if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) + m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); + + m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + + Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable != NULL ) + { + m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); + + if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) + m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); + + m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + } + + //re-acquire environment physics for owned entities + for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + TakePhysicsOwnership( m_InternalData.Simulation.Dynamic.OwnedEntities[i] ); + + if( m_InternalData.Simulation.pCollisionEntity ) + m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLocalPhysicsGenerated = true; +} + + + +void CPortalSimulator::CreateLinkedPhysics( void ) +{ + if( IsSimulatingVPhysics() == false ) + return; + + AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create linked physics before being placed itself." ); + + if( (m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_bLocalDataIsReady == false) ) + return; + + if( m_CreationChecklist.bLinkedPhysicsGenerated ) + return; + + CREATEDEBUGTIMER( functionTimer ); + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLinkedPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + CreateMinimumPhysics(); + + //int iDefaultSurfaceIndex = physprops->GetSurfaceIndex( "default" ); + objectparams_t params = g_PhysDefaultObjectParams; + + if( m_InternalData.Simulation.pCollisionEntity ) + params.pGameData = m_InternalData.Simulation.pCollisionEntity; + else + params.pGameData = GetWorldEntity(); + + //everything in our linked collision should be based on the linked portal's world collision + PS_SD_Static_World_t &RemoteSimulationStaticWorld = m_pLinkedPortal->m_InternalData.Simulation.Static.World; + + Assert( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( RemoteSimulationStaticWorld.Brushes.pCollideable != NULL ) + { + m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( RemoteSimulationStaticWorld.Brushes.pCollideable, m_pLinkedPortal->m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform, m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, ¶ms ); + m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + + + Assert( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count() == 0 ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations.Count() != 0 ) + { + for( int i = RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations[i]; + IPhysicsObject *pPhysObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( Representation.pCollide, Representation.iTraceSurfaceProps, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform, m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, ¶ms ); + if( pPhysObject ) + { + m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.AddToTail( pPhysObject ); + pPhysObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class + } + } + } + + //re-clone physicsshadowclones from the remote environment + CUtlVector<CBaseEntity *> &RemoteOwnedEntities = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.OwnedEntities; + for( int i = RemoteOwnedEntities.Count(); --i >= 0; ) + { + if( CPhysicsShadowClone::IsShadowClone( RemoteOwnedEntities[i] ) || + CPSCollisionEntity::IsPortalSimulatorCollisionEntity( RemoteOwnedEntities[i] ) ) + continue; + + int j; + for( j = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --j >= 0; ) + { + if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[j]->GetClonedEntity() == RemoteOwnedEntities[i] ) + break; + } + + if( j >= 0 ) //already cloning + continue; + + + + EHANDLE hEnt = RemoteOwnedEntities[i]; + CPhysicsShadowClone *pClone = CPhysicsShadowClone::CreateShadowClone( m_InternalData.Simulation.pPhysicsEnvironment, hEnt, "CPortalSimulator::CreateLinkedPhysics(): From Linked Portal", &m_InternalData.Placement.matLinkedToThis.As3x4() ); + if( pClone ) + { + MarkAsOwned( pClone ); + m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= PSEF_OWNS_PHYSICS; + m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.AddToTail( pClone ); + pClone->CollisionRulesChanged(); //adding the clone to the portal simulator changes how it collides + } + } + + if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) + { + Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions + m_bInCrossLinkedFunction = true; + m_pLinkedPortal->CreateLinkedPhysics(); + m_bInCrossLinkedFunction = false; + } + + if( m_InternalData.Simulation.pCollisionEntity ) + m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLinkedPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLinkedPhysicsGenerated = true; +} + + + +void CPortalSimulator::ClearAllPhysics( void ) +{ + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + ClearLinkedPhysics(); + ClearLocalPhysics(); + ClearMinimumPhysics(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::ClearMinimumPhysics( void ) +{ + if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearMinimumPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + m_InternalData.Simulation.pPhysicsEnvironment = NULL; + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearMinimumPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::ClearLocalPhysics( void ) +{ + if( m_CreationChecklist.bLocalPhysicsGenerated == false ) + return; + + if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); + m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( true ); //if we don't do this, things crash the next time we cleanup the delete list while checking mindists + + if( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject ) + { + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject ); + m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject = NULL; + } + + if( m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists && + (m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0) ) + { + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; + if( Representation.pPhysicsObject ) + { + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( Representation.pPhysicsObject ); + Representation.pPhysicsObject = NULL; + } + } + } + m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists = false; + + if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject ) + { + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject ); + m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject = NULL; + } + + if( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ) + { + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ); + m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject = NULL; + } + + //all physics clones + { + for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) + { + CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; + Assert( GetSimulatorThatOwnsEntity( pClone ) == this ); + m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; + MarkAsReleased( pClone ); + Assert( GetSimulatorThatOwnsEntity( pClone ) == NULL ); + pClone->Free(); + } + + m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); + } + + Assert( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0 ); + + //release physics ownership of owned entities + for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) + ReleasePhysicsOwnership( m_InternalData.Simulation.Dynamic.OwnedEntities[i], false ); + + Assert( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0 ); + + m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); + m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( false ); + + if( m_InternalData.Simulation.pCollisionEntity ) + m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLocalPhysicsGenerated = false; +} + + + +void CPortalSimulator::ClearLinkedPhysics( void ) +{ + if( m_CreationChecklist.bLinkedPhysicsGenerated == false ) + return; + + if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLinkedPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); + m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( true ); //if we don't do this, things crash the next time we cleanup the delete list while checking mindists + + //static collideables + { + if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ) + { + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ); + m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject = NULL; + } + + if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count() ) + { + for( int i = m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count(); --i >= 0; ) + m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects[i] ); + + m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.RemoveAll(); + } + } + + //clones from the linked portal + { + for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) + { + CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; + m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; + MarkAsReleased( pClone ); + pClone->Free(); + } + + m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); + } + + + if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) + { + Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions + m_bInCrossLinkedFunction = true; + m_pLinkedPortal->ClearLinkedPhysics(); + m_bInCrossLinkedFunction = false; + } + + Assert( (m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0) && + ((m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0)) ); + + m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); + m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( false ); + + if( m_InternalData.Simulation.pCollisionEntity ) + m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLinkedPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLinkedPhysicsGenerated = false; +} + + +void CPortalSimulator::ClearLinkedEntities( void ) +{ + //clones from the linked portal + { + for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) + { + CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; + m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; + MarkAsReleased( pClone ); + pClone->Free(); + } + + m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); + } +} +#endif //#ifndef CLIENT_DLL + + +void CPortalSimulator::CreateAllCollision( void ) +{ + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + CreateLocalCollision(); + CreateLinkedCollision(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::CreateLocalCollision( void ) +{ + AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create local collision before being placed." ); + + if( m_CreationChecklist.bLocalCollisionGenerated ) + return; + + if( IsCollisionGenerationEnabled() == false ) + return; + + DEBUGTIMERONLY( s_iPortalSimulatorGUID = GetPortalSimulatorGUID() ); + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + CREATEDEBUGTIMER( worldBrushTimer ); + STARTDEBUGTIMER( worldBrushTimer ); + Assert( m_InternalData.Simulation.Static.World.Brushes.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() != 0 ) + m_InternalData.Simulation.Static.World.Brushes.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Base(), m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() ); + STOPDEBUGTIMER( worldBrushTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWorld Brushes=%fms\n", GetPortalSimulatorGUID(), TABSPACING, worldBrushTimer.GetDuration().GetMillisecondsF() ); ); + + CREATEDEBUGTIMER( worldPropTimer ); + STARTDEBUGTIMER( worldPropTimer ); +#ifdef _DEBUG + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide == NULL ); + } +#endif + Assert( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists == false ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0 ) + { + Assert( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() != 0 ); + CPolyhedron **pPolyhedronsBase = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Base(); + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; + + Assert( Representation.pCollide == NULL ); + Representation.pCollide = ConvertPolyhedronsToCollideable( &pPolyhedronsBase[Representation.PolyhedronGroup.iStartIndex], Representation.PolyhedronGroup.iNumPolyhedrons ); + Assert( Representation.pCollide != NULL ); + } + } + m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists = true; + STOPDEBUGTIMER( worldPropTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWorld Props=%fms\n", GetPortalSimulatorGUID(), TABSPACING, worldPropTimer.GetDuration().GetMillisecondsF() ); ); + + if( IsSimulatingVPhysics() ) + { + //only need the tube when simulating player movement + + //TODO: replace the complete wall with the wall shell + CREATEDEBUGTIMER( wallBrushTimer ); + STARTDEBUGTIMER( wallBrushTimer ); + Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() != 0 ) + m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Base(), m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() ); + STOPDEBUGTIMER( wallBrushTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWall Brushes=%fms\n", GetPortalSimulatorGUID(), TABSPACING, wallBrushTimer.GetDuration().GetMillisecondsF() ); ); + } + + CREATEDEBUGTIMER( wallTubeTimer ); + STARTDEBUGTIMER( wallTubeTimer ); + Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation + if( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() != 0 ) + m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Base(), m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() ); + STOPDEBUGTIMER( wallTubeTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWall Tube=%fms\n", GetPortalSimulatorGUID(), TABSPACING, wallTubeTimer.GetDuration().GetMillisecondsF() ); ); + + //grab surface properties to use for the portal environment + { + CTraceFilterWorldAndPropsOnly filter; + trace_t Trace; + UTIL_TraceLine( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vForward, m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vForward * 500.0f), MASK_SOLID_BRUSHONLY, &filter, &Trace ); + + if( Trace.fraction != 1.0f ) + { + m_InternalData.Simulation.Static.SurfaceProperties.contents = Trace.contents; + m_InternalData.Simulation.Static.SurfaceProperties.surface = Trace.surface; + m_InternalData.Simulation.Static.SurfaceProperties.pEntity = Trace.m_pEnt; + } + else + { + m_InternalData.Simulation.Static.SurfaceProperties.contents = CONTENTS_SOLID; + m_InternalData.Simulation.Static.SurfaceProperties.surface.name = "**empty**"; + m_InternalData.Simulation.Static.SurfaceProperties.surface.flags = 0; + m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps = 0; +#ifndef CLIENT_DLL + m_InternalData.Simulation.Static.SurfaceProperties.pEntity = GetWorldEntity(); +#else + m_InternalData.Simulation.Static.SurfaceProperties.pEntity = GetClientWorldEntity(); +#endif + } + +#ifndef CLIENT_DLL + if( m_InternalData.Simulation.pCollisionEntity ) + m_InternalData.Simulation.Static.SurfaceProperties.pEntity = m_InternalData.Simulation.pCollisionEntity; +#endif + } + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLocalCollisionGenerated = true; +} + + + +void CPortalSimulator::CreateLinkedCollision( void ) +{ + if( m_CreationChecklist.bLinkedCollisionGenerated ) + return; + + if( IsCollisionGenerationEnabled() == false ) + return; + + //nothing to do for now, the current set of collision is just transformed from the linked simulator when needed. It's really cheap to transform in traces and physics generation. + + m_CreationChecklist.bLinkedCollisionGenerated = true; +} + + + +void CPortalSimulator::ClearAllCollision( void ) +{ + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + ClearLinkedCollision(); + ClearLocalCollision(); + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + + +void CPortalSimulator::ClearLinkedCollision( void ) +{ + if( m_CreationChecklist.bLinkedCollisionGenerated == false ) + return; + + //nothing to do for now, the current set of collision is just transformed from the linked simulator when needed. It's really cheap to transform in traces and physics generation. + + m_CreationChecklist.bLinkedCollisionGenerated = false; +} + + + +void CPortalSimulator::ClearLocalCollision( void ) +{ + if( m_CreationChecklist.bLocalCollisionGenerated == false ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable ) + { + physcollision->DestroyCollide( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable ); + m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable = NULL; + } + + if( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable ) + { + physcollision->DestroyCollide( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable ); + m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable = NULL; + } + + if( m_InternalData.Simulation.Static.World.Brushes.pCollideable ) + { + physcollision->DestroyCollide( m_InternalData.Simulation.Static.World.Brushes.pCollideable ); + m_InternalData.Simulation.Static.World.Brushes.pCollideable = NULL; + } + + if( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists && + (m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0) ) + { + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; + if( Representation.pCollide ) + { + physcollision->DestroyCollide( Representation.pCollide ); + Representation.pCollide = NULL; + } + } + } + m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists = false; + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bLocalCollisionGenerated = false; +} + + + +void CPortalSimulator::CreatePolyhedrons( void ) +{ + if( m_CreationChecklist.bPolyhedronsGenerated ) + return; + + if( IsCollisionGenerationEnabled() == false ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreatePolyhedrons() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + //forward reverse conventions signify whether the normal is the same direction as m_InternalData.Placement.PortalPlane.m_Normal + //World and wall conventions signify whether it's been shifted in front of the portal plane or behind it + + float fWorldClipPlane_Forward[4] = { m_InternalData.Placement.PortalPlane.m_Normal.x, + m_InternalData.Placement.PortalPlane.m_Normal.y, + m_InternalData.Placement.PortalPlane.m_Normal.z, + m_InternalData.Placement.PortalPlane.m_Dist + PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT }; + + float fWorldClipPlane_Reverse[4] = { -fWorldClipPlane_Forward[0], + -fWorldClipPlane_Forward[1], + -fWorldClipPlane_Forward[2], + -fWorldClipPlane_Forward[3] }; + + float fWallClipPlane_Forward[4] = { m_InternalData.Placement.PortalPlane.m_Normal.x, + m_InternalData.Placement.PortalPlane.m_Normal.y, + m_InternalData.Placement.PortalPlane.m_Normal.z, + m_InternalData.Placement.PortalPlane.m_Dist }; // - PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT + + //float fWallClipPlane_Reverse[4] = { -fWallClipPlane_Forward[0], + // -fWallClipPlane_Forward[1], + // -fWallClipPlane_Forward[2], + // -fWallClipPlane_Forward[3] }; + + + //World + { + Vector vOBBForward = m_InternalData.Placement.vForward; + Vector vOBBRight = m_InternalData.Placement.vRight; + Vector vOBBUp = m_InternalData.Placement.vUp; + + + //scale the extents to usable sizes + float flScaleX = sv_portal_collision_sim_bounds_x.GetFloat(); + if ( flScaleX < 200.0f ) + flScaleX = 200.0f; + float flScaleY = sv_portal_collision_sim_bounds_y.GetFloat(); + if ( flScaleY < 200.0f ) + flScaleY = 200.0f; + float flScaleZ = sv_portal_collision_sim_bounds_z.GetFloat(); + if ( flScaleZ < 252.0f ) + flScaleZ = 252.0f; + + vOBBForward *= flScaleX; + vOBBRight *= flScaleY; + vOBBUp *= flScaleZ; // default size for scale z (252) is player (height + portal half height) * 2. Any smaller than this will allow for players to + // reach unsimulated geometry before an end touch with teh portal. + + Vector ptOBBOrigin = m_InternalData.Placement.ptCenter; + ptOBBOrigin -= vOBBRight / 2.0f; + ptOBBOrigin -= vOBBUp / 2.0f; + + Vector vAABBMins, vAABBMaxs; + vAABBMins = vAABBMaxs = ptOBBOrigin; + + for( int i = 1; i != 8; ++i ) + { + Vector ptTest = ptOBBOrigin; + if( i & (1 << 0) ) ptTest += vOBBForward; + if( i & (1 << 1) ) ptTest += vOBBRight; + if( i & (1 << 2) ) ptTest += vOBBUp; + + if( ptTest.x < vAABBMins.x ) vAABBMins.x = ptTest.x; + if( ptTest.y < vAABBMins.y ) vAABBMins.y = ptTest.y; + if( ptTest.z < vAABBMins.z ) vAABBMins.z = ptTest.z; + if( ptTest.x > vAABBMaxs.x ) vAABBMaxs.x = ptTest.x; + if( ptTest.y > vAABBMaxs.y ) vAABBMaxs.y = ptTest.y; + if( ptTest.z > vAABBMaxs.z ) vAABBMaxs.z = ptTest.z; + } + + //Brushes + { + Assert( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() == 0 ); + + CUtlVector<int> WorldBrushes; + enginetrace->GetBrushesInAABB( vAABBMins, vAABBMaxs, &WorldBrushes, MASK_SOLID_BRUSHONLY|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP ); + + //create locally clipped polyhedrons for the world + { + int *pBrushList = WorldBrushes.Base(); + int iBrushCount = WorldBrushes.Count(); + ConvertBrushListToClippedPolyhedronList( pBrushList, iBrushCount, fWorldClipPlane_Reverse, 1, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.World.Brushes.Polyhedrons ); + } + } + + //static props + { + Assert( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() == 0 ); + + CUtlVector<ICollideable *> StaticProps; + staticpropmgr->GetAllStaticPropsInAABB( vAABBMins, vAABBMaxs, &StaticProps ); + + for( int i = StaticProps.Count(); --i >= 0; ) + { + ICollideable *pProp = StaticProps[i]; + + CPolyhedron *PolyhedronArray[1024]; + int iPolyhedronCount = g_StaticCollisionPolyhedronCache.GetStaticPropPolyhedrons( pProp, PolyhedronArray, 1024 ); + + StaticPropPolyhedronGroups_t indices; + indices.iStartIndex = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count(); + + for( int j = 0; j != iPolyhedronCount; ++j ) + { + CPolyhedron *pPropPolyhedronPiece = PolyhedronArray[j]; + if( pPropPolyhedronPiece ) + { + CPolyhedron *pClippedPropPolyhedron = ClipPolyhedron( pPropPolyhedronPiece, fWorldClipPlane_Reverse, 1, 0.01f, false ); + if( pClippedPropPolyhedron ) + m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.AddToTail( pClippedPropPolyhedron ); + } + } + + indices.iNumPolyhedrons = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() - indices.iStartIndex; + if( indices.iNumPolyhedrons != 0 ) + { + int index = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.AddToTail(); + PS_SD_Static_World_StaticProps_ClippedProp_t &NewEntry = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[index]; + + NewEntry.PolyhedronGroup = indices; + NewEntry.pCollide = NULL; +#ifndef CLIENT_DLL + NewEntry.pPhysicsObject = NULL; +#endif + NewEntry.pSourceProp = pProp->GetEntityHandle(); + + const model_t *pModel = pProp->GetCollisionModel(); + bool bIsStudioModel = pModel && (modelinfo->GetModelType( pModel ) == mod_studio); + AssertOnce( bIsStudioModel ); + if( bIsStudioModel ) + { + studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pModel ); + Assert( pStudioHdr != NULL ); + NewEntry.iTraceContents = pStudioHdr->contents; + NewEntry.iTraceSurfaceProps = physprops->GetSurfaceIndex( pStudioHdr->pszSurfaceProp() ); + } + else + { + NewEntry.iTraceContents = m_InternalData.Simulation.Static.SurfaceProperties.contents; + NewEntry.iTraceSurfaceProps = m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps; + } + } + } + } + } + + + + //(Holy) Wall + { + Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() == 0 ); + Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() == 0 ); + + Vector vBackward = -m_InternalData.Placement.vForward; + Vector vLeft = -m_InternalData.Placement.vRight; + Vector vDown = -m_InternalData.Placement.vUp; + + Vector vOBBForward = -m_InternalData.Placement.vForward; + Vector vOBBRight = -m_InternalData.Placement.vRight; + Vector vOBBUp = m_InternalData.Placement.vUp; + + //scale the extents to usable sizes + vOBBForward *= PORTAL_WALL_FARDIST / 2.0f; + vOBBRight *= PORTAL_WALL_FARDIST * 2.0f; + vOBBUp *= PORTAL_WALL_FARDIST * 2.0f; + + Vector ptOBBOrigin = m_InternalData.Placement.ptCenter; + ptOBBOrigin -= vOBBRight / 2.0f; + ptOBBOrigin -= vOBBUp / 2.0f; + + Vector vAABBMins, vAABBMaxs; + vAABBMins = vAABBMaxs = ptOBBOrigin; + + for( int i = 1; i != 8; ++i ) + { + Vector ptTest = ptOBBOrigin; + if( i & (1 << 0) ) ptTest += vOBBForward; + if( i & (1 << 1) ) ptTest += vOBBRight; + if( i & (1 << 2) ) ptTest += vOBBUp; + + if( ptTest.x < vAABBMins.x ) vAABBMins.x = ptTest.x; + if( ptTest.y < vAABBMins.y ) vAABBMins.y = ptTest.y; + if( ptTest.z < vAABBMins.z ) vAABBMins.z = ptTest.z; + if( ptTest.x > vAABBMaxs.x ) vAABBMaxs.x = ptTest.x; + if( ptTest.y > vAABBMaxs.y ) vAABBMaxs.y = ptTest.y; + if( ptTest.z > vAABBMaxs.z ) vAABBMaxs.z = ptTest.z; + } + + + float fPlanes[6 * 4]; + + //first and second planes are always forward and backward planes + fPlanes[(0*4) + 0] = fWallClipPlane_Forward[0]; + fPlanes[(0*4) + 1] = fWallClipPlane_Forward[1]; + fPlanes[(0*4) + 2] = fWallClipPlane_Forward[2]; + fPlanes[(0*4) + 3] = fWallClipPlane_Forward[3] - PORTAL_WALL_TUBE_OFFSET; + + fPlanes[(1*4) + 0] = vBackward.x; + fPlanes[(1*4) + 1] = vBackward.y; + fPlanes[(1*4) + 2] = vBackward.z; + float fTubeDepthDist = vBackward.Dot( m_InternalData.Placement.ptCenter + (vBackward * (PORTAL_WALL_TUBE_DEPTH + PORTAL_WALL_TUBE_OFFSET)) ); + fPlanes[(1*4) + 3] = fTubeDepthDist; + + + //the remaining planes will always have the same ordering of normals, with different distances plugged in for each convex we're creating + //normal order is up, down, left, right + + fPlanes[(2*4) + 0] = m_InternalData.Placement.vUp.x; + fPlanes[(2*4) + 1] = m_InternalData.Placement.vUp.y; + fPlanes[(2*4) + 2] = m_InternalData.Placement.vUp.z; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT) ); + + fPlanes[(3*4) + 0] = vDown.x; + fPlanes[(3*4) + 1] = vDown.y; + fPlanes[(3*4) + 2] = vDown.z; + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); + + fPlanes[(4*4) + 0] = vLeft.x; + fPlanes[(4*4) + 1] = vLeft.y; + fPlanes[(4*4) + 2] = vLeft.z; + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + (vLeft * PORTAL_HOLE_HALF_WIDTH) ); + + fPlanes[(5*4) + 0] = m_InternalData.Placement.vRight.x; + fPlanes[(5*4) + 1] = m_InternalData.Placement.vRight.y; + fPlanes[(5*4) + 2] = m_InternalData.Placement.vRight.z; + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vRight * PORTAL_HOLE_HALF_WIDTH) ); + + float *fSidePlanesOnly = &fPlanes[(2*4)]; + + //these 2 get re-used a bit + float fFarRightPlaneDistance = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_WALL_FARDIST * 10.0f) ); + float fFarLeftPlaneDistance = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_WALL_FARDIST * 10.0f) ); + + + CUtlVector<int> WallBrushes; + CUtlVector<CPolyhedron *> WallBrushPolyhedrons_ClippedToWall; + CPolyhedron **pWallClippedPolyhedrons = NULL; + int iWallClippedPolyhedronCount = 0; + if( IsSimulatingVPhysics() ) //if not simulating vphysics, we skip making the entire wall, and just create the minimal tube instead + { + enginetrace->GetBrushesInAABB( vAABBMins, vAABBMaxs, &WallBrushes, MASK_SOLID_BRUSHONLY ); + + if( WallBrushes.Count() != 0 ) + ConvertBrushListToClippedPolyhedronList( WallBrushes.Base(), WallBrushes.Count(), fPlanes, 1, PORTAL_POLYHEDRON_CUT_EPSILON, &WallBrushPolyhedrons_ClippedToWall ); + + if( WallBrushPolyhedrons_ClippedToWall.Count() != 0 ) + { + for( int i = WallBrushPolyhedrons_ClippedToWall.Count(); --i >= 0; ) + { + CPolyhedron *pPolyhedron = ClipPolyhedron( WallBrushPolyhedrons_ClippedToWall[i], fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, true ); + if( pPolyhedron ) + { + //a chunk of this brush passes through the hole, not eligible to be removed from cutting + pPolyhedron->Release(); + } + else + { + //no part of this brush interacts with the hole, no point in cutting the brush any later + m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.AddToTail( WallBrushPolyhedrons_ClippedToWall[i] ); + WallBrushPolyhedrons_ClippedToWall.FastRemove( i ); + } + } + + if( WallBrushPolyhedrons_ClippedToWall.Count() != 0 ) //might have become 0 while removing uncut brushes + { + pWallClippedPolyhedrons = WallBrushPolyhedrons_ClippedToWall.Base(); + iWallClippedPolyhedronCount = WallBrushPolyhedrons_ClippedToWall.Count(); + } + } + } + + + //upper wall + { + //minimal portion that extends into the hole space + //fPlanes[(1*4) + 3] = fTubeDepthDist; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT ); + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + + CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); + if( pTubePolyhedron ) + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); + + //general hole cut + //fPlanes[(1*4) + 3] += 2000.0f; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_WALL_FARDIST * 10.0f) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; + fPlanes[(5*4) + 3] = fFarRightPlaneDistance; + + + + ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); + } + + //lower wall + { + //minimal portion that extends into the hole space + //fPlanes[(1*4) + 3] = fTubeDepthDist; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + + CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); + if( pTubePolyhedron ) + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); + + //general hole cut + //fPlanes[(1*4) + 3] += 2000.0f; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_WALL_FARDIST * 10.0f)) ); + fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; + fPlanes[(5*4) + 3] = fFarRightPlaneDistance; + + ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); + } + + //left wall + { + //minimal portion that extends into the hole space + //fPlanes[(1*4) + 3] = fTubeDepthDist; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + (vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (vLeft * PORTAL_HOLE_HALF_WIDTH) ); + + CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); + if( pTubePolyhedron ) + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); + + //general hole cut + //fPlanes[(1*4) + 3] += 2000.0f; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS)) ); + + ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); + } + + //right wall + { + //minimal portion that extends into the hole space + //fPlanes[(1*4) + 3] = fTubeDepthDist; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT)) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT)) ); + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * PORTAL_HOLE_HALF_WIDTH ); + fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + + CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); + if( pTubePolyhedron ) + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); + + //general hole cut + //fPlanes[(1*4) + 3] += 2000.0f; + fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); + fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); + fPlanes[(5*4) + 3] = fFarRightPlaneDistance; + + ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); + } + + for( int i = WallBrushPolyhedrons_ClippedToWall.Count(); --i >= 0; ) + WallBrushPolyhedrons_ClippedToWall[i]->Release(); + + WallBrushPolyhedrons_ClippedToWall.RemoveAll(); + } + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreatePolyhedrons() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bPolyhedronsGenerated = true; +} + + + +void CPortalSimulator::ClearPolyhedrons( void ) +{ + if( m_CreationChecklist.bPolyhedronsGenerated == false ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearPolyhedrons() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + if( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() != 0 ) + { + for( int i = m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count(); --i >= 0; ) + m_InternalData.Simulation.Static.World.Brushes.Polyhedrons[i]->Release(); + + m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.RemoveAll(); + } + + if( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() != 0 ) + { + for( int i = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count(); --i >= 0; ) + m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons[i]->Release(); + + m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.RemoveAll(); + } +#ifdef _DEBUG + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { +#ifndef CLIENT_DLL + Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == NULL ); +#endif + Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide == NULL ); + } +#endif + m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.RemoveAll(); + + if( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() != 0 ) + { + for( int i = m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count(); --i >= 0; ) + m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons[i]->Release(); + + m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.RemoveAll(); + } + + if( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() != 0 ) + { + for( int i = m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count(); --i >= 0; ) + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons[i]->Release(); + + m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.RemoveAll(); + } + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearPolyhedrons() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + m_CreationChecklist.bPolyhedronsGenerated = false; +} + + + +void CPortalSimulator::DetachFromLinked( void ) +{ + if( m_pLinkedPortal == NULL ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DetachFromLinked() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + //IMPORTANT: Physics objects must be destroyed before their associated collision data or a fairly cryptic crash will ensue +#ifndef CLIENT_DLL + ClearLinkedEntities(); + ClearLinkedPhysics(); +#endif + ClearLinkedCollision(); + + if( m_pLinkedPortal->m_bInCrossLinkedFunction == false ) + { + Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions + m_bInCrossLinkedFunction = true; + m_pLinkedPortal->DetachFromLinked(); + m_bInCrossLinkedFunction = false; + } + + m_pLinkedPortal = NULL; + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DetachFromLinked() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + +void CPortalSimulator::SetPortalSimulatorCallbacks( CPortalSimulatorEventCallbacks *pCallbacks ) +{ + if( pCallbacks ) + m_pCallbacks = pCallbacks; + else + m_pCallbacks = &s_DummyPortalSimulatorCallback; //always keep the pointer valid +} + + + + +void CPortalSimulator::SetVPhysicsSimulationEnabled( bool bEnabled ) +{ + AssertMsg( (m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_bSimulateVPhysics == m_bSimulateVPhysics), "Linked portals are in disagreement as to whether they would simulate VPhysics." ); + + if( bEnabled == m_bSimulateVPhysics ) + return; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::SetVPhysicsSimulationEnabled() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); + INCREMENTTABSPACING(); + + m_bSimulateVPhysics = bEnabled; + if( bEnabled ) + { + //we took some local collision shortcuts when generating while physics simulation is off, regenerate + ClearLocalCollision(); + ClearPolyhedrons(); + CreatePolyhedrons(); + CreateLocalCollision(); +#ifndef CLIENT_DLL + CreateAllPhysics(); +#endif + } +#ifndef CLIENT_DLL + else + { + ClearAllPhysics(); + } +#endif + + if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) + { + Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions + m_bInCrossLinkedFunction = true; + m_pLinkedPortal->SetVPhysicsSimulationEnabled( bEnabled ); + m_bInCrossLinkedFunction = false; + } + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::SetVPhysicsSimulationEnabled() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); +} + + +#ifndef CLIENT_DLL +void CPortalSimulator::PrePhysFrame( void ) +{ + int iPortalSimulators = s_PortalSimulators.Count(); + + if( iPortalSimulators != 0 ) + { + CPortalSimulator **pAllSimulators = s_PortalSimulators.Base(); + for( int i = 0; i != iPortalSimulators; ++i ) + { + CPortalSimulator *pSimulator = pAllSimulators[i]; + if( !pSimulator->IsReadyToSimulate() ) + continue; + + int iOwnedEntities = pSimulator->m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); + if( iOwnedEntities != 0 ) + { + CBaseEntity **pOwnedEntities = pSimulator->m_InternalData.Simulation.Dynamic.OwnedEntities.Base(); + + for( int j = 0; j != iOwnedEntities; ++j ) + { + CBaseEntity *pEntity = pOwnedEntities[j]; + if( CPhysicsShadowClone::IsShadowClone( pEntity ) ) + continue; + + Assert( (pEntity != NULL) && (pEntity->IsMarkedForDeletion() == false) ); + IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); + if( (pPhysObject == NULL) || pPhysObject->IsAsleep() ) + continue; + + int iEntIndex = pEntity->entindex(); + int iExistingFlags = pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex]; + if( pSimulator->EntityIsInPortalHole( pEntity ) ) + pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_IS_IN_PORTAL_HOLE; + else + pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_IS_IN_PORTAL_HOLE; + + UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] ); + + if( ((iExistingFlags ^ pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex]) & PSEF_IS_IN_PORTAL_HOLE) != 0 ) //value changed + { + pEntity->CollisionRulesChanged(); //entity moved into or out of the portal hole, need to either add or remove collision with transformed geometry + + CPhysicsShadowCloneLL *pClones = CPhysicsShadowClone::GetClonesOfEntity( pEntity ); + while( pClones ) + { + pClones->pClone->CollisionRulesChanged(); + pClones = pClones->pNext; + } + } + } + } + } + } +} + +void CPortalSimulator::PostPhysFrame( void ) +{ + if ( g_bPlayerIsInSimulator ) + { + CPortal_Player* pPlayer = dynamic_cast<CPortal_Player*>( UTIL_GetLocalPlayer() ); + CProp_Portal* pTouchedPortal = pPlayer->m_hPortalEnvironment.Get(); + CPortalSimulator* pSim = GetSimulatorThatOwnsEntity( pPlayer ); + if ( pTouchedPortal && pSim && (pTouchedPortal->m_PortalSimulator.GetPortalSimulatorGUID() != pSim->GetPortalSimulatorGUID()) ) + { + Warning ( "Player is simulated in a physics environment but isn't touching a portal! Can't teleport, but can fall through portal hole. Returning player to main environment.\n" ); + ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "Player in PortalSimulator but not touching a portal, removing from sim at : %f\n", gpGlobals->curtime ) ); + + if ( pSim ) + { + pSim->ReleaseOwnershipOfEntity( pPlayer, false ); + } + } + } +} +#endif //#ifndef CLIENT_DLL + + +#ifndef CLIENT_DLL +int CPortalSimulator::GetMoveableOwnedEntities( CBaseEntity **pEntsOut, int iEntOutLimit ) +{ + int iOwnedEntCount = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); + int iOutputCount = 0; + + for( int i = 0; i != iOwnedEntCount; ++i ) + { + CBaseEntity *pEnt = m_InternalData.Simulation.Dynamic.OwnedEntities[i]; + Assert( pEnt != NULL ); + + if( CPhysicsShadowClone::IsShadowClone( pEnt ) ) + continue; + + if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEnt ) ) + continue; + + if( pEnt->GetMoveType() == MOVETYPE_NONE ) + continue; + + pEntsOut[iOutputCount] = pEnt; + ++iOutputCount; + + if( iOutputCount == iEntOutLimit ) + break; + } + + return iOutputCount; +} + +CPortalSimulator *CPortalSimulator::GetSimulatorThatOwnsEntity( const CBaseEntity *pEntity ) +{ +#ifdef _DEBUG + int iEntIndex = pEntity->entindex(); + CPortalSimulator *pOwningSimulatorCheck = NULL; + + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + { + if( s_PortalSimulators[i]->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY ) + { + AssertMsg( pOwningSimulatorCheck == NULL, "More than one portal simulator found owning the same entity." ); + pOwningSimulatorCheck = s_PortalSimulators[i]; + } + } + + AssertMsg( pOwningSimulatorCheck == s_OwnedEntityMap[iEntIndex], "Owned entity mapping out of sync with individual simulator ownership flags." ); +#endif + + return s_OwnedEntityMap[pEntity->entindex()]; +} + +CPortalSimulator *CPortalSimulator::GetSimulatorThatCreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType ) +{ + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + { + if( s_PortalSimulators[i]->CreatedPhysicsObject( pObject, pOut_SourceType ) ) + return s_PortalSimulators[i]; + } + + return NULL; +} + +bool CPortalSimulator::CreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType ) const +{ + if( (pObject == m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject) || (pObject == m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject) ) + { + if( pOut_SourceType ) + *pOut_SourceType = PSPOST_LOCAL_BRUSHES; + + return true; + } + + if( pObject == m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ) + { + if( pOut_SourceType ) + *pOut_SourceType = PSPOST_REMOTE_BRUSHES; + + return true; + } + + for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == pObject ) + { + if( pOut_SourceType ) + *pOut_SourceType = PSPOST_LOCAL_STATICPROPS; + return true; + } + } + + for( int i = m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count(); --i >= 0; ) + { + if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects[i] == pObject ) + { + if( pOut_SourceType ) + *pOut_SourceType = PSPOST_REMOTE_STATICPROPS; + + return true; + } + } + + if( pObject == m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ) + { + if( pOut_SourceType ) + *pOut_SourceType = PSPOST_HOLYWALL_TUBE; + + return true; + } + + return false; +} +#endif //#ifndef CLIENT_DLL + + + + + + + + +static void ConvertBrushListToClippedPolyhedronList( const int *pBrushes, int iBrushCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector<CPolyhedron *> *pPolyhedronList ) +{ + if( pPolyhedronList == NULL ) + return; + + if( (pBrushes == NULL) || (iBrushCount == 0) ) + return; + + for( int i = 0; i != iBrushCount; ++i ) + { + CPolyhedron *pPolyhedron = ClipPolyhedron( g_StaticCollisionPolyhedronCache.GetBrushPolyhedron( pBrushes[i] ), pOutwardFacingClipPlanes, iClipPlaneCount, fClipEpsilon ); + if( pPolyhedron ) + pPolyhedronList->AddToTail( pPolyhedron ); + } +} + +static void ClipPolyhedrons( CPolyhedron * const *pExistingPolyhedrons, int iPolyhedronCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector<CPolyhedron *> *pPolyhedronList ) +{ + if( pPolyhedronList == NULL ) + return; + + if( (pExistingPolyhedrons == NULL) || (iPolyhedronCount == 0) ) + return; + + for( int i = 0; i != iPolyhedronCount; ++i ) + { + CPolyhedron *pPolyhedron = ClipPolyhedron( pExistingPolyhedrons[i], pOutwardFacingClipPlanes, iClipPlaneCount, fClipEpsilon ); + if( pPolyhedron ) + pPolyhedronList->AddToTail( pPolyhedron ); + } +} + +static CPhysCollide *ConvertPolyhedronsToCollideable( CPolyhedron **pPolyhedrons, int iPolyhedronCount ) +{ + if( (pPolyhedrons == NULL) || (iPolyhedronCount == 0 ) ) + return NULL; + + CREATEDEBUGTIMER( functionTimer ); + + STARTDEBUGTIMER( functionTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvertPolyhedronsToCollideable() START\n", s_iPortalSimulatorGUID, TABSPACING ); ); + INCREMENTTABSPACING(); + + CPhysConvex **pConvexes = (CPhysConvex **)stackalloc( iPolyhedronCount * sizeof( CPhysConvex * ) ); + int iConvexCount = 0; + + CREATEDEBUGTIMER( convexTimer ); + STARTDEBUGTIMER( convexTimer ); + for( int i = 0; i != iPolyhedronCount; ++i ) + { + pConvexes[iConvexCount] = physcollision->ConvexFromConvexPolyhedron( *pPolyhedrons[i] ); + + Assert( pConvexes[iConvexCount] != NULL ); + + if( pConvexes[iConvexCount] ) + ++iConvexCount; + } + STOPDEBUGTIMER( convexTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvex Generation:%fms\n", s_iPortalSimulatorGUID, TABSPACING, convexTimer.GetDuration().GetMillisecondsF() ); ); + + + CPhysCollide *pReturn; + if( iConvexCount != 0 ) + { + CREATEDEBUGTIMER( collideTimer ); + STARTDEBUGTIMER( collideTimer ); + pReturn = physcollision->ConvertConvexToCollide( pConvexes, iConvexCount ); + STOPDEBUGTIMER( collideTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCollideable Generation:%fms\n", s_iPortalSimulatorGUID, TABSPACING, collideTimer.GetDuration().GetMillisecondsF() ); ); + } + else + { + pReturn = NULL; + } + + STOPDEBUGTIMER( functionTimer ); + DECREMENTTABSPACING(); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvertPolyhedronsToCollideable() FINISH: %fms\n", s_iPortalSimulatorGUID, TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); + + return pReturn; +} + + +static inline CPolyhedron *TransformAndClipSinglePolyhedron( CPolyhedron *pExistingPolyhedron, const VMatrix &Transform, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fCutEpsilon, bool bUseTempMemory ) +{ + Vector *pTempPointArray = (Vector *)stackalloc( sizeof( Vector ) * pExistingPolyhedron->iVertexCount ); + Polyhedron_IndexedPolygon_t *pTempPolygonArray = (Polyhedron_IndexedPolygon_t *)stackalloc( sizeof( Polyhedron_IndexedPolygon_t ) * pExistingPolyhedron->iPolygonCount ); + + Polyhedron_IndexedPolygon_t *pOriginalPolygons = pExistingPolyhedron->pPolygons; + pExistingPolyhedron->pPolygons = pTempPolygonArray; + + Vector *pOriginalPoints = pExistingPolyhedron->pVertices; + pExistingPolyhedron->pVertices = pTempPointArray; + + for( int j = 0; j != pExistingPolyhedron->iPolygonCount; ++j ) + { + pTempPolygonArray[j].iFirstIndex = pOriginalPolygons[j].iFirstIndex; + pTempPolygonArray[j].iIndexCount = pOriginalPolygons[j].iIndexCount; + pTempPolygonArray[j].polyNormal = Transform.ApplyRotation( pOriginalPolygons[j].polyNormal ); + } + + for( int j = 0; j != pExistingPolyhedron->iVertexCount; ++j ) + { + pTempPointArray[j] = Transform * pOriginalPoints[j]; + } + + CPolyhedron *pNewPolyhedron = ClipPolyhedron( pExistingPolyhedron, pOutwardFacingClipPlanes, iClipPlaneCount, fCutEpsilon, bUseTempMemory ); //copy the polyhedron + + //restore the original polyhedron to its former self + pExistingPolyhedron->pVertices = pOriginalPoints; + pExistingPolyhedron->pPolygons = pOriginalPolygons; + + return pNewPolyhedron; +} + +static int GetEntityPhysicsObjects( IPhysicsEnvironment *pEnvironment, CBaseEntity *pEntity, IPhysicsObject **pRetList, int iRetListArraySize ) +{ + int iCount, iRetCount = 0; + const IPhysicsObject **pList = pEnvironment->GetObjectList( &iCount ); + + if( iCount > iRetListArraySize ) + iCount = iRetListArraySize; + + for ( int i = 0; i < iCount; ++i ) + { + CBaseEntity *pEnvEntity = reinterpret_cast<CBaseEntity *>(pList[i]->GetGameData()); + if ( pEntity == pEnvEntity ) + { + pRetList[iRetCount] = (IPhysicsObject *)(pList[i]); + ++iRetCount; + } + } + + return iRetCount; +} + + + +#ifndef CLIENT_DLL +//Move all entities back to the main environment for removal, and make sure the main environment is in control during the UTIL_Remove process +struct UTIL_Remove_PhysicsStack_t +{ + IPhysicsEnvironment *pPhysicsEnvironment; + CEntityList *pShadowList; +}; +static CUtlVector<UTIL_Remove_PhysicsStack_t> s_UTIL_Remove_PhysicsStack; + +void CPortalSimulator::Pre_UTIL_Remove( CBaseEntity *pEntity ) +{ + int index = s_UTIL_Remove_PhysicsStack.AddToTail(); + s_UTIL_Remove_PhysicsStack[index].pPhysicsEnvironment = physenv; + s_UTIL_Remove_PhysicsStack[index].pShadowList = g_pShadowEntities; + int iEntIndex = pEntity->entindex(); + + //NDebugOverlay::EntityBounds( pEntity, 0, 0, 0, 50, 5.0f ); + + if( (CPhysicsShadowClone::IsShadowClone( pEntity ) == false) && + (CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) == false) ) + { + CPortalSimulator *pOwningSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); + if( pOwningSimulator ) + { + pOwningSimulator->ReleasePhysicsOwnership( pEntity, false ); + pOwningSimulator->ReleaseOwnershipOfEntity( pEntity ); + } + + //might be cloned from main to a few environments + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + s_PortalSimulators[i]->StopCloningEntity( pEntity ); + } + + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + { + s_PortalSimulators[i]->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] = 0; + } + + + physenv = physenv_main; + g_pShadowEntities = g_pShadowEntities_Main; +} + +void CPortalSimulator::Post_UTIL_Remove( CBaseEntity *pEntity ) +{ + int index = s_UTIL_Remove_PhysicsStack.Count() - 1; + Assert( index >= 0 ); + UTIL_Remove_PhysicsStack_t &PhysicsStackEntry = s_UTIL_Remove_PhysicsStack[index]; + physenv = PhysicsStackEntry.pPhysicsEnvironment; + g_pShadowEntities = PhysicsStackEntry.pShadowList; + s_UTIL_Remove_PhysicsStack.FastRemove(index); + +#ifdef _DEBUG + for( int i = CPhysicsShadowClone::g_ShadowCloneList.Count(); --i >= 0; ) + { + Assert( CPhysicsShadowClone::g_ShadowCloneList[i]->GetClonedEntity() != pEntity ); //shouldn't be any clones of this object anymore + } +#endif +} + +void UpdateShadowClonesPortalSimulationFlags( const CBaseEntity *pSourceEntity, unsigned int iFlags, int iSourceFlags ) +{ + unsigned int iOrFlags = iSourceFlags & iFlags; + + CPhysicsShadowCloneLL *pClones = CPhysicsShadowClone::GetClonesOfEntity( pSourceEntity ); + while( pClones ) + { + CPhysicsShadowClone *pClone = pClones->pClone; + CPortalSimulator *pCloneSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pClone ); + + unsigned int *pFlags = (unsigned int *)&pCloneSimulator->m_DataAccess.Simulation.Dynamic.EntFlags[pClone->entindex()]; + *pFlags &= ~iFlags; + *pFlags |= iOrFlags; + + Assert( ((iSourceFlags ^ *pFlags) & iFlags) == 0 ); + + pClones = pClones->pNext; + } +} +#endif + + + +#ifndef CLIENT_DLL + class CPS_AutoGameSys_EntityListener : public CAutoGameSystem, public IEntityListener +#else + class CPS_AutoGameSys_EntityListener : public CAutoGameSystem +#endif +{ +public: + virtual void LevelInitPreEntity( void ) + { + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + s_PortalSimulators[i]->ClearEverything(); + } + + virtual void LevelShutdownPreEntity( void ) + { + for( int i = s_PortalSimulators.Count(); --i >= 0; ) + s_PortalSimulators[i]->ClearEverything(); + } + +#ifndef CLIENT_DLL + virtual bool Init( void ) + { + gEntList.AddListenerEntity( this ); + return true; + } + + //virtual void OnEntityCreated( CBaseEntity *pEntity ) {} + virtual void OnEntitySpawned( CBaseEntity *pEntity ) + { + + } + virtual void OnEntityDeleted( CBaseEntity *pEntity ) + { + CPortalSimulator *pSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); + if( pSimulator ) + { + pSimulator->ReleasePhysicsOwnership( pEntity, false ); + pSimulator->ReleaseOwnershipOfEntity( pEntity ); + } + Assert( CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ) == NULL ); + } +#endif //#ifndef CLIENT_DLL +}; +static CPS_AutoGameSys_EntityListener s_CPS_AGS_EL_Singleton; + + + + + +#ifndef CLIENT_DLL +LINK_ENTITY_TO_CLASS( portalsimulator_collisionentity, CPSCollisionEntity ); + +static bool s_PortalSimulatorCollisionEntities[MAX_EDICTS] = { false }; + +CPSCollisionEntity::CPSCollisionEntity( void ) +{ + m_pOwningSimulator = NULL; +} + +CPSCollisionEntity::~CPSCollisionEntity( void ) +{ + if( m_pOwningSimulator ) + { + m_pOwningSimulator->m_InternalData.Simulation.Dynamic.EntFlags[entindex()] &= ~PSEF_OWNS_PHYSICS; + m_pOwningSimulator->MarkAsReleased( this ); + m_pOwningSimulator->m_InternalData.Simulation.pCollisionEntity = NULL; + m_pOwningSimulator = NULL; + } + s_PortalSimulatorCollisionEntities[entindex()] = false; +} + +void CPSCollisionEntity::UpdateOnRemove( void ) +{ + VPhysicsSetObject( NULL ); + if( m_pOwningSimulator ) + { + m_pOwningSimulator->m_InternalData.Simulation.Dynamic.EntFlags[entindex()] &= ~PSEF_OWNS_PHYSICS; + m_pOwningSimulator->MarkAsReleased( this ); + m_pOwningSimulator->m_InternalData.Simulation.pCollisionEntity = NULL; + m_pOwningSimulator = NULL; + } + s_PortalSimulatorCollisionEntities[entindex()] = false; + + BaseClass::UpdateOnRemove(); +} + +void CPSCollisionEntity::Spawn( void ) +{ + BaseClass::Spawn(); + SetSolid( SOLID_CUSTOM ); + SetMoveType( MOVETYPE_NONE ); + SetCollisionGroup( COLLISION_GROUP_NONE ); + s_PortalSimulatorCollisionEntities[entindex()] = true; + VPhysicsSetObject( NULL ); + AddFlag( FL_WORLDBRUSH ); + AddEffects( EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW ); + IncrementInterpolationFrame(); +} + +void CPSCollisionEntity::Activate( void ) +{ + BaseClass::Activate(); + CollisionRulesChanged(); +} + +int CPSCollisionEntity::ObjectCaps( void ) +{ + return ((BaseClass::ObjectCaps() | FCAP_DONT_SAVE) & ~(FCAP_FORCE_TRANSITION | FCAP_ACROSS_TRANSITION | FCAP_MUST_SPAWN | FCAP_SAVE_NON_NETWORKABLE)); +} + +bool CPSCollisionEntity::ShouldCollide( int collisionGroup, int contentsMask ) const +{ + return GetWorldEntity()->ShouldCollide( collisionGroup, contentsMask ); +} + +IPhysicsObject *CPSCollisionEntity::VPhysicsGetObject( void ) +{ + if( m_pOwningSimulator == NULL ) + return NULL; + + if( m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject != NULL ) + return m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject; + else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject != NULL ) + return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; + else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject != NULL ) + return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; + else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject != NULL ) + return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject; + else + return NULL; +} + +int CPSCollisionEntity::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ) +{ + if( m_pOwningSimulator == NULL ) + return 0; + + if( (pList == NULL) || (listMax == 0) ) + return 0; + + int iRetVal = 0; + + if( m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject != NULL ) + { + pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject; + ++iRetVal; + if( iRetVal == listMax ) + return iRetVal; + } + + if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject != NULL ) + { + pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; + ++iRetVal; + if( iRetVal == listMax ) + return iRetVal; + } + + if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject != NULL ) + { + pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject; + ++iRetVal; + if( iRetVal == listMax ) + return iRetVal; + } + + if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject != NULL ) + { + pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject; + ++iRetVal; + if( iRetVal == listMax ) + return iRetVal; + } + + return iRetVal; +} + +bool CPSCollisionEntity::IsPortalSimulatorCollisionEntity( const CBaseEntity *pEntity ) +{ + return s_PortalSimulatorCollisionEntities[pEntity->entindex()]; +} +#endif //#ifndef CLIENT_DLL + + + + + + + + +#ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS + +#include "filesystem.h" + +static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ); +static void PortalSimulatorDumps_DumpPlanesToGlView( float *pPlanes, int iPlaneCount, const char *pszFileName ); +static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ); +static void PortalSimulatorDumps_DumpOBBoxToGlView( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, float fRed, float fGreen, float fBlue, const char *pszFileName ); + +void DumpActiveCollision( const CPortalSimulator *pPortalSimulator, const char *szFileName ) +{ + CREATEDEBUGTIMER( collisionDumpTimer ); + STARTDEBUGTIMER( collisionDumpTimer ); + + //color coding scheme, static prop collision is brighter than brush collision. Remote world stuff transformed to the local wall is darker than completely local stuff +#define PSDAC_INTENSITY_LOCALBRUSH 0.25f +#define PSDAC_INTENSITY_LOCALPROP 1.0f +#define PSDAC_INTENSITY_REMOTEBRUSH 0.125f +#define PSDAC_INTENSITY_REMOTEPROP 0.5f + + if( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) + PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); + + if( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + for( int i = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + Assert( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide ); + PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALPROP, szFileName ); + } + } + + if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable ) + PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); + + if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable ) + PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); + + //if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable ) + // PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_REMOTEBRUSH, szFileName ); + CPortalSimulator *pLinkedPortal = pPortalSimulator->GetLinkedPortalSimulator(); + if( pLinkedPortal ) + { + if( pLinkedPortal->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) + PortalSimulatorDumps_DumpCollideToGlView( pLinkedPortal->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, PSDAC_INTENSITY_REMOTEBRUSH, szFileName ); + + //for( int i = pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.Collideables.Count(); --i >= 0; ) + // PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.Collideables[i], vec3_origin, vec3_angle, PSDAC_INTENSITY_REMOTEPROP, szFileName ); + if( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + for( int i = pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) + { + Assert( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide ); + PortalSimulatorDumps_DumpCollideToGlView( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, PSDAC_INTENSITY_REMOTEPROP, szFileName ); + } + } + } + + STOPDEBUGTIMER( collisionDumpTimer ); + DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DumpActiveCollision() Spent %fms generating a collision dump\n", pPortalSimulator->GetPortalSimulatorGUID(), TABSPACING, collisionDumpTimer.GetDuration().GetMillisecondsF() ); ); +} + +static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ) +{ + if ( !pCollide ) + return; + + printf("Writing %s...\n", pFilename ); + Vector *outVerts; + int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts ); + FileHandle_t fp = filesystem->Open( pFilename, "ab" ); + int triCount = vertCount / 3; + int vert = 0; + VMatrix tmp = SetupMatrixOrgAngles( origin, angles ); + int i; + for ( i = 0; i < vertCount; i++ ) + { + outVerts[i] = tmp.VMul4x3( outVerts[i] ); + } + + for ( i = 0; i < triCount; i++ ) + { + filesystem->FPrintf( fp, "3\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); + vert++; + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 %.2f 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); + vert++; + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 0 %.2f\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); + vert++; + } + filesystem->Close( fp ); + physcollision->DestroyDebugMesh( vertCount, outVerts ); +} + +static void PortalSimulatorDumps_DumpPlanesToGlView( float *pPlanes, int iPlaneCount, const char *pszFileName ) +{ + FileHandle_t fp = filesystem->Open( pszFileName, "wb" ); + + for( int i = 0; i < iPlaneCount; ++i ) + { + Vector vPlaneVerts[4]; + + float fRed, fGreen, fBlue; + fRed = rand()/32768.0f; + fGreen = rand()/32768.0f; + fBlue = rand()/32768.0f; + + PolyFromPlane( vPlaneVerts, *(Vector *)(pPlanes + (i*4)), pPlanes[(i*4) + 3], 1000.0f ); + + filesystem->FPrintf( fp, "4\n" ); + + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[3].x, vPlaneVerts[3].y, vPlaneVerts[3].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[2].x, vPlaneVerts[2].y, vPlaneVerts[2].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[1].x, vPlaneVerts[1].y, vPlaneVerts[1].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[0].x, vPlaneVerts[0].y, vPlaneVerts[0].z, fRed, fGreen, fBlue ); + } + + filesystem->Close( fp ); +} + + +static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ) +{ + FileHandle_t fp = filesystem->Open( pszFileName, "ab" ); + + //x min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + //x max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + + //y min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + + + //y max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + + + + //z min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); + + + + //z max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); + + filesystem->Close( fp ); +} + +static void PortalSimulatorDumps_DumpOBBoxToGlView( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, float fRed, float fGreen, float fBlue, const char *pszFileName ) +{ + FileHandle_t fp = filesystem->Open( pszFileName, "ab" ); + + Vector ptExtents[8]; + int counter; + for( counter = 0; counter != 8; ++counter ) + { + ptExtents[counter] = ptOrigin; + if( counter & (1<<0) ) ptExtents[counter] += vExtent1; + if( counter & (1<<1) ) ptExtents[counter] += vExtent2; + if( counter & (1<<2) ) ptExtents[counter] += vExtent3; + } + + //x min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + + //x max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + + + //y min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + + + + //y max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + + + + //z min side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); + + + + //z max side + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + + filesystem->FPrintf( fp, "4\n" ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); + filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); + + filesystem->Close( fp ); +} + + + +#endif + + + + + + + + + + + + + + diff --git a/game/shared/portal/PortalSimulation.h b/game/shared/portal/PortalSimulation.h new file mode 100644 index 0000000..91c6651 --- /dev/null +++ b/game/shared/portal/PortalSimulation.h @@ -0,0 +1,481 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Provides structures and classes necessary to simulate a portal. +// +// $NoKeywords: $ +//=====================================================================================// + +#ifndef PORTALSIMULATION_H +#define PORTALSIMULATION_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/polyhedron.h" +#include "const.h" +#include "tier1/utlmap.h" +#include "tier1/utlvector.h" + +#define PORTAL_SIMULATORS_EMBED_GUID //define this to embed a unique integer with each portal simulator for debugging purposes + +struct StaticPropPolyhedronGroups_t //each static prop is made up of a group of polyhedrons, these help us pull those groups from an array +{ + int iStartIndex; + int iNumPolyhedrons; +}; + +enum PortalSimulationEntityFlags_t +{ + PSEF_OWNS_ENTITY = (1 << 0), //this environment is responsible for the entity's physics objects + PSEF_OWNS_PHYSICS = (1 << 1), + PSEF_IS_IN_PORTAL_HOLE = (1 << 2), //updated per-phyframe + PSEF_CLONES_ENTITY_FROM_MAIN = (1 << 3), //entity is close enough to the portal to affect objects intersecting the portal + //PSEF_HAS_LINKED_CLONE = (1 << 1), //this environment has a clone of the entity which is transformed from its linked portal +}; + +enum PS_PhysicsObjectSourceType_t +{ + PSPOST_LOCAL_BRUSHES, + PSPOST_REMOTE_BRUSHES, + PSPOST_LOCAL_STATICPROPS, + PSPOST_REMOTE_STATICPROPS, + PSPOST_HOLYWALL_TUBE +}; + +struct PortalTransformAsAngledPosition_t //a matrix transformation from this portal to the linked portal, stored as vector and angle transforms +{ + Vector ptOriginTransform; + QAngle qAngleTransform; +}; + +inline bool LessFunc_Integer( const int &a, const int &b ) { return a < b; }; + + +class CPortalSimulatorEventCallbacks //sends out notifications of events to game specific code +{ +public: + virtual void PortalSimulator_TookOwnershipOfEntity( CBaseEntity *pEntity ) { }; + virtual void PortalSimulator_ReleasedOwnershipOfEntity( CBaseEntity *pEntity ) { }; + + virtual void PortalSimulator_TookPhysicsOwnershipOfEntity( CBaseEntity *pEntity ) { }; + virtual void PortalSimulator_ReleasedPhysicsOwnershipOfEntity( CBaseEntity *pEntity ) { }; +}; + +//==================================================================================== +// To any coder trying to understand the following nested structures.... +// +// You may be wondering... why? wtf? +// +// The answer. The previous incarnation of server side portal simulation suffered +// terribly from evolving variables with increasingly cryptic names with no clear +// definition of what part of the system the variable was involved with. +// +// It's my hope that a nested structure with clear boundaries will eliminate that +// horrible, awful, nasty, frustrating confusion. (It was really really bad). This +// system has the added benefit of pseudo-forcing a naming structure. +// +// Lastly, if it all roots in one struct, we can const reference it out to allow +// easy reads without writes +// +// It's broken out like this to solve a few problems.... +// 1. It cleans up intellisense when you don't actually define a structure +// within a structure. +// 2. Shorter typenames when you want to have a pointer/reference deep within +// the nested structure. +// 3. Needed at least one level removed from CPortalSimulator so +// pointers/references could be made while the primary instance of the +// data was private/protected. +// +// It may be slightly difficult to understand in it's broken out structure, but +// intellisense brings all the data together in a very cohesive manner for +// working with. +//==================================================================================== + +struct PS_PlacementData_t //stuff useful for geometric operations +{ + Vector ptCenter; + QAngle qAngles; + Vector vForward; + Vector vUp; + Vector vRight; + VPlane PortalPlane; + VMatrix matThisToLinked; + VMatrix matLinkedToThis; + PortalTransformAsAngledPosition_t ptaap_ThisToLinked; + PortalTransformAsAngledPosition_t ptaap_LinkedToThis; + CPhysCollide *pHoleShapeCollideable; //used to test if a collideable is in the hole, should NOT be collided against in general + PS_PlacementData_t( void ) + { + memset( this, 0, sizeof( PS_PlacementData_t ) ); + } +}; + +struct PS_SD_Static_World_Brushes_t +{ + CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision + CPhysCollide *pCollideable; +#ifndef CLIENT_DLL + IPhysicsObject *pPhysicsObject; + PS_SD_Static_World_Brushes_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; +#else + PS_SD_Static_World_Brushes_t() : pCollideable(NULL) {}; +#endif + +}; + + +struct PS_SD_Static_World_StaticProps_ClippedProp_t +{ + StaticPropPolyhedronGroups_t PolyhedronGroup; + CPhysCollide * pCollide; +#ifndef CLIENT_DLL + IPhysicsObject * pPhysicsObject; +#endif + IHandleEntity * pSourceProp; + + int iTraceContents; + short iTraceSurfaceProps; + static CBaseEntity * pTraceEntity; + static const char * szTraceSurfaceName; //same for all static props, here just for easy reference + static const int iTraceSurfaceFlags; //same for all static props, here just for easy reference +}; + +struct PS_SD_Static_World_StaticProps_t +{ + CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision + CUtlVector<PS_SD_Static_World_StaticProps_ClippedProp_t> ClippedRepresentations; + bool bCollisionExists; //the shortcut to know if collideables exist for each prop + bool bPhysicsExists; //the shortcut to know if physics obects exist for each prop + PS_SD_Static_World_StaticProps_t( void ) : bCollisionExists( false ), bPhysicsExists( false ) { }; +}; + +struct PS_SD_Static_World_t //stuff in front of the portal +{ + PS_SD_Static_World_Brushes_t Brushes; + PS_SD_Static_World_StaticProps_t StaticProps; +}; + +struct PS_SD_Static_Wall_Local_Tube_t //a minimal tube, an object must fit inside this to be eligible for portaling +{ + CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision + CPhysCollide *pCollideable; + +#ifndef CLIENT_DLL + IPhysicsObject *pPhysicsObject; + PS_SD_Static_Wall_Local_Tube_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; +#else + PS_SD_Static_Wall_Local_Tube_t() : pCollideable(NULL) {}; +#endif +}; + +struct PS_SD_Static_Wall_Local_Brushes_t +{ + CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision + CPhysCollide *pCollideable; + +#ifndef CLIENT_DLL + IPhysicsObject *pPhysicsObject; + PS_SD_Static_Wall_Local_Brushes_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; +#else + PS_SD_Static_Wall_Local_Brushes_t() : pCollideable(NULL) {}; +#endif +}; + +struct PS_SD_Static_Wall_Local_t //things in the wall that are completely independant of having a linked portal +{ + PS_SD_Static_Wall_Local_Tube_t Tube; + PS_SD_Static_Wall_Local_Brushes_t Brushes; +}; + +struct PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t +{ + IPhysicsObject *pPhysicsObject; + PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t() : pPhysicsObject(NULL) {}; +}; + +struct PS_SD_Static_Wall_RemoteTransformedToLocal_StaticProps_t +{ + CUtlVector<IPhysicsObject *> PhysicsObjects; +}; + +struct PS_SD_Static_Wall_RemoteTransformedToLocal_t //things taken from the linked portal's "World" collision and transformed into local space +{ + PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t Brushes; + PS_SD_Static_Wall_RemoteTransformedToLocal_StaticProps_t StaticProps; +}; + +struct PS_SD_Static_Wall_t //stuff behind the portal +{ + PS_SD_Static_Wall_Local_t Local; +#ifndef CLIENT_DLL + PS_SD_Static_Wall_RemoteTransformedToLocal_t RemoteTransformedToLocal; +#endif +}; + +struct PS_SD_Static_SurfaceProperties_t //surface properties to pretend every collideable here is using +{ + int contents; + csurface_t surface; + CBaseEntity *pEntity; +}; + +struct PS_SD_Static_t //stuff that doesn't move around +{ + PS_SD_Static_World_t World; + PS_SD_Static_Wall_t Wall; + PS_SD_Static_SurfaceProperties_t SurfaceProperties; +}; + +class CPhysicsShadowClone; + +struct PS_SD_Dynamic_PhysicsShadowClones_t +{ + CUtlVector<CBaseEntity *> ShouldCloneFromMain; //a list of entities that should be cloned from main if physics simulation is enabled + //in single-environment mode, this helps us track who should collide with who + + CUtlVector<CPhysicsShadowClone *> FromLinkedPortal; +}; + +struct PS_SD_Dynamic_t //stuff that moves around +{ + unsigned int EntFlags[MAX_EDICTS]; //flags maintained for every entity in the world based on its index + + PS_SD_Dynamic_PhysicsShadowClones_t ShadowClones; + + CUtlVector<CBaseEntity *> OwnedEntities; + + PS_SD_Dynamic_t() + { + memset( EntFlags, 0, sizeof( EntFlags ) ); + } +}; + +class CPSCollisionEntity; + +struct PS_SimulationData_t //compartmentalized data for coherent management +{ + PS_SD_Static_t Static; + +#ifndef CLIENT_DLL + PS_SD_Dynamic_t Dynamic; + + IPhysicsEnvironment *pPhysicsEnvironment; + CPSCollisionEntity *pCollisionEntity; //the entity we'll be tying physics objects to for collision + + PS_SimulationData_t() : pPhysicsEnvironment(NULL), pCollisionEntity(NULL) {}; +#endif +}; + +struct PS_InternalData_t +{ + PS_PlacementData_t Placement; + PS_SimulationData_t Simulation; +}; + + +class CPortalSimulator +{ +public: + CPortalSimulator( void ); + ~CPortalSimulator( void ); + + void MoveTo( const Vector &ptCenter, const QAngle &angles ); + void ClearEverything( void ); + + void AttachTo( CPortalSimulator *pLinkedPortalSimulator ); + void DetachFromLinked( void ); //detach portals to sever the connection, saves work when planning on moving both portals + CPortalSimulator *GetLinkedPortalSimulator( void ) const; + + void SetPortalSimulatorCallbacks( CPortalSimulatorEventCallbacks *pCallbacks ); + + bool IsReadyToSimulate( void ) const; //is active and linked to another portal + + void SetCollisionGenerationEnabled( bool bEnabled ); //enable/disable collision generation for the hole in the wall, needed for proper vphysics simulation + bool IsCollisionGenerationEnabled( void ) const; + + void SetVPhysicsSimulationEnabled( bool bEnabled ); //enable/disable vphysics simulation. Will automatically update the linked portal to be the same + bool IsSimulatingVPhysics( void ) const; //this portal is setup to handle any physically simulated object, false means the portal is handling player movement only + + bool EntityIsInPortalHole( CBaseEntity *pEntity ) const; //true if the entity is within the portal cutout bounds and crossing the plane. Not just *near* the portal + bool EntityHitBoxExtentIsInPortalHole( CBaseAnimating *pBaseAnimating ) const; //true if the entity is within the portal cutout bounds and crossing the plane. Not just *near* the portal + void RemoveEntityFromPortalHole( CBaseEntity *pEntity ); //if the entity is in the portal hole, this forcibly moves it out by any means possible + + bool RayIsInPortalHole( const Ray_t &ray ) const; //traces a ray against the same detector for EntityIsInPortalHole(), bias is towards false positives + +#ifndef CLIENT_DLL + int GetMoveableOwnedEntities( CBaseEntity **pEntsOut, int iEntOutLimit ); //gets owned entities that aren't either world or static props. Excludes fake portal ents such as physics clones + + static CPortalSimulator *GetSimulatorThatOwnsEntity( const CBaseEntity *pEntity ); //fairly cheap to call + static CPortalSimulator *GetSimulatorThatCreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType = NULL ); + static void Pre_UTIL_Remove( CBaseEntity *pEntity ); + static void Post_UTIL_Remove( CBaseEntity *pEntity ); + + //these three really should be made internal and the public interface changed to a "watch this entity" setup + void TakeOwnershipOfEntity( CBaseEntity *pEntity ); //general ownership, not necessarily physics ownership + void ReleaseOwnershipOfEntity( CBaseEntity *pEntity, bool bMovingToLinkedSimulator = false ); //if bMovingToLinkedSimulator is true, the code skips some steps that are going to be repeated when the entity is added to the other simulator + void ReleaseAllEntityOwnership( void ); //go back to not owning any entities + + //void TeleportEntityToLinkedPortal( CBaseEntity *pEntity ); + void StartCloningEntity( CBaseEntity *pEntity ); + void StopCloningEntity( CBaseEntity *pEntity ); + + bool OwnsEntity( const CBaseEntity *pEntity ) const; + bool OwnsPhysicsForEntity( const CBaseEntity *pEntity ) const; + + bool CreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType = NULL ) const; //true if the physics object was generated by this portal simulator + + static void PrePhysFrame( void ); + static void PostPhysFrame( void ); + +#endif //#ifndef CLIENT_DLL + +#ifdef PORTAL_SIMULATORS_EMBED_GUID + int GetPortalSimulatorGUID( void ) const { return m_iPortalSimulatorGUID; }; +#endif + +protected: + bool m_bLocalDataIsReady; //this side of the portal is properly setup, no guarantees as to linkage to another portal + bool m_bSimulateVPhysics; + bool m_bGenerateCollision; + bool m_bSharedCollisionConfiguration; //when portals are in certain configurations, they need to cross-clip and share some collision data and things get nasty. For the love of all that is holy, pray that this is false. + CPortalSimulator *m_pLinkedPortal; + bool m_bInCrossLinkedFunction; //A flag to mark that we're already in a linked function and that the linked portal shouldn't call our side + CPortalSimulatorEventCallbacks *m_pCallbacks; +#ifdef PORTAL_SIMULATORS_EMBED_GUID + int m_iPortalSimulatorGUID; +#endif + + struct + { + bool bPolyhedronsGenerated; + bool bLocalCollisionGenerated; + bool bLinkedCollisionGenerated; + bool bLocalPhysicsGenerated; + bool bLinkedPhysicsGenerated; + } m_CreationChecklist; + + friend class CPSCollisionEntity; + +#ifndef CLIENT_DLL //physics handled purely by server side + void TakePhysicsOwnership( CBaseEntity *pEntity ); + void ReleasePhysicsOwnership( CBaseEntity *pEntity, bool bContinuePhysicsCloning = true, bool bMovingToLinkedSimulator = false ); + + void CreateAllPhysics( void ); + void CreateMinimumPhysics( void ); //stuff needed by any part of physics simulations + void CreateLocalPhysics( void ); + void CreateLinkedPhysics( void ); + + void ClearAllPhysics( void ); + void ClearMinimumPhysics( void ); + void ClearLocalPhysics( void ); + void ClearLinkedPhysics( void ); + + void ClearLinkedEntities( void ); //gets rid of transformed shadow clones +#endif + + void CreateAllCollision( void ); + void CreateLocalCollision( void ); + void CreateLinkedCollision( void ); + + void ClearAllCollision( void ); + void ClearLinkedCollision( void ); + void ClearLocalCollision( void ); + + void CreatePolyhedrons( void ); //carves up the world around the portal's position into sets of polyhedrons + void ClearPolyhedrons( void ); + + void UpdateLinkMatrix( void ); + + void MarkAsOwned( CBaseEntity *pEntity ); + void MarkAsReleased( CBaseEntity *pEntity ); + + PS_InternalData_t m_InternalData; + +public: + const PS_InternalData_t &m_DataAccess; + + friend class CPS_AutoGameSys_EntityListener; +}; + +extern CUtlVector<CPortalSimulator *> const &g_PortalSimulators; + + +#ifndef CLIENT_DLL +class CPSCollisionEntity : public CBaseEntity +{ + DECLARE_CLASS( CPSCollisionEntity, CBaseEntity ); +private: + CPortalSimulator *m_pOwningSimulator; + +public: + CPSCollisionEntity( void ); + virtual ~CPSCollisionEntity( void ); + + virtual void Spawn( void ); + virtual void Activate( void ); + virtual int ObjectCaps( void ); + virtual IPhysicsObject *VPhysicsGetObject( void ); + virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ); + virtual void UpdateOnRemove( void ); + virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const; + virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) {} + virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ) {} + + static bool IsPortalSimulatorCollisionEntity( const CBaseEntity *pEntity ); + friend class CPortalSimulator; +}; +#endif + + + + + + + + + + + + + + + + +#ifndef CLIENT_DLL +inline bool CPortalSimulator::OwnsEntity( const CBaseEntity *pEntity ) const +{ + return ((m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_OWNS_ENTITY) != 0); +} + +inline bool CPortalSimulator::OwnsPhysicsForEntity( const CBaseEntity *pEntity ) const +{ + return ((m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_OWNS_PHYSICS) != 0); +} +#endif + +inline bool CPortalSimulator::IsReadyToSimulate( void ) const +{ + return m_bLocalDataIsReady && m_pLinkedPortal && m_pLinkedPortal->m_bLocalDataIsReady; +} + +inline bool CPortalSimulator::IsSimulatingVPhysics( void ) const +{ + return m_bSimulateVPhysics; +} + +inline bool CPortalSimulator::IsCollisionGenerationEnabled( void ) const +{ + return m_bGenerateCollision; +} + +inline CPortalSimulator *CPortalSimulator::GetLinkedPortalSimulator( void ) const +{ + return m_pLinkedPortal; +} + + + + +#endif //#ifndef PORTALSIMULATION_H + diff --git a/game/shared/portal/StaticCollisionPolyhedronCache.cpp b/game/shared/portal/StaticCollisionPolyhedronCache.cpp new file mode 100644 index 0000000..7f91060 --- /dev/null +++ b/game/shared/portal/StaticCollisionPolyhedronCache.cpp @@ -0,0 +1,485 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=====================================================================================// + +#include "cbase.h" +#include "StaticCollisionPolyhedronCache.h" +#include "engine/IEngineTrace.h" +#include "edict.h" + +#include "tier0/memdbgon.h" + + +class CPolyhedron_LumpedMemory : public CPolyhedron //we'll be allocating one big chunk of memory for all our polyhedrons. No individual will own any memory. +{ +public: + virtual void Release( void ) { }; + static CPolyhedron_LumpedMemory *AllocateAt( void *pMemory, int iVertices, int iLines, int iIndices, int iPolygons ) + { +#include "tier0/memdbgoff.h" //the following placement new doesn't compile with memory debugging + CPolyhedron_LumpedMemory *pAllocated = new ( pMemory ) CPolyhedron_LumpedMemory; +#include "tier0/memdbgon.h" + + pAllocated->iVertexCount = iVertices; + pAllocated->iLineCount = iLines; + pAllocated->iIndexCount = iIndices; + pAllocated->iPolygonCount = iPolygons; + pAllocated->pVertices = (Vector *)(pAllocated + 1); //start vertex memory at the end of the class + pAllocated->pLines = (Polyhedron_IndexedLine_t *)(pAllocated->pVertices + iVertices); + pAllocated->pIndices = (Polyhedron_IndexedLineReference_t *)(pAllocated->pLines + iLines); + pAllocated->pPolygons = (Polyhedron_IndexedPolygon_t *)(pAllocated->pIndices + iIndices); + + return pAllocated; + } +}; + +static uint8 *s_BrushPolyhedronMemory = NULL; +static uint8 *s_StaticPropPolyhedronMemory = NULL; + +CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache; + +typedef ICollideable *ICollideablePtr; //needed for key comparison function syntax +static bool CollideablePtr_KeyCompareFunc( const ICollideablePtr &a, const ICollideablePtr &b ) +{ + return a < b; +}; + +CStaticCollisionPolyhedronCache::CStaticCollisionPolyhedronCache( void ) +: m_CollideableIndicesMap( CollideablePtr_KeyCompareFunc ) +{ + +} + +CStaticCollisionPolyhedronCache::~CStaticCollisionPolyhedronCache( void ) +{ + Clear(); +} + +void CStaticCollisionPolyhedronCache::LevelInitPreEntity( void ) +{ + + // FIXME: Fast updates would be nice but this method doesn't work with the recent changes to standard containers. + // For now we're going with the quick fix of always doing a full update. -Jeep + +// if( Q_stricmp( m_CachedMap, MapName() ) != 0 ) +// { +// // New map or the last load was a transition, fully update the cache +// m_CachedMap.Set( MapName() ); + + Update(); +// } +// else +// { +// // No need for a full update, but we need to remap static prop ICollideable's in the old system to the new system +// for( int i = m_CollideableIndicesMap.Count(); --i >= 0; ) +// { +//#ifdef _DEBUG +// StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element(i); +//#endif +// m_CollideableIndicesMap.Reinsert( staticpropmgr->GetStaticPropByIndex( m_CollideableIndicesMap.Element(i).iStaticPropIndex ), i ); +// +// Assert( (m_CollideableIndicesMap.Element(i).iStartIndex == cacheInfo.iStartIndex) && +// (m_CollideableIndicesMap.Element(i).iNumPolyhedrons == cacheInfo.iNumPolyhedrons) && +// (m_CollideableIndicesMap.Element(i).iStaticPropIndex == cacheInfo.iStaticPropIndex) ); //I'm assuming this doesn't cause a reindex of the unordered list, if it does then this needs to be rewritten +// } +// } +} + +void CStaticCollisionPolyhedronCache::Shutdown( void ) +{ + Clear(); +} + + +void CStaticCollisionPolyhedronCache::Clear( void ) +{ + //The uses one big lump of memory to store polyhedrons. No need to Release() the polyhedrons. + + //Brushes + { + m_BrushPolyhedrons.RemoveAll(); + if( s_BrushPolyhedronMemory != NULL ) + { + delete []s_BrushPolyhedronMemory; + s_BrushPolyhedronMemory = NULL; + } + } + + //Static props + { + m_CollideableIndicesMap.RemoveAll(); + m_StaticPropPolyhedrons.RemoveAll(); + if( s_StaticPropPolyhedronMemory != NULL ) + { + delete []s_StaticPropPolyhedronMemory; + s_StaticPropPolyhedronMemory = NULL; + } + } +} + +void CStaticCollisionPolyhedronCache::Update( void ) +{ + Clear(); + + //There's no efficient way to know exactly how much memory we'll need to cache off all these polyhedrons. + //So we're going to allocated temporary workspaces as we need them and consolidate into one allocation at the end. + const size_t workSpaceSize = 1024 * 1024; //1MB. Fairly arbitrary size for a workspace. Brushes usually use 1-3MB in the end. Static props usually use about half as much as brushes. + + uint8 *workSpaceAllocations[256]; + size_t usedSpaceInWorkspace[256]; + unsigned int workSpacesAllocated = 0; + uint8 *pCurrentWorkSpace = new uint8 [workSpaceSize]; + size_t roomLeftInWorkSpace = workSpaceSize; + workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace; + usedSpaceInWorkspace[workSpacesAllocated] = 0; + ++workSpacesAllocated; + + + //brushes + { + int iBrush = 0; + CUtlVector<Vector4D> Planes; + + float fStackPlanes[4 * 400]; //400 is a crapload of planes in my opinion + + while( enginetrace->GetBrushInfo( iBrush, &Planes, NULL ) ) + { + int iPlaneCount = Planes.Count(); + AssertMsg( iPlaneCount != 0, "A brush with no planes???????" ); + + const Vector4D *pReturnedPlanes = Planes.Base(); + + CPolyhedron *pTempPolyhedron; + + if( iPlaneCount > 400 ) + { + // o_O, we'll have to get more memory to transform this brush + float *pNonstackPlanes = new float [4 * iPlaneCount]; + + for( int i = 0; i != iPlaneCount; ++i ) + { + pNonstackPlanes[(i * 4) + 0] = pReturnedPlanes[i].x; + pNonstackPlanes[(i * 4) + 1] = pReturnedPlanes[i].y; + pNonstackPlanes[(i * 4) + 2] = pReturnedPlanes[i].z; + pNonstackPlanes[(i * 4) + 3] = pReturnedPlanes[i].w; + } + + pTempPolyhedron = GeneratePolyhedronFromPlanes( pNonstackPlanes, iPlaneCount, 0.01f, true ); + + delete []pNonstackPlanes; + } + else + { + for( int i = 0; i != iPlaneCount; ++i ) + { + fStackPlanes[(i * 4) + 0] = pReturnedPlanes[i].x; + fStackPlanes[(i * 4) + 1] = pReturnedPlanes[i].y; + fStackPlanes[(i * 4) + 2] = pReturnedPlanes[i].z; + fStackPlanes[(i * 4) + 3] = pReturnedPlanes[i].w; + } + + pTempPolyhedron = GeneratePolyhedronFromPlanes( fStackPlanes, iPlaneCount, 0.01f, true ); + } + + if( pTempPolyhedron ) + { + size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) + + (sizeof( Vector ) * pTempPolyhedron->iVertexCount) + + (sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) + + (sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) + + (sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount); + + Assert( memRequired < workSpaceSize ); + + if( roomLeftInWorkSpace < memRequired ) + { + usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace; + + pCurrentWorkSpace = new uint8 [workSpaceSize]; + roomLeftInWorkSpace = workSpaceSize; + workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace; + usedSpaceInWorkspace[workSpacesAllocated] = 0; + ++workSpacesAllocated; + } + + CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace, + pTempPolyhedron->iVertexCount, + pTempPolyhedron->iLineCount, + pTempPolyhedron->iIndexCount, + pTempPolyhedron->iPolygonCount ); + + pCurrentWorkSpace += memRequired; + roomLeftInWorkSpace -= memRequired; + + memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) ); + memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) ); + memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) ); + memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) ); + + m_BrushPolyhedrons.AddToTail( pWorkSpacePolyhedron ); + + pTempPolyhedron->Release(); + } + else + { + m_BrushPolyhedrons.AddToTail( NULL ); + } + + ++iBrush; + } + + usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace; + + if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used. + { + //consolidate workspaces into a single memory chunk + size_t totalMemoryNeeded = 0; + for( unsigned int i = 0; i != workSpacesAllocated; ++i ) + { + totalMemoryNeeded += usedSpaceInWorkspace[i]; + } + + uint8 *pFinalDest = new uint8 [totalMemoryNeeded]; + s_BrushPolyhedronMemory = pFinalDest; + + DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d brush polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_BrushPolyhedrons.Count() ); + + int iCount = m_BrushPolyhedrons.Count(); + for( int i = 0; i != iCount; ++i ) + { + CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_BrushPolyhedrons[i]; + + if( pSource == NULL ) + continue; + + size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) + + (sizeof( Vector ) * pSource->iVertexCount) + + (sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) + + (sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) + + (sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount); + + CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest; + m_BrushPolyhedrons[i] = pDest; + pFinalDest += memRequired; + + int memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource); + + memcpy( pDest, pSource, memRequired ); + //move all the pointers to their new location. + pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset); + pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset); + pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset); + pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset); + } + } + } + + unsigned int iBrushWorkSpaces = workSpacesAllocated; + workSpacesAllocated = 1; + pCurrentWorkSpace = workSpaceAllocations[0]; + usedSpaceInWorkspace[0] = 0; + roomLeftInWorkSpace = workSpaceSize; + + //static props + { + CUtlVector<ICollideable *> StaticPropCollideables; + staticpropmgr->GetAllStaticProps( &StaticPropCollideables ); + + if( StaticPropCollideables.Count() != 0 ) + { + ICollideable **pCollideables = StaticPropCollideables.Base(); + ICollideable **pStop = pCollideables + StaticPropCollideables.Count(); + + int iStaticPropIndex = 0; + do + { + ICollideable *pProp = *pCollideables; + vcollide_t *pCollide = modelinfo->GetVCollide( pProp->GetCollisionModel() ); + StaticPropPolyhedronCacheInfo_t cacheInfo; + cacheInfo.iStartIndex = m_StaticPropPolyhedrons.Count(); + + if( pCollide != NULL ) + { + VMatrix matToWorldPosition = pProp->CollisionToWorldTransform(); + + for( int i = 0; i != pCollide->solidCount; ++i ) + { + CPhysConvex *ConvexesArray[1024]; + int iConvexes = physcollision->GetConvexesUsedInCollideable( pCollide->solids[i], ConvexesArray, 1024 ); + + for( int j = 0; j != iConvexes; ++j ) + { + CPolyhedron *pTempPolyhedron = physcollision->PolyhedronFromConvex( ConvexesArray[j], true ); + if( pTempPolyhedron ) + { + for( int iPointCounter = 0; iPointCounter != pTempPolyhedron->iVertexCount; ++iPointCounter ) + pTempPolyhedron->pVertices[iPointCounter] = matToWorldPosition * pTempPolyhedron->pVertices[iPointCounter]; + + for( int iPolyCounter = 0; iPolyCounter != pTempPolyhedron->iPolygonCount; ++iPolyCounter ) + pTempPolyhedron->pPolygons[iPolyCounter].polyNormal = matToWorldPosition.ApplyRotation( pTempPolyhedron->pPolygons[iPolyCounter].polyNormal ); + + + size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) + + (sizeof( Vector ) * pTempPolyhedron->iVertexCount) + + (sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) + + (sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) + + (sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount); + + Assert( memRequired < workSpaceSize ); + + if( roomLeftInWorkSpace < memRequired ) + { + usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace; + + if( workSpacesAllocated < iBrushWorkSpaces ) + { + //re-use a workspace already allocated during brush polyhedron conversion + pCurrentWorkSpace = workSpaceAllocations[workSpacesAllocated]; + usedSpaceInWorkspace[workSpacesAllocated] = 0; + } + else + { + //allocate a new workspace + pCurrentWorkSpace = new uint8 [workSpaceSize]; + workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace; + usedSpaceInWorkspace[workSpacesAllocated] = 0; + } + + roomLeftInWorkSpace = workSpaceSize; + ++workSpacesAllocated; + } + + CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace, + pTempPolyhedron->iVertexCount, + pTempPolyhedron->iLineCount, + pTempPolyhedron->iIndexCount, + pTempPolyhedron->iPolygonCount ); + + pCurrentWorkSpace += memRequired; + roomLeftInWorkSpace -= memRequired; + + memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) ); + memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) ); + memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) ); + memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) ); + + m_StaticPropPolyhedrons.AddToTail( pWorkSpacePolyhedron ); + +#ifdef _DEBUG + CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pTempPolyhedron ); + AssertMsg( pConvex != NULL, "Conversion from Convex to Polyhedron was unreversable" ); + if( pConvex ) + { + physcollision->ConvexFree( pConvex ); + } +#endif + + pTempPolyhedron->Release(); + } + } + } + + cacheInfo.iNumPolyhedrons = m_StaticPropPolyhedrons.Count() - cacheInfo.iStartIndex; + cacheInfo.iStaticPropIndex = iStaticPropIndex; + Assert( staticpropmgr->GetStaticPropByIndex( iStaticPropIndex ) == pProp ); + + m_CollideableIndicesMap.InsertOrReplace( pProp, cacheInfo ); + } + + ++iStaticPropIndex; + ++pCollideables; + } while( pCollideables != pStop ); + + + usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace; + + if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used. + { + //consolidate workspaces into a single memory chunk + size_t totalMemoryNeeded = 0; + for( unsigned int i = 0; i != workSpacesAllocated; ++i ) + { + totalMemoryNeeded += usedSpaceInWorkspace[i]; + } + + uint8 *pFinalDest = new uint8 [totalMemoryNeeded]; + s_StaticPropPolyhedronMemory = pFinalDest; + + DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d static prop polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_StaticPropPolyhedrons.Count() ); + + int iCount = m_StaticPropPolyhedrons.Count(); + for( int i = 0; i != iCount; ++i ) + { + CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_StaticPropPolyhedrons[i]; + + size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) + + (sizeof( Vector ) * pSource->iVertexCount) + + (sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) + + (sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) + + (sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount); + + CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest; + m_StaticPropPolyhedrons[i] = pDest; + pFinalDest += memRequired; + + int memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource); + + memcpy( pDest, pSource, memRequired ); + //move all the pointers to their new location. + pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset); + pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset); + pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset); + pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset); + } + } + } + } + + if( iBrushWorkSpaces > workSpacesAllocated ) + workSpacesAllocated = iBrushWorkSpaces; + + for( unsigned int i = 0; i != workSpacesAllocated; ++i ) + { + delete []workSpaceAllocations[i]; + } +} + + + +const CPolyhedron *CStaticCollisionPolyhedronCache::GetBrushPolyhedron( int iBrushNumber ) +{ + Assert( iBrushNumber < m_BrushPolyhedrons.Count() ); + + if( (iBrushNumber < 0) || (iBrushNumber >= m_BrushPolyhedrons.Count()) ) + return NULL; + + return m_BrushPolyhedrons[iBrushNumber]; +} + +int CStaticCollisionPolyhedronCache::GetStaticPropPolyhedrons( ICollideable *pStaticProp, CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize ) +{ + unsigned short iPropIndex = m_CollideableIndicesMap.Find( pStaticProp ); + if( !m_CollideableIndicesMap.IsValidIndex( iPropIndex ) ) //static prop never made it into the cache for some reason (specifically no collision data when this workaround was written) + return 0; + + StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element( iPropIndex ); + + if( cacheInfo.iNumPolyhedrons < iOutputArraySize ) + iOutputArraySize = cacheInfo.iNumPolyhedrons; + + for( int i = cacheInfo.iStartIndex, iWriteIndex = 0; iWriteIndex != iOutputArraySize; ++i, ++iWriteIndex ) + { + pOutputPolyhedronArray[iWriteIndex] = m_StaticPropPolyhedrons[i]; + } + + return iOutputArraySize; +} + + + + + + diff --git a/game/shared/portal/StaticCollisionPolyhedronCache.h b/game/shared/portal/StaticCollisionPolyhedronCache.h new file mode 100644 index 0000000..b759317 --- /dev/null +++ b/game/shared/portal/StaticCollisionPolyhedronCache.h @@ -0,0 +1,52 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Portals use polyhedrons to clip and carve their custom collision areas. +// This file should provide caches of polyhedrons with the initial conversion +// processes already completed. +// +// $NoKeywords: $ +//=====================================================================================// + + +#include "igamesystem.h" +#include "mathlib/polyhedron.h" +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" +#include "tier1/utlmap.h" + + + +class CStaticCollisionPolyhedronCache : public CAutoGameSystem +{ +public: + CStaticCollisionPolyhedronCache( void ); + ~CStaticCollisionPolyhedronCache( void ); + + void LevelInitPreEntity( void ); + void Shutdown( void ); + + const CPolyhedron *GetBrushPolyhedron( int iBrushNumber ); + int GetStaticPropPolyhedrons( ICollideable *pStaticProp, CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize ); + +private: + // See comments in LevelInitPreEntity for why these members are commented out +// CUtlString m_CachedMap; + + CUtlVector<CPolyhedron *> m_BrushPolyhedrons; + + struct StaticPropPolyhedronCacheInfo_t + { + int iStartIndex; + int iNumPolyhedrons; + int iStaticPropIndex; //helps us remap ICollideable pointers when the map is restarted + }; + + CUtlVector<CPolyhedron *> m_StaticPropPolyhedrons; + CUtlMap<ICollideable *, StaticPropPolyhedronCacheInfo_t> m_CollideableIndicesMap; + + + void Clear( void ); + void Update( void ); +}; + +extern CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache; diff --git a/game/shared/portal/achievements_portal.cpp b/game/shared/portal/achievements_portal.cpp new file mode 100644 index 0000000..bf2a49e --- /dev/null +++ b/game/shared/portal/achievements_portal.cpp @@ -0,0 +1,547 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + + +#include "cbase.h" +#include "achievementmgr.h" +#include "baseachievement.h" + +#ifdef GAME_DLL +#include "prop_portal.h" +#include "util.h" + +CAchievementMgr g_AchievementMgrPortal; // global achievement mgr for Portal + +class CAchievementPortalInfiniteFall : public CBaseAchievement +{ + DECLARE_CLASS( CAchievementPortalInfiniteFall, CBaseAchievement ); + +public: + void Init() + { + SetFlags( ACH_SAVE_WITH_GAME ); + SetGameDirFilter( "portal" ); + SetGoal( 1 ); + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "portal_player_portaled" ); + ListenForGameEvent( "portal_player_touchedground" ); + } + virtual void PreRestoreSavedGame() + { + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + BaseClass::PreRestoreSavedGame(); + } + +protected: + virtual void FireGameEvent( IGameEvent *event ) + { + const char *name = event->GetName(); + if ( 0 == Q_strcmp( name, "portal_player_portaled" ) ) + { + bool bIsPortal2 = event->GetBool( "portal2", false ); + // Get the portals that they teleported through + CProp_Portal *pInPortal = CProp_Portal::FindPortal( 0, bIsPortal2, false ); + CProp_Portal *pOutPortal = CProp_Portal::FindPortal( 0, !bIsPortal2, false ); + + if ( pInPortal && pOutPortal ) + { + if ( m_bIsFlinging ) + { + // Add up how far we traveled since the last teleport + m_fAccumulatedDistance += m_fZPortalPosition - pInPortal->GetAbsOrigin().z; + + if ( m_fAccumulatedDistance > 30000.0f * 12 ) + IncrementCount(); + } + + // Remember the Z position to get the distance when the teleport again or land + m_fZPortalPosition = pOutPortal->GetAbsOrigin().z; + m_bIsFlinging = true; + } + } + else if ( 0 == Q_strcmp( name, "portal_player_touchedground" ) ) + { + if ( m_bIsFlinging ) + { + CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer(); + + if ( pLocalPlayer ) + { + m_fAccumulatedDistance += m_fZPortalPosition - pLocalPlayer->GetAbsOrigin().z; + + if ( m_fAccumulatedDistance > 30000.0f * 12 ) + IncrementCount(); + + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + } + } + } + } + +private: + bool m_bIsFlinging; + float m_fAccumulatedDistance; + float m_fZPortalPosition; +}; +DECLARE_ACHIEVEMENT( CAchievementPortalInfiniteFall, ACHIEVEMENT_PORTAL_INFINITEFALL, "PORTAL_INFINITEFALL", 5 ); + +class CAchievementPortalLongJump : public CBaseAchievement +{ + DECLARE_CLASS( CAchievementPortalLongJump, CBaseAchievement ); + +public: + void Init() + { + SetFlags( ACH_SAVE_WITH_GAME ); + SetGameDirFilter( "portal" ); + SetGoal( 1 ); + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "portal_player_portaled" ); + ListenForGameEvent( "portal_player_touchedground" ); + } + virtual void PreRestoreSavedGame() + { + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + BaseClass::PreRestoreSavedGame(); + } + +protected: + virtual void FireGameEvent( IGameEvent *event ) + { + const char *name = event->GetName(); + if ( 0 == Q_strcmp( name, "portal_player_portaled" ) ) + { + bool bIsPortal2 = event->GetBool( "portal2", false ); + // Get the portals that they teleported through + CProp_Portal *pInPortal = CProp_Portal::FindPortal( 0, bIsPortal2, false ); + CProp_Portal *pOutPortal = CProp_Portal::FindPortal( 0, !bIsPortal2, false ); + + if ( pInPortal && pOutPortal ) + { + if ( m_bIsFlinging ) + { + // Add up how far we traveled since the last teleport + float flDist = pInPortal->GetAbsOrigin().AsVector2D().DistTo( m_vec2DPortalPosition ); + + // Ignore small distances that can be caused by microadjustments in infinite falls + if ( flDist > 63.0f ) + { + m_fAccumulatedDistance += flDist; + } + + if ( m_fAccumulatedDistance > 300.0f * 12 ) + IncrementCount(); + } + + // Remember the 2D position to get the distance when the teleport again or land + m_vec2DPortalPosition = pOutPortal->GetAbsOrigin().AsVector2D(); + m_bIsFlinging = true; + } + } + else if ( 0 == Q_strcmp( name, "portal_player_touchedground" ) ) + { + if ( m_bIsFlinging ) + { + CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer(); + + if ( pLocalPlayer ) + { + float flDist = pLocalPlayer->GetAbsOrigin().AsVector2D().DistTo( m_vec2DPortalPosition ); + + // Ignore small distances that can be caused by microadjustments in infinite falls + if ( flDist > 63.0f ) + { + m_fAccumulatedDistance += flDist; + } + + if ( m_fAccumulatedDistance > 300.0f * 12 ) + IncrementCount(); + + m_fAccumulatedDistance = 0.0f; + m_bIsFlinging = false; + } + } + } + } + +private: + bool m_bIsFlinging; + float m_fAccumulatedDistance; + Vector2D m_vec2DPortalPosition; +}; +DECLARE_ACHIEVEMENT( CAchievementPortalLongJump, ACHIEVEMENT_PORTAL_LONGJUMP, "PORTAL_LONGJUMP", 5 ); + +class CAchievementPortalBeat2AdvancedMaps: public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 2 ); + m_iProgressMsgMinimum = 0; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "advanced_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iNumAdvanced = event->GetInt( "numadvanced" ); + + SetCount ( iNumAdvanced ); + if ( iNumAdvanced >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalBeat2AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_2ADVANCEDMAPS, "PORTAL_BEAT_2ADVANCEDMAPS", 10 ); + +class CAchievementPortalBeat4AdvancedMaps : public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 4 ); + m_iProgressMsgMinimum = 3; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "advanced_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iNumAdvanced = event->GetInt( "numadvanced" ); + + SetCount ( iNumAdvanced ); + if ( iNumAdvanced >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalBeat4AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_4ADVANCEDMAPS, "PORTAL_BEAT_4ADVANCEDMAPS", 20 ); + +class CAchievementPortalBeat6AdvancedMaps : public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 6 ); + m_iProgressMsgMinimum = 5; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "advanced_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iNumAdvanced = event->GetInt( "numadvanced" ); + + SetCount ( iNumAdvanced ); + if ( iNumAdvanced >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalBeat6AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_6ADVANCEDMAPS, "PORTAL_BEAT_6ADVANCEDMAPS", 30 ); + +class CAchievementPortalGetAllBronze : public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 18 ); + } + virtual void ListenForEvents() + { + ListenForGameEvent( "challenge_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iBronzeCount = event->GetInt( "numbronze" ); + + SetCount ( iBronzeCount ); + if ( iBronzeCount >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalGetAllBronze, ACHIEVEMENT_PORTAL_GET_ALLBRONZE, "PORTAL_GET_ALLBRONZE", 10 ); + +class CAchievementPortalGetAllSilver : public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 18 ); + } + virtual void ListenForEvents() + { + ListenForGameEvent( "challenge_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iSilverCount = event->GetInt( "numsilver" ); + + SetCount ( iSilverCount ); + if ( iSilverCount >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalGetAllSilver, ACHIEVEMENT_PORTAL_GET_ALLSILVER, "PORTAL_GET_ALLSILVER", 20 ); + +class CAchievementPortalGetAllGold : public CBaseAchievement +{ +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 18 ); + } + virtual void ListenForEvents() + { + ListenForGameEvent( "challenge_map_complete" ); + } + +protected: + virtual void FireGameEvent( IGameEvent* event ) + { + if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) ) + { + if ( !IsAchieved() ) + { + int iGoldCount = event->GetInt( "numgold" ); + + SetCount ( iGoldCount ); + if ( iGoldCount >= GetGoal() ) + { + AwardAchievement(); + } + else + { + HandleProgressUpdate(); + } + } + } + } + virtual void CalcProgressMsgIncrement() + { + // show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalGetAllGold, ACHIEVEMENT_PORTAL_GET_ALLGOLD, "PORTAL_GET_ALLGOLD", 40 ); + + +class CAchievementPortalDetachAllCameras : public CBaseAchievement +{ +protected: + virtual void ListenForEvents() + { + ListenForGameEvent( "security_camera_detached" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( 0 == Q_strcmp( event->GetName(), "security_camera_detached" ) ) + { + IncrementCount(); + } + } +public: + virtual void Init() + { + SetFlags( ACH_SAVE_WITH_GAME ); + SetGoal( 33 ); + ListenForGameEvent( "security_camera_detached" ); + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalDetachAllCameras, ACHIEVEMENT_PORTAL_DETACH_ALL_CAMERAS, "PORTAL_DETACH_ALL_CAMERAS", 5 ); + + +class CAchievementPortalHitTurretWithTurret : public CBaseAchievement +{ +protected: + virtual void ListenForEvents() + { + ListenForGameEvent( "turret_hit_turret" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( 0 == Q_strcmp( event->GetName(), "turret_hit_turret" ) ) + { + IncrementCount(); + } + } +public: + virtual void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + ListenForGameEvent( "turret_hit_turret" ); + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalHitTurretWithTurret, ACHIEVEMENT_PORTAL_HIT_TURRET_WITH_TURRET, "PORTAL_HIT_TURRET_WITH_TURRET", 5 ); + + +#ifndef _XBOX +class CAchievementPortalFindAllDinosaurs : public CBaseAchievement +{ + DECLARE_CLASS( CAchievementPortalFindAllDinosaurs, CBaseAchievement ); + void Init() + { + SetFlags( ACH_HAS_COMPONENTS | ACH_SAVE_GLOBAL ); + m_iNumComponents = 26; + SetStoreProgressInSteam( true ); + SetGoal( m_iNumComponents ); + BaseClass::Init(); + m_iProgressMsgMinimum = 1; + } + virtual void ListenForEvents() + { + ListenForGameEvent( "dinosaur_signal_found" ); + } + virtual void FireGameEvent_Internal( IGameEvent *event ) + { + if ( 0 == Q_strcmp( event->GetName(), "dinosaur_signal_found" ) ) + { + int id = event->GetInt( "id", -1 ); + Assert( id >= 0 && id < m_iNumComponents ); + if ( id >= 0 && id < m_iNumComponents ) + { + EnsureComponentBitSetAndEvaluate( id ); + + // Update our Steam stat + steamapicontext->SteamUserStats()->SetStat( "PORTAL_TRANSMISSION_RECEIVED_STAT", m_iCount ); + } + else + { + Warning( "Failed to set achievement progress. Dinosaur ID(%d) out of range (0 to %d)\n", id, m_iNumComponents ); + } + } + } + virtual void CalcProgressMsgIncrement() + { + // Show progress every tick + m_iProgressMsgIncrement = 1; + } +}; +DECLARE_ACHIEVEMENT( CAchievementPortalFindAllDinosaurs, ACHIEVEMENT_PORTAL_TRANSMISSION_RECEIVED, "PORTAL_TRANSMISSION_RECEIVED", 0 ); +#endif // _XBOX + +// achievements which are won by a map event firing once +DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_GET_PORTALGUNS, "PORTAL_GET_PORTALGUNS", 5 ); +DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_KILL_COMPANIONCUBE, "PORTAL_KILL_COMPANIONCUBE", 5 ); +DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_ESCAPE_TESTCHAMBERS, "PORTAL_ESCAPE_TESTCHAMBERS", 5 ); +DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_BEAT_GAME, "PORTAL_BEAT_GAME", 10 ); + +#endif // GAME_DLL diff --git a/game/shared/portal/env_lightrail_endpoint_shared.h b/game/shared/portal/env_lightrail_endpoint_shared.h new file mode 100644 index 0000000..8b65883 --- /dev/null +++ b/game/shared/portal/env_lightrail_endpoint_shared.h @@ -0,0 +1,69 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ENV_LIGHTRAIL_ENDPOINT_SHARED_H +#define ENV_LIGHTRAIL_ENDPOINT_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + +#define SF_ENDPOINT_START_SMALLFX (1<<0) //Define spawnflags +//#define SF_ENDPOINT_START_LARGEFX (1<<1) + +enum //Enumeration of the 4 states the endpoints can be in. +{ + ENDPOINT_STATE_OFF, //No FX displayed + ENDPOINT_STATE_SMALLFX, //Just the small particle trail is displayed and a faint glow + ENDPOINT_STATE_CHARGING, //Ramp up over a certain amount of time to the large bright glow + ENDPOINT_STATE_LARGEFX, //Shows a particle trail and a large bright glow + ENDPOINT_STATE_COUNT, +}; + +#ifndef CLIENT_DLL + +// ============================================================================ +// +// Energy core - charges up and then releases energy from its position +// +// ============================================================================ + +class CEnv_Lightrail_Endpoint : public CBaseEntity +{ + DECLARE_CLASS( CEnv_Lightrail_Endpoint, CBaseEntity ); + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + +public: + void InputStartCharge( inputdata_t &inputdata ); + void InputStartSmallFX(inputdata_t &inputdata ); + void InputStartLargeFX( inputdata_t &inputdata ); + void InputStop( inputdata_t &inputdata ); + void SetSmallFXScale( float flSmallScale ) { m_flSmallScale = flSmallScale; } + void SetLargeFXScale( float flLargeScale ) { m_flLargeScale = flLargeScale; } + + void StartCharge( float flWarmUpTime ); //Charging difference between the small and large fx + void StartSmallFX(); //Start discharging the scaled down version of the FX + void StartLargeFX(); //Start discharging the larger brighter version of the FX + void StopSmallFX( float flCoolDownTime ); //Stop discharging the small fx + void StopLargeFX( float flCoolDownTime ); //Stop discharging the small fx + + virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo ); + virtual int UpdateTransmitState( void ); + + virtual void Precache(); + void Spawn( void ); + +private: + CNetworkVar( float, m_flSmallScale ); //Scale of the small fx + CNetworkVar( float, m_flLargeScale ); //Scale of the large fx + CNetworkVar( int, m_nState ); //Current state of the fx + CNetworkVar( float, m_flDuration ); + CNetworkVar( float, m_flStartTime ); +}; + +#endif + +#endif // ENV_LIGHTRAIL_ENDPOINT_SHARED_H diff --git a/game/shared/portal/env_portal_path_track_shared.h b/game/shared/portal/env_portal_path_track_shared.h new file mode 100644 index 0000000..440732f --- /dev/null +++ b/game/shared/portal/env_portal_path_track_shared.h @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: A version of path_track which draws. +// +//=============================================================================// + +#ifndef ENV_PORTAL_PATH_TRACK_SHARED_H +#define ENV_PORTAL_PATH_TRACK_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + +// States for track drawing +enum +{ + PORTAL_PATH_TRACK_STATE_OFF, + PORTAL_PATH_TRACK_STATE_INACTIVE, + PORTAL_PATH_TRACK_STATE_ACTIVE, + PORTAL_PATH_TRACK_STATE_COUNT +}; + + +#ifndef CLIENT_DLL + +#include "pathtrack.h" + +class CBeam; + +//============================================================== +// +//============================================================== +class CEnvPortalPathTrack : public CPathTrack +{ + DECLARE_CLASS( CEnvPortalPathTrack, CPathTrack ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + +public: + CEnvPortalPathTrack(); + ~CEnvPortalPathTrack(); + virtual void Precache(); + void Spawn( void ); + void Activate( void ); + + void InitTrackFX(); + void ShutDownTrackFX(); + void InitEndpointFX(); + void ShutDownEndpointFX(); + + void InputActivateTrack( inputdata_t &inputdata ); + void InputActivateEndpoint( inputdata_t &inputdata ); + + void InputDeactivateTrack( inputdata_t &inputdata ); + void InputDeactivateEndpoint( inputdata_t &inputdata ); + + void ActivateTrackFX ( void ); //Activate all of the track's beams (at least the ones that are flagged to display) + void ActivateEndpointFX ( void ); //Activate all of the endpoint's glowy bits that are flagged to display + + void DeactivateTrackFX ( void ); //Activate all of the track's beams (at least the ones that are flagged to display) + void DeactivateEndpointFX ( void ); //Activate all of the endpoint's glowy bits that are flagged to display + +protected: + CNetworkVar( bool, m_bTrackActive ); + CNetworkVar( bool, m_bEndpointActive ); +// CNetworkVar( float, m_fScaleEndpoint ); // Scale of the endpoint for this beam +// CNetworkVar( float, m_fScaleTrack ); // Scale of the track effect +// CNetworkVar( float, m_fFadeOutEndpoint ); // Scale of the track effect +// CNetworkVar( float, m_fFadeInEndpoint ); // Scale of the track effect + CNetworkVar( int, m_nState ); // particle emmision state + + COutputEvent m_OnActivatedEndpoint; + + CBeam *m_pBeam; // Pointer to look at a cbeam object for the track fx + +}; + +#endif // CLIENT_DLL + +#endif //ENV_PORTAL_PATH_TRACK_SHARED_H
\ No newline at end of file diff --git a/game/shared/portal/portal_collideable_enumerator.cpp b/game/shared/portal/portal_collideable_enumerator.cpp new file mode 100644 index 0000000..6cd2742 --- /dev/null +++ b/game/shared/portal/portal_collideable_enumerator.cpp @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "prop_portal_shared.h" +#include "portal_collideable_enumerator.h" + +#define PORTAL_TELEPORTATION_PLANE_OFFSET 7.0f + +CPortalCollideableEnumerator::CPortalCollideableEnumerator( const CProp_Portal *pAssociatedPortal ) +{ + Assert( pAssociatedPortal ); + m_hTestPortal = pAssociatedPortal; + + pAssociatedPortal->GetVectors( &m_vPlaneNormal, NULL, NULL ); + + m_ptForward1000 = pAssociatedPortal->GetAbsOrigin(); + m_ptForward1000 += m_vPlaneNormal * PORTAL_TELEPORTATION_PLANE_OFFSET; + m_fPlaneDist = m_vPlaneNormal.Dot( m_ptForward1000 ); + + m_ptForward1000 += m_vPlaneNormal * 1000.0f; + + m_iHandleCount = 0; +} + +IterationRetval_t CPortalCollideableEnumerator::EnumElement( IHandleEntity *pHandleEntity ) +{ + EHANDLE hEnt = pHandleEntity->GetRefEHandle(); + + CBaseEntity *pEnt = hEnt.Get(); + if( pEnt == NULL ) //I really never thought this would be necessary + return ITERATION_CONTINUE; + + if( hEnt == m_hTestPortal ) + return ITERATION_CONTINUE; //ignore this portal + + /*if( staticpropmgr->IsStaticProp( pHandleEntity ) ) + { + //we're dealing with a static prop, which unfortunately doesn't have everything I want to use for checking + + ICollideable *pCollideable = pEnt->GetCollideable(); + + Vector vMins, vMaxs; + pCollideable->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); + + Vector ptTest( (m_vPlaneNormal.x > 0.0f)?(vMaxs.x):(vMins.x), + (m_vPlaneNormal.y > 0.0f)?(vMaxs.y):(vMins.y), + (m_vPlaneNormal.z > 0.0f)?(vMaxs.z):(vMins.z) ); + + float fPtPlaneDist = m_vPlaneNormal.Dot( ptTest ) - m_fPlaneDist; + if( fPtPlaneDist <= 0.0f ) + return ITERATION_CONTINUE; + } + else*/ + { + //not a static prop, w00t + CCollisionProperty *pEntityCollision = pEnt->CollisionProp(); + + if( !pEntityCollision->IsSolid() ) + return ITERATION_CONTINUE; //not solid + + Vector ptEntCenter = pEntityCollision->WorldSpaceCenter(); + + float fBoundRadius = pEntityCollision->BoundingRadius(); + float fPtPlaneDist = m_vPlaneNormal.Dot( ptEntCenter ) - m_fPlaneDist; + + if( fPtPlaneDist < -fBoundRadius ) + return ITERATION_CONTINUE; //object wholly behind the portal + + if( !(fPtPlaneDist > fBoundRadius) && (fPtPlaneDist > -fBoundRadius) ) //object is not wholly in front of the portal, but could be partially in front, do more checks + { + Vector ptNearest; + pEntityCollision->CalcNearestPoint( m_ptForward1000, &ptNearest ); + fPtPlaneDist = m_vPlaneNormal.Dot( ptNearest ) - m_fPlaneDist; + if( fPtPlaneDist < 0.0f ) + return ITERATION_CONTINUE; //closest point was behind the portal plane, we don't want it + } + + + } + + //if we're down here, this entity needs to be added to our enumeration + Assert( m_iHandleCount < 1024 ); + if( m_iHandleCount < 1024 ) + m_pHandles[m_iHandleCount] = pHandleEntity; + ++m_iHandleCount; + + return ITERATION_CONTINUE; +} + + + + + + + + + + diff --git a/game/shared/portal/portal_collideable_enumerator.h b/game/shared/portal/portal_collideable_enumerator.h new file mode 100644 index 0000000..3294131 --- /dev/null +++ b/game/shared/portal/portal_collideable_enumerator.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PORTAL_COLLIDEABLE_ENUMERATOR_H +#define PORTAL_COLLIDEABLE_ENUMERATOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "ispatialpartition.h" + +#ifdef CLIENT_DLL +class C_Prop_Portal; +typedef C_Prop_Portal CProp_Portal; +#else +class CProp_Portal; +#endif + +//only enumerates entities in front of the associated portal and are solid (as in a player would get stuck in them) +class CPortalCollideableEnumerator : public IPartitionEnumerator +{ +private: + EHANDLE m_hTestPortal; //the associated portal that we only want objects in front of + Vector m_vPlaneNormal; //portal plane normal + float m_fPlaneDist; //plane equation distance + Vector m_ptForward1000; //a point exactly 1000 units from the portal center along its forward vector +public: + IHandleEntity *m_pHandles[1024]; + int m_iHandleCount; + CPortalCollideableEnumerator( const CProp_Portal *pAssociatedPortal ); + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); +}; + + + +#endif //#ifndef PORTAL_COLLIDEABLE_ENUMERATOR_H
\ No newline at end of file diff --git a/game/shared/portal/portal_game_account.h b/game/shared/portal/portal_game_account.h new file mode 100644 index 0000000..6888be6 --- /dev/null +++ b/game/shared/portal/portal_game_account.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Holds the CPortalGameAccount object +// +// $NoKeywords: $ +//=============================================================================// + +#include "gcsdk/schemasharedobject.h" + +//--------------------------------------------------------------------------------- +// Purpose: All the account-level information that the GC tracks for TF +//--------------------------------------------------------------------------------- +class CPortalGameAccount : public GCSDK::CSchemaSharedObject< CSchGameAccount > +{ +public: + CPortalGameAccount() : GCSDK::CSchemaSharedObject< CSchGameAccount >() {} + CPortalGameAccount( uint32 unAccountID ) : GCSDK::CSchemaSharedObject< CSchGameAccount >() + { + Obj().m_unAccountID = unAccountID; + } +}; diff --git a/game/shared/portal/portal_game_account_client.h b/game/shared/portal/portal_game_account_client.h new file mode 100644 index 0000000..ffb2cad --- /dev/null +++ b/game/shared/portal/portal_game_account_client.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Holds the CPortalGameAccount object +// +// $NoKeywords: $ +//=============================================================================// + +#ifdef USE_GC_IN_PORTAL1 +#include "gcsdk/schemasharedobject.h" + +//--------------------------------------------------------------------------------- +// Purpose: All the account-level information that the GC tracks for Portal +//--------------------------------------------------------------------------------- +class CPortalGameAccountClient : public GCSDK::CSchemaSharedObject< CSchGameAccountClient > +{ +public: + CPortalGameAccountClient() : GCSDK::CSchemaSharedObject< CSchGameAccountClient >() {} + CPortalGameAccountClient( uint32 unAccountID ) : GCSDK::CSchemaSharedObject< CSchGameAccountClient >() + { + Obj().m_unAccountID = unAccountID; + } + + +}; +#endif //#ifdef USE_GC_IN_PORTAL1
\ No newline at end of file diff --git a/game/shared/portal/portal_gamemovement.cpp b/game/shared/portal/portal_gamemovement.cpp new file mode 100644 index 0000000..a966290 --- /dev/null +++ b/game/shared/portal/portal_gamemovement.cpp @@ -0,0 +1,752 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Special handling for Portal usable ladders +// +//=============================================================================// +#include "cbase.h" +#include "hl_gamemovement.h" +#include "in_buttons.h" +#include "utlrbtree.h" +#include "movevars_shared.h" +#include "portal_shareddefs.h" +#include "portal_collideable_enumerator.h" +#include "prop_portal_shared.h" +#include "rumble_shared.h" + +#if defined( CLIENT_DLL ) + #include "c_portal_player.h" + #include "c_rumble.h" +#else + #include "portal_player.h" + #include "env_player_surface_trigger.h" + #include "portal_gamestats.h" + #include "physicsshadowclone.h" + #include "recipientfilter.h" + #include "SoundEmitterSystem/isoundemittersystembase.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar sv_player_trace_through_portals("sv_player_trace_through_portals", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Causes player movement traces to trace through portals." ); +ConVar sv_player_funnel_into_portals("sv_player_funnel_into_portals", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Causes the player to auto correct toward the center of floor portals." ); + +class CReservePlayerSpot; + +#define PORTAL_FUNNEL_AMOUNT 6.0f + +extern bool g_bAllowForcePortalTrace; +extern bool g_bForcePortalTrace; + +static inline CBaseEntity *TranslateGroundEntity( CBaseEntity *pGroundEntity ) +{ +#ifndef CLIENT_DLL + CPhysicsShadowClone *pClone = dynamic_cast<CPhysicsShadowClone *>(pGroundEntity); + + if( pClone && pClone->IsUntransformedClone() ) + { + CBaseEntity *pSource = pClone->GetClonedEntity(); + + if( pSource ) + return pSource; + } +#endif //#ifndef CLIENT_DLL + + return pGroundEntity; +} + + +//----------------------------------------------------------------------------- +// Purpose: Portal specific movement code +//----------------------------------------------------------------------------- +class CPortalGameMovement : public CHL2GameMovement +{ + typedef CGameMovement BaseClass; +public: + + CPortalGameMovement(); + + bool m_bInPortalEnv; +// Overrides + virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ); + virtual bool CheckJumpButton( void ); + + void FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir ); + + virtual void AirAccelerate( Vector& wishdir, float wishspeed, float accel ); + virtual void AirMove( void ); + + virtual void PlayerRoughLandingEffects( float fvol ); + + virtual void CategorizePosition( void ); + + // Traces the player bbox as it is swept from start to end + virtual void TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm ); + + // Tests the player position + virtual CBaseHandle TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm ); + + virtual void Duck( void ); // Check for a forced duck + + virtual int CheckStuck( void ); + + virtual void SetGroundEntity( trace_t *pm ); + +private: + + + CPortal_Player *GetPortalPlayer(); +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPortalGameMovement::CPortalGameMovement() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline CPortal_Player *CPortalGameMovement::GetPortalPlayer() +{ + return static_cast< CPortal_Player * >( player ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pMove - +//----------------------------------------------------------------------------- +void CPortalGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ) +{ + Assert( pMove && pPlayer ); + + float flStoreFrametime = gpGlobals->frametime; + + //!!HACK HACK: Adrian - slow down all player movement by this factor. + //!!Blame Yahn for this one. + gpGlobals->frametime *= pPlayer->GetLaggedMovementValue(); + + ResetGetPointContentsCache(); + + // Cropping movement speed scales mv->m_fForwardSpeed etc. globally + // Once we crop, we don't want to recursively crop again, so we set the crop + // flag globally here once per usercmd cycle. + m_iSpeedCropped = SPEED_CROPPED_RESET; + + player = pPlayer; + mv = pMove; + mv->m_flMaxSpeed = sv_maxspeed.GetFloat(); + + m_bInPortalEnv = (((CPortal_Player *)pPlayer)->m_hPortalEnvironment != NULL); + + g_bAllowForcePortalTrace = m_bInPortalEnv; + g_bForcePortalTrace = m_bInPortalEnv; + + // Run the command. + PlayerMove(); + + FinishMove(); + + g_bAllowForcePortalTrace = false; + g_bForcePortalTrace = false; + +#ifndef CLIENT_DLL + pPlayer->UnforceButtons( IN_DUCK ); + pPlayer->UnforceButtons( IN_JUMP ); +#endif + + //This is probably not needed, but just in case. + gpGlobals->frametime = flStoreFrametime; +} + +//----------------------------------------------------------------------------- +// Purpose: Base jump behavior, plus an anim event +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPortalGameMovement::CheckJumpButton() +{ + if ( BaseClass::CheckJumpButton() && GetPortalPlayer() ) + { + GetPortalPlayer()->DoAnimationEvent( PLAYERANIMEVENT_JUMP, 0 ); + return true; + } + + return false; +} + +void CPortalGameMovement::FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir ) +{ + // Make sure there's a portal + if ( !pPortal ) + return; + + // Get portal vectors + Vector vPortalForward, vPortalRight, vPortalUp; + pPortal->GetVectors( &vPortalForward, &vPortalRight, &vPortalUp ); + + // Make sure it's a floor portal + if ( vPortalForward.z < 0.8f ) + return; + + vPortalRight.z = 0.0f; + vPortalUp.z = 0.0f; + VectorNormalize( vPortalRight ); + VectorNormalize( vPortalUp ); + + // Make sure the player is looking downward + CPortal_Player *pPlayer = GetPortalPlayer(); + + Vector vPlayerForward; + pPlayer->EyeVectors( &vPlayerForward ); + + if ( vPlayerForward.z > -0.1f ) + return; + + Vector vPlayerOrigin = pPlayer->GetAbsOrigin(); + Vector vPlayerToPortal = pPortal->GetAbsOrigin() - vPlayerOrigin; + + // Make sure the player is trying to air control, they're falling downward and they are vertically close to the portal + if ( fabsf( wishdir[ 0 ] ) > 64.0f || fabsf( wishdir[ 1 ] ) > 64.0f || mv->m_vecVelocity[ 2 ] > -165.0f || vPlayerToPortal.z < -512.0f ) + return; + + // Make sure we're in the 2D portal rectangle + if ( ( vPlayerToPortal.Dot( vPortalRight ) * vPortalRight ).Length() > PORTAL_HALF_WIDTH * 1.5f ) + return; + if ( ( vPlayerToPortal.Dot( vPortalUp ) * vPortalUp ).Length() > PORTAL_HALF_HEIGHT * 1.5f ) + return; + + if ( vPlayerToPortal.z > -8.0f ) + { + // We're too close the the portal to continue correcting, but zero the velocity so our fling velocity is nice + mv->m_vecVelocity[ 0 ] = 0.0f; + mv->m_vecVelocity[ 1 ] = 0.0f; + } + else + { + // Funnel toward the portal + float fFunnelX = vPlayerToPortal.x * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 0 ]; + float fFunnelY = vPlayerToPortal.y * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 1 ]; + + wishdir[ 0 ] += fFunnelX; + wishdir[ 1 ] += fFunnelY; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : wishdir - +// accel - +//----------------------------------------------------------------------------- +void CPortalGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel ) +{ + int i; + float addspeed, accelspeed, currentspeed; + float wishspd; + + wishspd = wishspeed; + + if (player->pl.deadflag) + return; + + if (player->m_flWaterJumpTime) + return; + + // Cap speed + if (wishspd > 60.0f) + wishspd = 60.0f; + + // Determine veer amount + currentspeed = mv->m_vecVelocity.Dot(wishdir); + + // See how much to add + addspeed = wishspd - currentspeed; + + // If not adding any, done. + if (addspeed <= 0) + return; + + // Determine acceleration speed after acceleration + accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction; + + // Cap it + if (accelspeed > addspeed) + accelspeed = addspeed; + + // Adjust pmove vel. + for (i=0 ; i<3 ; i++) + { + mv->m_vecVelocity[i] += accelspeed * wishdir[i]; + mv->m_outWishVel[i] += accelspeed * wishdir[i]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPortalGameMovement::AirMove( void ) +{ + int i; + Vector wishvel; + float fmove, smove; + Vector wishdir; + float wishspeed; + Vector forward, right, up; + + AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles + + // Copy movement amounts + fmove = mv->m_flForwardMove; + smove = mv->m_flSideMove; + + // Zero out z components of movement vectors + forward[2] = 0; + right[2] = 0; + VectorNormalize(forward); // Normalize remainder of vectors + VectorNormalize(right); // + + for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity + wishvel[i] = forward[i]*fmove + right[i]*smove; + wishvel[2] = 0; // Zero out z part of velocity + + VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move + + // + // Don't let the player screw their fling because of adjusting into a floor portal + // + if ( mv->m_vecVelocity[ 0 ] * mv->m_vecVelocity[ 0 ] + mv->m_vecVelocity[ 1 ] * mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * MIN_FLING_SPEED ) + { + if ( mv->m_vecVelocity[ 0 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] < 0.0f ) + wishdir[ 0 ] = 0.0f; + else if ( mv->m_vecVelocity[ 0 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] > 0.0f ) + wishdir[ 0 ] = 0.0f; + + if ( mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] < 0.0f ) + wishdir[ 1 ] = 0.0f; + else if ( mv->m_vecVelocity[ 1 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] > 0.0f ) + wishdir[ 1 ] = 0.0f; + } + + // + // Try to autocorrect the player to fall into the middle of the portal + // + else if ( sv_player_funnel_into_portals.GetBool() ) + { + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount != 0 ) + { + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->IsActivedAndLinked() ) + { + FunnelIntoPortal( pTempPortal, wishdir ); + } + } + } + } + + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed)) + { + VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); + wishspeed = mv->m_flMaxSpeed; + } + + AirAccelerate( wishdir, wishspeed, 15.0f ); + + // Add in any base velocity to the current velocity. + VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); + + TryPlayerMove(); + + // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?) + VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); +} + +void CPortalGameMovement::PlayerRoughLandingEffects( float fvol ) +{ + BaseClass::PlayerRoughLandingEffects( fvol ); + +#ifndef CLIENT_DLL + if ( fvol >= 1.0 ) + { + // Play the future shoes sound + CRecipientFilter filter; + filter.AddRecipientsByPAS( player->GetAbsOrigin() ); + + CSoundParameters params; + if ( CBaseEntity::GetParametersForSound( "PortalPlayer.FallRecover", params, NULL ) ) + { + EmitSound_t ep( params ); + ep.m_nPitch = 125.0f - player->m_Local.m_flFallVelocity * 0.03f; // lower pitch the harder they land + ep.m_flVolume = MIN( player->m_Local.m_flFallVelocity * 0.00075f - 0.38, 1.0f ); // louder the harder they land + + CBaseEntity::EmitSound( filter, player->entindex(), ep ); + } + } +#endif +} + +void TracePlayerBBoxForGround2( const Vector& start, const Vector& end, const Vector& minsSrc, + const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask, + int collisionGroup, trace_t& pm ) +{ + + VPROF( "TracePlayerBBoxForGround" ); + + CPortal_Player *pPortalPlayer = dynamic_cast<CPortal_Player *>(player->GetRefEHandle().Get()); + CProp_Portal *pPlayerPortal = pPortalPlayer->m_hPortalEnvironment; + +#ifndef CLIENT_DLL + if( pPlayerPortal && pPlayerPortal->m_PortalSimulator.IsReadyToSimulate() == false ) + pPlayerPortal = NULL; +#endif + + Ray_t ray; + Vector mins, maxs; + + float fraction = pm.fraction; + Vector endpos = pm.endpos; + + // Check the -x, -y quadrant + mins = minsSrc; + maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z ); + ray.Init( start, end, mins, maxs ); + + if( pPlayerPortal ) + UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm ); + else + UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); + + if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) + { + pm.fraction = fraction; + pm.endpos = endpos; + return; + } + + // Check the +x, +y quadrant + mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z ); + maxs = maxsSrc; + ray.Init( start, end, mins, maxs ); + + if( pPlayerPortal ) + UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm ); + else + UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); + + if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) + { + pm.fraction = fraction; + pm.endpos = endpos; + return; + } + + // Check the -x, +y quadrant + mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z ); + maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z ); + ray.Init( start, end, mins, maxs ); + + if( pPlayerPortal ) + UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm ); + else + UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); + + if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) + { + pm.fraction = fraction; + pm.endpos = endpos; + return; + } + + // Check the +x, -y quadrant + mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z ); + maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z ); + ray.Init( start, end, mins, maxs ); + + if( pPlayerPortal ) + UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm ); + else + UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); + + if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) + { + pm.fraction = fraction; + pm.endpos = endpos; + return; + } + + pm.fraction = fraction; + pm.endpos = endpos; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &input - +//----------------------------------------------------------------------------- +void CPortalGameMovement::CategorizePosition( void ) +{ + Vector point; + trace_t pm; + + // if the player hull point one unit down is solid, the player + // is on ground + + // see if standing on something solid + + // Doing this before we move may introduce a potential latency in water detection, but + // doing it after can get us stuck on the bottom in water if the amount we move up + // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call + // this several times per frame, so we really need to avoid sticking to the bottom of + // water on each call, and the converse case will correct itself if called twice. + CheckWater(); + + // observers don't have a ground entity + if ( player->IsObserver() ) + return; + + point[0] = mv->GetAbsOrigin()[0]; + point[1] = mv->GetAbsOrigin()[1]; + point[2] = mv->GetAbsOrigin()[2] - 2; + + Vector bumpOrigin; + bumpOrigin = mv->GetAbsOrigin(); + + // Shooting up really fast. Definitely not on ground. + // On ladder moving up, so not on ground either + // NOTE: 145 is a jump. + if ( mv->m_vecVelocity[2] > 140 || + ( mv->m_vecVelocity[2] > 0.0f && player->GetMoveType() == MOVETYPE_LADDER ) ) + { + SetGroundEntity( NULL ); + } + else + { + // Try and move down. + TracePlayerBBox( bumpOrigin, point, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); + + // If we hit a steep plane, we are not on ground + if ( pm.plane.normal[2] < 0.7) + { + // Test four sub-boxes, to see if any of them would have found shallower slope we could + // actually stand on + + TracePlayerBBoxForGround2( bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(), mv->m_nPlayerHandle.Get(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); + if ( pm.plane.normal[2] < 0.7) + { + + SetGroundEntity( NULL ); // too steep + // probably want to add a check for a +z velocity too! + if ( ( mv->m_vecVelocity.z > 0.0f ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) ) + { + player->m_surfaceFriction = 0.25f; + } + } + else + { + SetGroundEntity( &pm ); // Otherwise, point to index of ent under us. + } + } + else + { + SetGroundEntity( &pm ); // Otherwise, point to index of ent under us. + } + + // If we are on something... + if (player->GetGroundEntity() != NULL) + { + // Then we are not in water jump sequence + player->m_flWaterJumpTime = 0; + + // If we could make the move, drop us down that 1 pixel + if ( player->GetWaterLevel() < WL_Waist && !pm.startsolid && !pm.allsolid ) + { + // check distance we would like to move -- this is supposed to just keep up + // "on the ground" surface not stap us back to earth (i.e. on move origin to + // end position when the ground is within .5 units away) (2 units) + if( pm.fraction ) + // if( pm.fraction < 0.5) + { + mv->SetAbsOrigin( pm.endpos ); + } + } + } + +#ifndef CLIENT_DLL + + //Adrian: vehicle code handles for us. + if ( player->IsInAVehicle() == false ) + { + // If our gamematerial has changed, tell any player surface triggers that are watching + IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps(); + surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps ); + char cCurrGameMaterial = pSurfaceProp->game.material; + if ( !player->GetGroundEntity() ) + { + cCurrGameMaterial = 0; + } + + // Changed? + if ( player->m_chPreviousTextureType != cCurrGameMaterial ) + { + CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial ); + } + + player->m_chPreviousTextureType = cCurrGameMaterial; + } +#endif + } +} + +void CPortalGameMovement::Duck( void ) +{ + return BaseClass::Duck(); +} + +int CPortalGameMovement::CheckStuck( void ) +{ + if( BaseClass::CheckStuck() ) + { + CPortal_Player *pPortalPlayer = GetPortalPlayer(); + +#ifndef CLIENT_DLL + if( pPortalPlayer->IsAlive() ) + g_PortalGameStats.Event_PlayerStuck( pPortalPlayer ); +#endif + + //try to fix it, then recheck + Vector vIndecisive; + if( pPortalPlayer->m_hPortalEnvironment ) + { + pPortalPlayer->m_hPortalEnvironment->GetVectors( &vIndecisive, NULL, NULL ); + } + else + { + vIndecisive.Init( 0.0f, 0.0f, 1.0f ); + } + Vector ptOldOrigin = pPortalPlayer->GetAbsOrigin(); + + if( pPortalPlayer->m_hPortalEnvironment ) + { + if( !FindClosestPassableSpace( pPortalPlayer, vIndecisive ) ) + { +#ifndef CLIENT_DLL + DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" ); + + CTakeDamageInfo info( pPortalPlayer, pPortalPlayer, vec3_origin, vec3_origin, 1e10, DMG_CRUSH ); + pPortalPlayer->OnTakeDamage( info ); +#endif + } + + //make sure we didn't get put behind the portal >_< + Vector ptCurrentOrigin = pPortalPlayer->GetAbsOrigin(); + if( vIndecisive.Dot( ptCurrentOrigin - ptOldOrigin ) < 0.0f ) + { + pPortalPlayer->SetAbsOrigin( ptOldOrigin + (vIndecisive * 5.0f) ); //this is an anti-bug hack, since this would have probably popped them out of the world, we're just going to move them forward a few units + } + } + + mv->SetAbsOrigin( pPortalPlayer->GetAbsOrigin() ); + return BaseClass::CheckStuck(); + } + else + { + return 0; + } +} + +void CPortalGameMovement::SetGroundEntity( trace_t *pm ) +{ +#ifndef CLIENT_DLL + if ( !player->GetGroundEntity() && pm && pm->m_pEnt ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_touchedground" ); + if ( event ) + { + event->SetInt( "userid", player->GetUserID() ); + gameeventmanager->FireEvent( event ); + } + } +#endif + + BaseClass::SetGroundEntity( pm ); +} + +void CPortalGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm ) +{ + VPROF( "CGameMovement::TracePlayerBBox" ); + + CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get()); + + Ray_t ray; + ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() ); + +#ifdef CLIENT_DLL + CTraceFilterSimple traceFilter( mv->m_nPlayerHandle.Get(), collisionGroup ); +#else + CTraceFilterSimple baseFilter( mv->m_nPlayerHandle.Get(), collisionGroup ); + CTraceFilterTranslateClones traceFilter( &baseFilter ); +#endif + + UTIL_Portal_TraceRay_With( pPortalPlayer->m_hPortalEnvironment, ray, fMask, &traceFilter, &pm ); + + // If we're moving through a portal and failed to hit anything with the above ray trace + // Use UTIL_Portal_TraceEntity to test this movement through a portal and override the trace with the result + if ( pm.fraction == 1.0f && UTIL_DidTraceTouchPortals( ray, pm ) && sv_player_trace_through_portals.GetBool() ) + { + trace_t tempTrace; + UTIL_Portal_TraceEntity( pPortalPlayer, start, end, fMask, &traceFilter, &tempTrace ); + + if ( tempTrace.DidHit() && tempTrace.fraction < pm.fraction && !tempTrace.startsolid && !tempTrace.allsolid ) + { + pm = tempTrace; + } + } +} + +CBaseHandle CPortalGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm ) +{ + TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); //hook into the existing portal special trace functionality + + //Ray_t ray; + //ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() ); + //UTIL_TraceRay( ray, MASK_PLAYERSOLID, mv->m_nPlayerHandle.Get(), collisionGroup, &pm ); + if( pm.startsolid && pm.m_pEnt && (pm.contents & MASK_PLAYERSOLID) ) + { +#ifdef _DEBUG + AssertMsgOnce( false, "The player got stuck on something. Break to investigate." ); //happens enough to just leave in a perma-debugger + //this next trace is PURELY for tracking down how the player got stuck. Nothing new is discovered over the same trace about 10 lines up + TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); +#endif + return pm.m_pEnt->GetRefEHandle(); + } +#ifndef CLIENT_DLL + else if ( pm.startsolid && pm.m_pEnt && CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pm.m_pEnt ) ) + { + // Stuck in a portal environment object, so unstick them! + CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get()); + pPortalPlayer->SetStuckOnPortalCollisionObject(); + + return INVALID_EHANDLE_INDEX; + } +#endif + else + { + return INVALID_EHANDLE_INDEX; + } +} + + +// Expose our interface. +static CPortalGameMovement g_GameMovement; +IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement ); + diff --git a/game/shared/portal/portal_gamerules.cpp b/game/shared/portal/portal_gamerules.cpp new file mode 100644 index 0000000..3143d2b --- /dev/null +++ b/game/shared/portal/portal_gamerules.cpp @@ -0,0 +1,1284 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The Half-Life 2 game rules, such as the relationship tables and ammo +// damage cvars. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "portal_gamerules.h" +#include "ammodef.h" +#include "hl2_shareddefs.h" +#include "portal_shareddefs.h" + +#ifdef CLIENT_DLL +#else + #include "player.h" + #include "game.h" + #include "gamerules.h" + #include "teamplay_gamerules.h" + #include "portal_player.h" + #include "globalstate.h" + #include "ai_basenpc.h" + #include "portal/weapon_physcannon.h" + #include "props.h" // For props flags used in making the portal weight box + #include "datacache/imdlcache.h" // For precaching box model + + #include "achievementmgr.h" + extern CAchievementMgr g_AchievementMgrPortal; +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + + + +REGISTER_GAMERULES_CLASS( CPortalGameRules ); + +BEGIN_NETWORK_TABLE_NOBASE( CPortalGameRules, DT_PortalGameRules ) + #ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bMegaPhysgun ) ), + #else + SendPropBool( SENDINFO( m_bMegaPhysgun ) ), + #endif +END_NETWORK_TABLE() + + +LINK_ENTITY_TO_CLASS( portal_gamerules, CPortalGameRulesProxy ); +IMPLEMENT_NETWORKCLASS_ALIASED( PortalGameRulesProxy, DT_PortalGameRulesProxy ) + + +#ifdef CLIENT_DLL + void RecvProxy_PortalGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) + { + CPortalGameRules *pRules = PortalGameRules(); + Assert( pRules ); + *pOut = pRules; + } + + BEGIN_RECV_TABLE( CPortalGameRulesProxy, DT_PortalGameRulesProxy ) + RecvPropDataTable( "portal_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_PortalGameRules ), RecvProxy_PortalGameRules ) + END_RECV_TABLE() +#else + void* SendProxy_PortalGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) + { + CPortalGameRules *pRules = PortalGameRules(); + Assert( pRules ); + pRecipients->SetAllRecipients(); + return pRules; + } + + BEGIN_SEND_TABLE( CPortalGameRulesProxy, DT_PortalGameRulesProxy ) + SendPropDataTable( "portal_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_PortalGameRules ), SendProxy_PortalGameRules ) + END_SEND_TABLE() +#endif + + +extern ConVar sv_robust_explosions; +extern ConVar sk_allow_autoaim; +extern ConVar sk_autoaim_scale1; +extern ConVar sk_autoaim_scale2; + +#if !defined ( CLIENT_DLL ) +extern ConVar sv_alternateticks; +#endif // !CLIENT_DLL + +#define PORTAL_WEIGHT_BOX_MODEL_NAME "models/props/metal_box.mdl" + +// Portal-only con commands + +#ifndef CLIENT_DLL +// Create the box used for portal puzzles, named 'box'. Used for easy debugging of portal puzzles. +void CC_Create_PortalWeightBox( void ) +{ + MDLCACHE_CRITICAL_SECTION(); + + bool allowPrecache = CBaseEntity::IsPrecacheAllowed(); + CBaseEntity::SetAllowPrecache( true ); + + // Try to create entity + CBaseEntity *entity = dynamic_cast< CBaseEntity * >( CreateEntityByName("prop_physics") ); + if (entity) + { + entity->PrecacheModel( PORTAL_WEIGHT_BOX_MODEL_NAME ); + entity->SetModel( PORTAL_WEIGHT_BOX_MODEL_NAME ); + entity->SetName( MAKE_STRING("box") ); + entity->AddSpawnFlags( SF_PHYSPROP_ENABLE_PICKUP_OUTPUT ); + entity->Precache(); + DispatchSpawn(entity); + + // Now attempt to drop into the world + CBasePlayer* pPlayer = UTIL_GetCommandClient(); + trace_t tr; + Vector forward; + pPlayer->EyeVectors( &forward ); + UTIL_TraceLine(pPlayer->EyePosition(), + pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID, + pPlayer, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 ) + { + tr.endpos.z += 12; + entity->Teleport( &tr.endpos, NULL, NULL ); + UTIL_DropToFloor( entity, MASK_SOLID ); + } + } + CBaseEntity::SetAllowPrecache( allowPrecache ); +} +static ConCommand ent_create_portal_weight_box("ent_create_portal_weight_box", CC_Create_PortalWeightBox, "Creates a weight box used in portal puzzles at the location the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT); +#endif // CLIENT_DLL + + +#define PORTAL_METAL_SPHERE_MODEL_NAME "models/props/sphere.mdl" + +#ifndef CLIENT_DLL +// Create a very reflective bouncy metal sphere +void CC_Create_PortalMetalSphere( void ) +{ + MDLCACHE_CRITICAL_SECTION(); + + bool allowPrecache = CBaseEntity::IsPrecacheAllowed(); + CBaseEntity::SetAllowPrecache( true ); + + // Try to create entity + CBaseEntity *entity = dynamic_cast< CBaseEntity * >( CreateEntityByName("prop_physics") ); + if (entity) + { + entity->PrecacheModel( PORTAL_METAL_SPHERE_MODEL_NAME ); + entity->SetModel( PORTAL_METAL_SPHERE_MODEL_NAME ); + entity->SetName( MAKE_STRING("sphere") ); + entity->AddSpawnFlags( SF_PHYSPROP_ENABLE_PICKUP_OUTPUT ); + entity->Precache(); + DispatchSpawn(entity); + + // Now attempt to drop into the world + CBasePlayer* pPlayer = UTIL_GetCommandClient(); + trace_t tr; + Vector forward; + pPlayer->EyeVectors( &forward ); + UTIL_TraceLine(pPlayer->EyePosition(), + pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID, + pPlayer, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 ) + { + tr.endpos.z += 12; + entity->Teleport( &tr.endpos, NULL, NULL ); + UTIL_DropToFloor( entity, MASK_SOLID ); + } + } + CBaseEntity::SetAllowPrecache( allowPrecache ); +} +static ConCommand ent_create_portal_metal_sphere("ent_create_portal_metal_sphere", CC_Create_PortalMetalSphere, "Creates a reflective metal sphere where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT); +#endif // CLIENT_DLL + + + +#ifdef CLIENT_DLL //{ + + +#else //}{ + + extern bool g_fGameOver; + + //----------------------------------------------------------------------------- + // Purpose: + // Input : + // Output : + //----------------------------------------------------------------------------- + CPortalGameRules::CPortalGameRules() + { + m_bMegaPhysgun = false; + g_pCVar->FindVar( "sv_maxreplay" )->SetValue( "1.5" ); + } + + + //----------------------------------------------------------------------------- + // Purpose: called each time a player uses a "cmd" command + // Input : *pEdict - the player who issued the command + // Use engine.Cmd_Argv, engine.Cmd_Argv, and engine.Cmd_Argc to get + // pointers the character string command. + //----------------------------------------------------------------------------- + bool CPortalGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args ) + { + if( BaseClass::ClientCommand( pEdict, args ) ) + return true; + + CPortal_Player *pPlayer = (CPortal_Player *) pEdict; + + if ( pPlayer->ClientCommand( args ) ) + return true; + + return false; + } + + //----------------------------------------------------------------------------- + // Purpose: Player has just spawned. Equip them. + //----------------------------------------------------------------------------- + void CPortalGameRules::PlayerSpawn( CBasePlayer *pPlayer ) + { + } + + //------------------------------------------------------------------------------ + // Purpose : Initialize all default class relationships + // Input : + // Output : + //------------------------------------------------------------------------------ + void CPortalGameRules::InitDefaultAIRelationships( void ) + { + int i, j; + + // Allocate memory for default relationships + CBaseCombatCharacter::AllocateDefaultRelationships(); + + // -------------------------------------------------------------- + // First initialize table so we can report missing relationships + // -------------------------------------------------------------- + for (i=0;i<NUM_AI_CLASSES;i++) + { + for (j=0;j<NUM_AI_CLASSES;j++) + { + // By default all relationships are neutral of priority zero + CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 ); + } + } + + // ------------------------------------------------------------ + // > CLASS_ANTLION + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PROTOSNIPER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_ANTLION, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_BARNACLE + // + // In this case, the relationship D_HT indicates which characters + // the barnacle will try to eat. + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_BARNACLE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MANHACK, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_EARTH_FAUNA, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_BULLSEYE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_ANTLION, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_BULLSQUID + // ------------------------------------------------------------ + /* + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_BULLSEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_HEADCRAB, D_HT, 1); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_HOUNDEYE, D_HT, 1); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_MANHACK, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSQUID, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + */ + // ------------------------------------------------------------ + // > CLASS_CITIZEN_PASSIVE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_BULLSQUID, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_HEADCRAB, D_FR, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_HOUNDEYE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MANHACK, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MISSILE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_VORTIGAUNT, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_ZOMBIE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_CITIZEN_REBEL + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_BULLSQUID, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MISSILE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_VORTIGAUNT, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_COMBINE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_COMBINE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_COMBINE_GUNSHIP, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_COMBINE_GUNSHIP + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_COMBINE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_COMBINE_GUNSHIP, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_MISSILE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_CONSCRIPT + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_FLARE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_ANTLION, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_HEADCRAB + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_BULLSQUID, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_HOUNDEYE + // ------------------------------------------------------------ + /* + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_BULLSQUID, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_HEADCRAB, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_HOUNDEYE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + */ + + // ------------------------------------------------------------ + // > CLASS_MANHACK + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_HEADCRAB, D_HT,-1); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_HOUNDEYE, D_HT,-1); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_METROPOLICE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_MILITARY + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_MISSILE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_NONE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_ANTLION, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + + // ------------------------------------------------------------ + // > CLASS_PLAYER + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_BARNACLE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_BULLSEYE, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CITIZEN_PASSIVE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CITIZEN_REBEL, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_COMBINE_GUNSHIP, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_VORTIGAUNT, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PROTOSNIPER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PLAYER_ALLY, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); + + // ------------------------------------------------------------ + // > CLASS_PLAYER_ALLY + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_PLAYER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_BARNACLE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_HEADCRAB, D_FR, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_VORTIGAUNT, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_ZOMBIE, D_FR, 1); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_PROTOSNIPER, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_PLAYER_ALLY, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); + + // ------------------------------------------------------------ + // > CLASS_PLAYER_ALLY_VITAL + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_PLAYER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_BARNACLE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_VORTIGAUNT, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_PROTOSNIPER, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_PLAYER_ALLY, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); + + // ------------------------------------------------------------ + // > CLASS_SCANNER + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_COMBINE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_COMBINE_GUNSHIP, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MANHACK, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_METROPOLICE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MILITARY, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_SCANNER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_STALKER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PROTOSNIPER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_STALKER + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_VORTIGAUNT + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PLAYER, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_BARNACLE, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CITIZEN_PASSIVE, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CITIZEN_REBEL, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PLAYER_ALLY, D_LI, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); + + // ------------------------------------------------------------ + // > CLASS_ZOMBIE + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MANHACK, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MILITARY, D_FR, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_PROTOSNIPER + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MISSILE, D_NU, 5); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + + // ------------------------------------------------------------ + // > CLASS_EARTH_FAUNA + // + // Hates pretty much everything equally except other earth fauna. + // This will make the critter choose the nearest thing as its enemy. + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_NONE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_ANTLION, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_BULLSQUID, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CITIZEN_PASSIVE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CITIZEN_REBEL, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_COMBINE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_COMBINE_GUNSHIP, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CONSCRIPT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_FLARE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_HEADCRAB, D_HT, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_HOUNDEYE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MANHACK, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_METROPOLICE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MILITARY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MISSILE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_SCANNER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_STALKER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_VORTIGAUNT, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_ZOMBIE, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PROTOSNIPER, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER_ALLY, D_HT, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); + } + + + //------------------------------------------------------------------------------ + // Purpose : Return classify text for classify type + // Input : + // Output : + //------------------------------------------------------------------------------ + const char* CPortalGameRules::AIClassText(int classType) + { + switch (classType) + { + case CLASS_NONE: return "CLASS_NONE"; + case CLASS_PLAYER: return "CLASS_PLAYER"; + case CLASS_ANTLION: return "CLASS_ANTLION"; + case CLASS_BARNACLE: return "CLASS_BARNACLE"; + case CLASS_BULLSEYE: return "CLASS_BULLSEYE"; + //case CLASS_BULLSQUID: return "CLASS_BULLSQUID"; + case CLASS_CITIZEN_PASSIVE: return "CLASS_CITIZEN_PASSIVE"; + case CLASS_CITIZEN_REBEL: return "CLASS_CITIZEN_REBEL"; + case CLASS_COMBINE: return "CLASS_COMBINE"; + case CLASS_CONSCRIPT: return "CLASS_CONSCRIPT"; + case CLASS_HEADCRAB: return "CLASS_HEADCRAB"; + //case CLASS_HOUNDEYE: return "CLASS_HOUNDEYE"; + case CLASS_MANHACK: return "CLASS_MANHACK"; + case CLASS_METROPOLICE: return "CLASS_METROPOLICE"; + case CLASS_MILITARY: return "CLASS_MILITARY"; + case CLASS_SCANNER: return "CLASS_SCANNER"; + case CLASS_STALKER: return "CLASS_STALKER"; + case CLASS_VORTIGAUNT: return "CLASS_VORTIGAUNT"; + case CLASS_ZOMBIE: return "CLASS_ZOMBIE"; + case CLASS_PROTOSNIPER: return "CLASS_PROTOSNIPER"; + case CLASS_MISSILE: return "CLASS_MISSILE"; + case CLASS_FLARE: return "CLASS_FLARE"; + case CLASS_EARTH_FAUNA: return "CLASS_EARTH_FAUNA"; + + default: return "MISSING CLASS in ClassifyText()"; + } + } + + void CPortalGameRules::PlayerThink( CBasePlayer *pPlayer ) + { + } + + void CPortalGameRules::Think( void ) + { + BaseClass::Think(); + } + + //----------------------------------------------------------------------------- + // Purpose: Returns how much damage the given ammo type should do to the victim + // when fired by the attacker. + // Input : pAttacker - Dude what shot the gun. + // pVictim - Dude what done got shot. + // nAmmoType - What been shot out. + // Output : How much hurt to put on dude what done got shot (pVictim). + //----------------------------------------------------------------------------- + float CPortalGameRules::GetAmmoDamage( CBaseEntity *pAttacker, CBaseEntity *pVictim, int nAmmoType ) + { + return BaseClass::GetAmmoDamage( pAttacker, pVictim, nAmmoType ); + } + + float CPortalGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer ) + { + // No fall damage in Portal! + return 0.0f; + } + + +#endif //} !CLIENT_DLL + + +//----------------------------------------------------------------------------- +// Purpose: On starting a game, make global state changes specific to portal +//----------------------------------------------------------------------------- +bool CPortalGameRules::Init() +{ +#if !defined ( CLIENT_DLL ) + // Portal never wants alternate ticks. Some low end hardware sets it in dxsupport.cfg so this will catch those cases. + sv_alternateticks.SetValue( 0 ); +#endif // !CLIENT_DLL + + return BaseClass::Init(); +} + +// ------------------------------------------------------------------------------------ // +// Shared CPortalGameRules implementation. +// ------------------------------------------------------------------------------------ // +bool CPortalGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) +{ + // If it's a portal, we want to collide with it! + /*if ( collisionGroup0 == PORTALCOLLISION_GROUP_PORTAL && collisionGroup1 != PORTALCOLLISION_GROUP_PORTAL || + collisionGroup1 == PORTALCOLLISION_GROUP_PORTAL && collisionGroup0 != PORTALCOLLISION_GROUP_PORTAL ) + { + return true; + }*/ + + return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CPortalGameRules::ShouldUseRobustRadiusDamage(CBaseEntity *pEntity) +{ +#ifdef CLIENT_DLL + return false; +#endif + + if( !sv_robust_explosions.GetBool() ) + return false; + + if( !pEntity->IsNPC() ) + { + // Only NPC's + return false; + } + +#ifndef CLIENT_DLL + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if( pNPC->CapabilitiesGet() & bits_CAP_SIMPLE_RADIUS_DAMAGE ) + { + // This NPC only eligible for simple radius damage. + return false; + } +#endif//CLIENT_DLL + + return true; +} + +#ifndef CLIENT_DLL +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CPortalGameRules::ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target ) +{ + return sk_allow_autoaim.GetBool() != 0; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +float CPortalGameRules::GetAutoAimScale( CBasePlayer *pPlayer ) +{ + switch( GetSkillLevel() ) + { + case SKILL_EASY: + return sk_autoaim_scale1.GetFloat(); + + case SKILL_MEDIUM: + return sk_autoaim_scale2.GetFloat(); + + default: + return 0.0f; + } +} + +//----------------------------------------------------------------------------- +// This takes the long way around to see if a prop should emit a DLIGHT when it +// ignites, to avoid having Alyx-related code in props.cpp. +//----------------------------------------------------------------------------- +bool CPortalGameRules::ShouldBurningPropsEmitLight() +{ + return false; +} + +//--------------------------------------------------------- +// This is the only way we can silence the radio sound from the first room without touching them map -- jdw +//--------------------------------------------------------- +bool CPortalGameRules::ShouldRemoveRadio( void ) +{ + IAchievement *pHeartbreaker = g_AchievementMgrPortal.GetAchievementByName( "PORTAL_BEAT_GAME" ); + if ( pHeartbreaker && pHeartbreaker->IsAchieved() ) + return true; + + return false; +} + + +#endif//CLIENT_DLL + +#ifdef CLIENT_DLL + +bool CPortalGameRules::IsBonusChallengeTimeBased( void ) +{ + CBasePlayer* pPlayer = UTIL_PlayerByIndex( 1 ); + if ( !pPlayer ) + return true; + + int iBonusChallenge = pPlayer->GetBonusChallenge(); + if ( iBonusChallenge == PORTAL_CHALLENGE_TIME || iBonusChallenge == PORTAL_CHALLENGE_NONE ) + return true; + + return false; +} + +#endif + +// ------------------------------------------------------------------------------------ // +// Global functions. +// ------------------------------------------------------------------------------------ // + +// shared ammo definition +// JAY: Trying to make a more physical bullet response +#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) +#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) + +// exaggerate all of the forces, but use real numbers to keep them consistent +#define BULLET_IMPULSE_EXAGGERATION 3.5 +// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s +#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION) + + +CAmmoDef *GetAmmoDef() +{ + static CAmmoDef def; + static bool bInitted = false; + + if ( !bInitted ) + { + bInitted = true; + + def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_ar2", "sk_npc_dmg_ar2", "sk_max_ar2", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("AlyxGun", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_alyxgun", "sk_npc_dmg_alyxgun", "sk_max_alyxgun", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_pistol", "sk_npc_dmg_pistol", "sk_max_pistol", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_smg1", "sk_npc_dmg_smg1", "sk_max_smg1", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_357", "sk_npc_dmg_357", "sk_max_357", BULLET_IMPULSE(800, 5000), 0 ); + def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_crossbow", "sk_npc_dmg_crossbow", "sk_max_crossbow", BULLET_IMPULSE(800, 8000), 0 ); + + def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, "sk_plr_dmg_buckshot", "sk_npc_dmg_buckshot", "sk_max_buckshot", BULLET_IMPULSE(400, 1200), 0 ); + def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, "sk_plr_dmg_rpg_round", "sk_npc_dmg_rpg_round", "sk_max_rpg_round", 0, 0 ); + def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_smg1_grenade", "sk_npc_dmg_smg1_grenade", "sk_max_smg1_grenade", 0, 0 ); + def.AddAmmoType("SniperRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_plr_dmg_sniper_round", "sk_npc_dmg_sniper_round", "sk_max_sniper_round", BULLET_IMPULSE(650, 6000), 0 ); + def.AddAmmoType("SniperPenetratedRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_dmg_sniper_penetrate_plr", "sk_dmg_sniper_penetrate_npc", "sk_max_sniper_round", BULLET_IMPULSE(150, 6000), 0 ); + def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_grenade", 0, 0); + def.AddAmmoType("Thumper", DMG_SONIC, TRACER_NONE, 10, 10, 2, 0, 0 ); + def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 ); + def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, NULL, NULL, NULL, 0, 0 ); + def.AddAmmoType("GaussEnergy", DMG_SHOCK, TRACER_NONE, "sk_jeep_gauss_damage", "sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s + def.AddAmmoType("CombineCannon", DMG_BULLET, TRACER_LINE, "sk_npc_dmg_gunship_to_plr", "sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s + def.AddAmmoType("AirboatGun", DMG_AIRBOAT, TRACER_LINE, "sk_plr_dmg_airboat", "sk_npc_dmg_airboat", NULL, BULLET_IMPULSE(10, 600), 0 ); + def.AddAmmoType("StriderMinigun", DMG_BULLET, TRACER_LINE, 5, 5, 15, 1.0 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 1.0kg weight at 750 ft/s + def.AddAmmoType("HelicopterGun", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_npc_dmg_helicopter_to_plr", "sk_npc_dmg_helicopter", "sk_max_smg1", BULLET_IMPULSE(400, 1225), AMMO_FORCE_DROP_IF_CARRIED | AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER ); + def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, "sk_max_ar2_altfire", 0, 0 ); +#ifdef HL2_EPISODIC + def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); + def.AddAmmoType("CombineHeavyCannon", DMG_BULLET, TRACER_LINE, 40, 40, NULL, 1.5 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 100 kg weight at 750 ft/s + def.AddAmmoType("ammo_proto1", DMG_BULLET, TRACER_LINE, 0, 0, 10, 0, 0 ); +#endif // HL2_EPISODIC + } + + return &def; +} + diff --git a/game/shared/portal/portal_gamerules.h b/game/shared/portal/portal_gamerules.h new file mode 100644 index 0000000..4dd073d --- /dev/null +++ b/game/shared/portal/portal_gamerules.h @@ -0,0 +1,116 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game rules for Portal. +// +//=============================================================================// + +#ifdef PORTAL_MP + + + +#include "portal_mp_gamerules.h" //redirect to multiplayer gamerules in multiplayer builds + + + +#else + +#ifndef PORTAL_GAMERULES_H +#define PORTAL_GAMERULES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "gamerules.h" +#include "hl2_gamerules.h" + +#ifdef CLIENT_DLL + #define CPortalGameRules C_PortalGameRules + #define CPortalGameRulesProxy C_PortalGameRulesProxy +#endif + +#if defined ( CLIENT_DLL ) +#include "steam/steam_api.h" +#endif + + +class CPortalGameRulesProxy : public CGameRulesProxy +{ +public: + DECLARE_CLASS( CPortalGameRulesProxy, CGameRulesProxy ); + DECLARE_NETWORKCLASS(); +}; + + +class CPortalGameRules : public CHalfLife2 +{ +public: + DECLARE_CLASS( CPortalGameRules, CSingleplayRules ); + + virtual bool Init(); + + virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 ); + virtual bool ShouldUseRobustRadiusDamage(CBaseEntity *pEntity); +#ifndef CLIENT_DLL + virtual bool ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target ); + virtual float GetAutoAimScale( CBasePlayer *pPlayer ); +#endif + +#ifdef CLIENT_DLL + virtual bool IsBonusChallengeTimeBased( void ); +#endif + +private: + // Rules change for the mega physgun + CNetworkVar( bool, m_bMegaPhysgun ); + +#ifdef CLIENT_DLL + + DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. + +#else + + DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. + + CPortalGameRules(); + virtual ~CPortalGameRules() {} + + virtual void Think( void ); + + virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args ); + virtual void PlayerSpawn( CBasePlayer *pPlayer ); + + virtual void InitDefaultAIRelationships( void ); + virtual const char* AIClassText(int classType); + virtual const char *GetGameDescription( void ) { return "Portal"; } + + // Ammo + virtual void PlayerThink( CBasePlayer *pPlayer ); + virtual float GetAmmoDamage( CBaseEntity *pAttacker, CBaseEntity *pVictim, int nAmmoType ); + + virtual bool ShouldBurningPropsEmitLight(); + + bool ShouldRemoveRadio( void ); + +public: + + virtual float FlPlayerFallDamage( CBasePlayer *pPlayer ); + + bool MegaPhyscannonActive( void ) { return m_bMegaPhysgun; } + +private: + + int DefaultFOV( void ) { return 75; } +#endif +}; + + +//----------------------------------------------------------------------------- +// Gets us at the Half-Life 2 game rules +//----------------------------------------------------------------------------- +inline CPortalGameRules* PortalGameRules() +{ + return static_cast<CPortalGameRules*>(g_pGameRules); +} + +#endif // PORTAL_GAMERULES_H +#endif diff --git a/game/shared/portal/portal_gcmessages.cpp b/game/shared/portal/portal_gcmessages.cpp new file mode 100644 index 0000000..f45b65b --- /dev/null +++ b/game/shared/portal/portal_gcmessages.cpp @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Provides names for GC message types for Portal +// +//============================================================================= + +#include "cbase.h" + +#ifdef USE_GC_IN_PORTAL1 +#include "gcsdk/gcsdk.h" +#include "portal_gcmessages.h" + +//----------------------------------------------------------------------------- +// Purpose: A big array of message types for keeping track of their names +//----------------------------------------------------------------------------- +GCSDK::MsgInfo_t g_MsgInfo[] = +{ + DECLARE_GC_MSG( k_EMsgGCReportWarKill ), + + DECLARE_GC_MSG( k_EMsgGCDev_GrantWarKill ), +}; + +void InitGCPortalMessageTypes() +{ + static GCSDK::CMessageListRegistration m_reg( g_MsgInfo, Q_ARRAYSIZE(g_MsgInfo) ); +} + +#endif //#ifdef USE_GC_IN_PORTAL1
\ No newline at end of file diff --git a/game/shared/portal/portal_gcmessages.h b/game/shared/portal/portal_gcmessages.h new file mode 100644 index 0000000..2661217 --- /dev/null +++ b/game/shared/portal/portal_gcmessages.h @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This file defines all of our over-the-wire net protocols for the +// Game Coordinator for Portal. Note that we never use types +// with undefined length (like int). Always use an explicit type +// (like int32). +// +//============================================================================= + +#ifndef PORTAL_GCMESSAGES_H +#define PORTAL_GCMESSAGES_H +#ifdef _WIN32 +#pragma once +#endif + + +enum EGCMsg +{ + k_EMsgGCPortalBase = 5000, + k_EMsgGCReportWarKill = k_EMsgGCPortalBase + 1, //War kill tracking. No longer in use + + // Development only messages + k_EMsgGCPortalDEVBase = 6000, + k_EMsgGCDev_GrantWarKill = k_EMsgGCPortalDEVBase + 1, //War kill tracking. No longer in use +}; + + +#endif diff --git a/game/shared/portal/portal_gcschema.cpp b/game/shared/portal/portal_gcschema.cpp new file mode 100644 index 0000000..ece8e6c --- /dev/null +++ b/game/shared/portal/portal_gcschema.cpp @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// ------------------------------------------------------- +// DO NOT EDIT +// This file was generated from portal\portal_gcschema.sch by SchemaCompiler.EXE +// on Mon Feb 22 13:22:55 2010 +// ------------------------------------------------------- + +#include "cbase.h" +#include "portal_gcschema.h" + +CSchGameAccount::CSchGameAccount() +{ + memset( PubRecordFixed(), 0, CubRecordFixed() ); +} +int CSchGameAccount::GetITable() const { return k_iTable; } +CSchGameAccount::CSchGameAccount( const CSchGameAccount &that ) { *this = that; } +void CSchGameAccount::operator=( const CSchGameAccount &that ) { CRecordBase::operator =( that ); } + + +CSchGameAccountClient::CSchGameAccountClient() +{ + memset( PubRecordFixed(), 0, CubRecordFixed() ); +} +int CSchGameAccountClient::GetITable() const { return k_iTable; } +CSchGameAccountClient::CSchGameAccountClient( const CSchGameAccountClient &that ) { *this = that; } +void CSchGameAccountClient::operator=( const CSchGameAccountClient &that ) { CRecordBase::operator =( that ); } + + +// statics for index IDs + +int CSchGameAccount::m_nPrimaryKeyID; +int CSchGameAccountClient::m_nPrimaryKeyID; + +// other initializers + + +// run-time initializer + +namespace PORTAL_GCSCHEMA +{ +void GenerateIntrinsicSQLSchema( GCSDK::CSchemaFull &schemaFull ) +{ + GCSDK::CSchema *pSchema; + pSchema = schemaFull.AddNewSchema(); + schemaFull.SetITable( pSchema, CSchGameAccount::k_iTable ); // 0 + pSchema->SetESchemaCatalog( GCSDK::k_ESchemaCatalogMain ); + pSchema->SetName( "GameAccount" ); + pSchema->EnsureFieldCount( CSchGameAccount::k_iFieldMax ); + pSchema->SetReportingInterval( 0 ); + pSchema->AddField( "unAccountID", "AccountID", k_EGCSQLType_int32, sizeof( uint32 ), 0, true, 0 ); + pSchema->AddField( "unRewardPoints", "RewardPoints", k_EGCSQLType_int32, sizeof( uint32 ), 0, true, 0 ); + pSchema->AddField( "unPointCap", "PointCap", k_EGCSQLType_int32, sizeof( uint32 ), 0, true, 0 ); + pSchema->AddField( "unLastCapRollover", "LastCapRollover", k_EGCSQLType_int32, sizeof( RTime32 ), 0, true, 0 ); + CSchGameAccount::m_nPrimaryKeyID = pSchema->PrimaryKey( true, 100, "unAccountID" ); + pSchema->SetTestWipePolicy( GCSDK::k_EWipePolicyWipeForAllTests ); + pSchema->SetBAllowWipeTableInProd( false ); + pSchema->CalcOffsets(); + schemaFull.CheckSchema( pSchema, CSchGameAccount::k_iFieldMax, sizeof( CSchGameAccount ) - sizeof( GCSDK::CRecordBase ) ); + pSchema->PrepareForUse(); + + pSchema = schemaFull.AddNewSchema(); + schemaFull.SetITable( pSchema, CSchGameAccountClient::k_iTable ); // 1 + pSchema->SetESchemaCatalog( GCSDK::k_ESchemaCatalogMain ); + pSchema->SetName( "GameAccountClient" ); + pSchema->EnsureFieldCount( CSchGameAccountClient::k_iFieldMax ); + pSchema->SetReportingInterval( 0 ); + pSchema->AddField( "unAccountID", "AccountID", k_EGCSQLType_int32, sizeof( uint32 ), 0, true, 0 ); + CSchGameAccountClient::m_nPrimaryKeyID = pSchema->PrimaryKey( true, 80, "unAccountID" ); + pSchema->SetTestWipePolicy( GCSDK::k_EWipePolicyWipeForAllTests ); + pSchema->SetBAllowWipeTableInProd( false ); + pSchema->CalcOffsets(); + schemaFull.CheckSchema( pSchema, CSchGameAccountClient::k_iFieldMax, sizeof( CSchGameAccountClient ) - sizeof( GCSDK::CRecordBase ) ); + pSchema->PrepareForUse(); + + + schemaFull.FinishInit(); +} +} // namespace
\ No newline at end of file diff --git a/game/shared/portal/portal_gcschema.h b/game/shared/portal/portal_gcschema.h new file mode 100644 index 0000000..57cfa0b --- /dev/null +++ b/game/shared/portal/portal_gcschema.h @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// ------------------------------------------------------- +// DO NOT EDIT +// This file was generated from portal\portal_gcschema.sch by SchemaCompiler.EXE +// on Mon Feb 22 13:22:55 2010 +// ------------------------------------------------------- +#ifndef PORTAL_GCSCHEMA_H +#define PORTAL_GCSCHEMA_H +#ifdef _WIN32 +#pragma once +#endif + +#include "gcsdk/gcschema.h" +#pragma pack(push, 1) + +//----------------------------------------------------------------------------- +// GameAccount +// +//----------------------------------------------------------------------------- + +class CSchGameAccount : public GCSDK::CRecordBase +{ +public: + const static int k_iTable = 0; + CSchGameAccount(); + int GetITable() const; + CSchGameAccount( const CSchGameAccount &that ); + void operator=( const CSchGameAccount &that ); + + uint32 m_unAccountID; // Account ID of the user + uint32 m_unRewardPoints; // number of timed reward points (coplayed minutes) for this user + uint32 m_unPointCap; // Current maximum number of points + RTime32 m_unLastCapRollover; // Last time the player's cap was adjusted + + static int m_nPrimaryKeyID; + + const static int k_iField_unAccountID = 0; + const static int k_iField_unRewardPoints = 1; + const static int k_iField_unPointCap = 2; + const static int k_iField_unLastCapRollover = 3; + const static int k_iFieldMax = 4; +}; + +//----------------------------------------------------------------------------- +// GameAccountClient +// +//----------------------------------------------------------------------------- + +class CSchGameAccountClient : public GCSDK::CRecordBase +{ +public: + const static int k_iTable = 1; + CSchGameAccountClient(); + int GetITable() const; + CSchGameAccountClient( const CSchGameAccountClient &that ); + void operator=( const CSchGameAccountClient &that ); + + uint32 m_unAccountID; // Item Owner + + static int m_nPrimaryKeyID; + + const static int k_iField_unAccountID = 0; + const static int k_iFieldMax = 1; +}; + +namespace PORTAL_GCSCHEMA +{ +// ITABLE_STATS_BEGIN is the number of the first stats table; +// this should be one more than the number of the last data table. +const int ITABLE_STATS_BEGIN = 2; + +const int k_iTableStatsFirst = -1; +const int k_iTableStatsMax = -1; +const int NUM_BASE_STATS_TABLES = 0; + +extern void GenerateIntrinsicSQLSchema( GCSDK::CSchemaFull &schemaFull ); + +} +#pragma pack(pop) +#endif // PORTAL_GCSCHEMA_H diff --git a/game/shared/portal/portal_gcschema.sch b/game/shared/portal/portal_gcschema.sch new file mode 100644 index 0000000..d9fe7a5 --- /dev/null +++ b/game/shared/portal/portal_gcschema.sch @@ -0,0 +1,38 @@ +START_SCHEMA( GC, cbase.h ) + +//----------------------------------------------------------------------------- +// GameAccount +// +//----------------------------------------------------------------------------- +START_TABLE( k_ESchemaCatalogMain, GameAccount, TABLE_PROP_NORMAL ) +MEM_FIELD_BIN( unAccountID, AccountID, uint32 ) // Account ID of the user +MEM_FIELD_BIN( unRewardPoints, RewardPoints, uint32 ) // number of timed reward points (coplayed minutes) for this user +MEM_FIELD_BIN( unPointCap, PointCap, uint32 ) // Current maximum number of points +MEM_FIELD_BIN( unLastCapRollover, LastCapRollover, RTime32 ) // Last time the player's cap was adjusted +PRIMARY_KEY_CLUSTERED( 100, unAccountID ) +WIPE_TABLE_BETWEEN_TESTS( k_EWipePolicyWipeForAllTests ) +ALLOW_WIPE_TABLE_IN_PRODUCTION( false ) +END_TABLE + + +//----------------------------------------------------------------------------- +// GameAccountClient +// +//----------------------------------------------------------------------------- +START_TABLE( k_ESchemaCatalogMain, GameAccountClient, TABLE_PROP_NORMAL ) +MEM_FIELD_BIN( unAccountID, AccountID, uint32 ) // Item Owner +PRIMARY_KEY_CLUSTERED( 80, unAccountID ) +WIPE_TABLE_BETWEEN_TESTS( k_EWipePolicyWipeForAllTests ) +ALLOW_WIPE_TABLE_IN_PRODUCTION( false ) +END_TABLE + + + + +// -------------------------------------------------------- +// WARNING! All new tables need to be added to the end of the file +// if you expect to deploy the GC without deploying new clients. +// -------------------------------------------------------- + +// NEED A CARRIAGE RETURN HERE! +//-------------------------
\ No newline at end of file diff --git a/game/shared/portal/portal_mp_gamerules.cpp b/game/shared/portal/portal_mp_gamerules.cpp new file mode 100644 index 0000000..4e2ad28 --- /dev/null +++ b/game/shared/portal/portal_mp_gamerules.cpp @@ -0,0 +1,1283 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The Half-Life 2 game rules, such as the relationship tables and ammo +// damage cvars. +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "portal_gamerules.h" +#include "viewport_panel_names.h" +#include "gameeventdefs.h" +#include <KeyValues.h> +#include "ammodef.h" +#include "hl2_shareddefs.h" + +#ifdef CLIENT_DLL +#include "c_portal_player.h" +#else + +#include "eventqueue.h" +#include "player.h" +#include "gamerules.h" +#include "game.h" +#include "items.h" +#include "entitylist.h" +#include "mapentities.h" +#include "in_buttons.h" +#include <ctype.h> +#include "voice_gamemgr.h" +#include "iscorer.h" +#include "portal_player.h" +//#include "weapon_hl2mpbasehlmpcombatweapon.h" +#include "team.h" +#include "voice_gamemgr.h" + + +extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse); + + +ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY ); +ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY ); +ConVar mp_restartgame( "mp_restartgame", "0", 0, "If non-zero, game will restart in the specified number of seconds" ); +ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY ); + +extern ConVar mp_chattime; + +#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64 + +#endif + + +REGISTER_GAMERULES_CLASS( CPortalMPGameRules ); + +BEGIN_NETWORK_TABLE_NOBASE( CPortalMPGameRules, DT_PortalMPGameRules ) + +#ifdef CLIENT_DLL +RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ), +#else +SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ), +#endif + +END_NETWORK_TABLE() + + +LINK_ENTITY_TO_CLASS( portalmp_gamerules, CPortalMPGameRulesProxy ); +IMPLEMENT_NETWORKCLASS_ALIASED( PortalMPGameRulesProxy, DT_PortalMPGameRulesProxy ) + +static PortalMPViewVectors g_PortalMPViewVectors( + Vector( 0, 0, 64 ), //VEC_VIEW (m_vView) + + Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin) + Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax) + + Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin) + Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax) + Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView) + + Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin) + Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax) + + Vector( 0, 0, 14 ), //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight) + + Vector(-16, -16, 0 ), //VEC_CROUCH_TRACE_MIN (m_vCrouchTraceMin) + Vector( 16, 16, 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax) + ); + +static const char *s_PreserveEnts[] = +{ + "ai_network", + "ai_hint", + "hl2mp_gamerules", + "team_manager", + "player_manager", + "env_soundscape", + "env_soundscape_proxy", + "env_soundscape_triggerable", + "env_sun", + "env_wind", + "env_fog_controller", + "func_brush", + "func_wall", + "func_buyzone", + "func_illusionary", + "infodecal", + "info_projecteddecal", + "info_node", + "info_target", + "info_node_hint", + "info_player_deathmatch", + "info_player_combine", + "info_player_rebel", + "info_map_parameters", + "keyframe_rope", + "move_rope", + "info_ladder", + "player", + "point_viewcontrol", + "scene_manager", + "shadow_control", + "sky_camera", + "soundent", + "trigger_soundscape", + "viewmodel", + "predicted_viewmodel", + "worldspawn", + "point_devshot_camera", + "", // END Marker +}; + + + +#ifdef CLIENT_DLL +void RecvProxy_PortalMPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + CPortalMPGameRules *pRules = PortalMPGameRules(); + Assert( pRules ); + *pOut = pRules; +} + +BEGIN_RECV_TABLE( CPortalMPGameRulesProxy, DT_PortalMPGameRulesProxy ) +RecvPropDataTable( "portalmp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_PortalMPGameRules ), RecvProxy_PortalMPRules ) +END_RECV_TABLE() +#else +void* SendProxy_PortalMPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) +{ + CPortalMPGameRules *pRules = PortalMPGameRules(); + Assert( pRules ); + return pRules; +} + +BEGIN_SEND_TABLE( CPortalMPGameRulesProxy, DT_PortalMPGameRulesProxy ) +SendPropDataTable( "portalmp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_PortalMPGameRules ), SendProxy_PortalMPRules ) +END_SEND_TABLE() +#endif + +#ifndef CLIENT_DLL + +class CVoiceGameMgrHelper : public IVoiceGameMgrHelper +{ +public: + virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker ) + { + return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() ); + } +}; +CVoiceGameMgrHelper g_VoiceGameMgrHelper; +IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper; + +#endif + +// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc. +char *sTeamNames[] = +{ + "Unassigned", + "Spectator", + "Combine", + "Rebels", +}; + +CPortalMPGameRules::CPortalMPGameRules() +{ +#ifndef CLIENT_DLL + // Create the team managers + for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ ) + { + CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "team_manager" )); + pTeam->Init( sTeamNames[i], i ); + + g_Teams.AddToTail( pTeam ); + } + + m_bTeamPlayEnabled = teamplay.GetBool(); + m_flIntermissionEndTime = 0.0f; + m_flGameStartTime = 0; + + m_hRespawnableItemsAndWeapons.RemoveAll(); + m_tmNextPeriodicThink = 0; + m_flRestartGameTime = 0; + m_bCompleteReset = false; + m_bHeardAllPlayersReady = false; + m_bAwaitingReadyRestart = false; + +#endif +} + +const CViewVectors* CPortalMPGameRules::GetViewVectors()const +{ + return &g_PortalMPViewVectors; +} + +const PortalMPViewVectors* CPortalMPGameRules::GetPortalMPViewVectors()const +{ + return &g_PortalMPViewVectors; +} + +CPortalMPGameRules::~CPortalMPGameRules( void ) +{ +#ifndef CLIENT_DLL + // Note, don't delete each team since they are in the gEntList and will + // automatically be deleted from there, instead. + g_Teams.Purge(); +#endif +} + +void CPortalMPGameRules::CreateStandardEntities( void ) +{ + +#ifndef CLIENT_DLL + // Create the entity that will send our data to the client. + + BaseClass::CreateStandardEntities(); + +#ifdef _DEBUG + CBaseEntity *pEnt = +#endif + CBaseEntity::Create( "portalmp_gamerules", vec3_origin, vec3_angle ); + Assert( pEnt ); +#endif +} + +//========================================================= +// FlWeaponRespawnTime - what is the time in the future +// at which this weapon may spawn? +//========================================================= +float CPortalMPGameRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( weaponstay.GetInt() > 0 ) + { + // make sure it's only certain weapons + if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) + { + return 0; // weapon respawns almost instantly + } + } + + return sv_hl2mp_weapon_respawn_time.GetFloat(); +#endif + + return 0; // weapon respawns almost instantly +} + + +bool CPortalMPGameRules::IsIntermission( void ) +{ +#ifndef CLIENT_DLL + return m_flIntermissionEndTime > gpGlobals->curtime; +#endif + + return false; +} + +void CPortalMPGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ) +{ +#ifndef CLIENT_DLL + if ( IsIntermission() ) + return; + BaseClass::PlayerKilled( pVictim, info ); +#endif +} + + +void CPortalMPGameRules::Think( void ) +{ + +#ifndef CLIENT_DLL + + CGameRules::Think(); + + if ( g_fGameOver ) // someone else quit the game already + { + // check to see if we should change levels now + if ( m_flIntermissionEndTime < gpGlobals->curtime ) + { + ChangeLevel(); // intermission is over + } + + return; + } + + // float flTimeLimit = mp_timelimit.GetFloat() * 60; + float flFragLimit = fraglimit.GetFloat(); + + if ( GetMapRemainingTime() < 0 ) + { + GoToIntermission(); + return; + } + + if ( flFragLimit ) + { + if( IsTeamplay() == true ) + { + CTeam *pCombine = g_Teams[TEAM_COMBINE]; + CTeam *pRebels = g_Teams[TEAM_REBELS]; + + if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit ) + { + GoToIntermission(); + return; + } + } + else + { + // check if any player is over the frag limit + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && pPlayer->FragCount() >= flFragLimit ) + { + GoToIntermission(); + return; + } + } + } + } + + if ( gpGlobals->curtime > m_tmNextPeriodicThink ) + { + CheckAllPlayersReady(); + CheckRestartGame(); + m_tmNextPeriodicThink = gpGlobals->curtime + 1.0; + } + + if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime ) + { + RestartGame(); + } + + if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady ) + { + UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" ); + UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" ); + + m_flRestartGameTime = gpGlobals->curtime + 5; + m_bAwaitingReadyRestart = false; + } + + ManageObjectRelocation(); + +#endif +} + +void CPortalMPGameRules::GoToIntermission( void ) +{ +#ifndef CLIENT_DLL + if ( g_fGameOver ) + return; + + g_fGameOver = true; + + m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt(); + + for ( int i = 0; i < MAX_PLAYERS; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD ); + pPlayer->AddFlag( FL_FROZEN ); + } +#endif + +} + +bool CPortalMPGameRules::CheckGameOver() +{ +#ifndef CLIENT_DLL + if ( g_fGameOver ) // someone else quit the game already + { + // check to see if we should change levels now + if ( m_flIntermissionEndTime < gpGlobals->curtime ) + { + ChangeLevel(); // intermission is over + } + + return true; + } +#endif + + return false; +} + +// when we are within this close to running out of entities, items +// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn +#define ENTITY_INTOLERANCE 100 + +//========================================================= +// FlWeaponRespawnTime - Returns 0 if the weapon can respawn +// now, otherwise it returns the time at which it can try +// to spawn again. +//========================================================= +float CPortalMPGameRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) + { + if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) + return 0; + + // we're past the entity tolerance level, so delay the respawn + return FlWeaponRespawnTime( pWeapon ); + } +#endif + return 0; +} + +//========================================================= +// VecWeaponRespawnSpot - where should this weapon spawn? +// Some game variations may choose to randomize spawn locations +//========================================================= +Vector CPortalMPGameRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ) +{ +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled weapon respawn location code" ) +#if 0 +#ifndef CLIENT_DLL + CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon ); + + if ( pHL2Weapon ) + { + return pHL2Weapon->GetOriginalSpawnOrigin(); + } +#endif +#endif + + return pWeapon->GetAbsOrigin(); +} + +#ifndef CLIENT_DLL + +CItem* IsManagedObjectAnItem( CBaseEntity *pObject ) +{ + return dynamic_cast< CItem*>( pObject ); +} + +CWeaponPortalBase* IsManagedObjectAWeapon( CBaseEntity *pObject ) +{ + return dynamic_cast<CWeaponPortalBase*>( pObject ); +} + +bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles ) +{ + if ( CItem *pItem = IsManagedObjectAnItem( pObject ) ) + { +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled rest time code" ) +#if 0 + if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime ) + return false; +#endif + vOriginalOrigin = pItem->GetOriginalSpawnOrigin(); + vOriginalAngles = pItem->GetOriginalSpawnAngles(); + +#if 0 + pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat(); +#endif + return true; + } + else if ( CWeaponPortalBase *pWeapon = IsManagedObjectAWeapon( pObject )) + { + if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime ) + return false; + + vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin(); + vOriginalAngles = pWeapon->GetOriginalSpawnAngles(); + + pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat(); + return true; + } + + return false; +} + +void CPortalMPGameRules::ManageObjectRelocation( void ) +{ + int iTotal = m_hRespawnableItemsAndWeapons.Count(); + + if ( iTotal > 0 ) + { + for ( int i = 0; i < iTotal; i++ ) + { + CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get(); + + if ( pObject ) + { + Vector vSpawOrigin; + QAngle vSpawnAngles; + + if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true ) + { + float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length(); + + if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN ) + { + bool shouldReset = false; + IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); + + if ( pPhysics ) + { + shouldReset = pPhysics->IsAsleep(); + } + else + { + shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false; + } + + if ( shouldReset ) + { + pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL ); + pObject->EmitSound( "AlyxEmp.Charge" ); + + IPhysicsObject *pPhys = pObject->VPhysicsGetObject(); + + if ( pPhys ) + { + pPhys->Wake(); + } + } + } + } + } + } + } +} + +//========================================================= +//AddLevelDesignerPlacedWeapon +//========================================================= +void CPortalMPGameRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity ) +{ + if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 ) + { + m_hRespawnableItemsAndWeapons.AddToTail( pEntity ); + } +} + +//========================================================= +//RemoveLevelDesignerPlacedWeapon +//========================================================= +void CPortalMPGameRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ) +{ + if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 ) + { + m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity ); + } +} + +//========================================================= +// Where should this item respawn? +// Some game variations may choose to randomize spawn locations +//========================================================= +Vector CPortalMPGameRules::VecItemRespawnSpot( CItem *pItem ) +{ + return pItem->GetOriginalSpawnOrigin(); +} + +//========================================================= +// What angles should this item use to respawn? +//========================================================= +QAngle CPortalMPGameRules::VecItemRespawnAngles( CItem *pItem ) +{ + return pItem->GetOriginalSpawnAngles(); +} + +//========================================================= +// At what time in the future may this Item respawn? +//========================================================= +float CPortalMPGameRules::FlItemRespawnTime( CItem *pItem ) +{ + return sv_hl2mp_item_respawn_time.GetFloat(); +} + + +//========================================================= +// CanHaveWeapon - returns false if the player is not allowed +// to pick up this weapon +//========================================================= +bool CPortalMPGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ) +{ + if ( weaponstay.GetInt() > 0 ) + { + if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) ) + return false; + } + + return BaseClass::CanHavePlayerItem( pPlayer, pItem ); +} + +#endif + +//========================================================= +// WeaponShouldRespawn - any conditions inhibiting the +// respawning of this weapon? +//========================================================= +int CPortalMPGameRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) ) + { + return GR_WEAPON_RESPAWN_NO; + } +#endif + + return GR_WEAPON_RESPAWN_YES; +} + +//----------------------------------------------------------------------------- +// Purpose: Player has just left the game +//----------------------------------------------------------------------------- +void CPortalMPGameRules::ClientDisconnected( edict_t *pClient ) +{ +#ifndef CLIENT_DLL + // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" ); + + CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); + if ( pPlayer ) + { + // Remove the player from his team + if ( pPlayer->GetTeam() ) + { + pPlayer->GetTeam()->RemovePlayer( pPlayer ); + } + } + + BaseClass::ClientDisconnected( pClient ); + +#endif +} + + +//========================================================= +// Deathnotice. +//========================================================= +void CPortalMPGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) +{ +#ifndef CLIENT_DLL + // Work out what killed the player, and send a message to all clients about it + const char *killer_weapon_name = "world"; // by default, the player is killed by the world + int killer_ID = 0; + + // Find the killer & the scorer + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); + + // Custom kill type? + if ( info.GetDamageCustom() ) + { + killer_weapon_name = GetDamageCustomString( info ); + if ( pScorer ) + { + killer_ID = pScorer->GetUserID(); + } + } + else + { + // Is the killer a client? + if ( pScorer ) + { + killer_ID = pScorer->GetUserID(); + + if ( pInflictor ) + { + if ( pInflictor == pScorer ) + { + // If the inflictor is the killer, then it must be their current weapon doing the damage + if ( pScorer->GetActiveWeapon() ) + { + killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); + } + } + else + { + killer_weapon_name = pInflictor->GetClassname(); // it's just that easy + } + } + } + else + { + killer_weapon_name = pInflictor->GetClassname(); + } + + // strip the NPC_* or weapon_* from the inflictor's classname + if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) + { + killer_weapon_name += 7; + } + else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 ) + { + killer_weapon_name += 4; + } + else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) + { + killer_weapon_name += 5; + } + else if ( strstr( killer_weapon_name, "physics" ) ) + { + killer_weapon_name = "physics"; + } + + if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 ) + { + killer_weapon_name = "combine_ball"; + } + else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 ) + { + killer_weapon_name = "smg1_grenade"; + } + else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0) + { + killer_weapon_name = "slam"; + } + + + } + + IGameEvent *event = gameeventmanager->CreateEvent( "player_death" ); + if( event ) + { + event->SetInt("userid", pVictim->GetUserID() ); + event->SetInt("attacker", killer_ID ); + event->SetString("weapon", killer_weapon_name ); + event->SetInt( "priority", 7 ); + gameeventmanager->FireEvent( event ); + } +#endif + +} + +void CPortalMPGameRules::ClientSettingsChanged( CBasePlayer *pPlayer ) +{ +#ifndef CLIENT_DLL + + CPortal_Player *pPortalPlayer = ToPortalPlayer( pPlayer ); + //CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer ); + + if ( pPortalPlayer == NULL ) + return; + + const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() ); + const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" ); + + //If we're different. + if ( stricmp( szModelName, pCurrentModel ) ) + { + //Too soon, set the cvar back to what it was. + //Note: this will make this function be called again + //but since our models will match it'll just skip this whole dealio. +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled model change code" ) +#if 0 + if ( pPortalPlayer->GetNextModelChangeTime() >= gpGlobals->curtime ) + { + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel ); + engine->ClientCommand ( pPlayer->edict(), szReturnString ); + + Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pPlayer->GetNextModelChangeTime() - gpGlobals->curtime) ); + ClientPrint( pPlayer, HUD_PRINTTALK, szReturnString ); + return; + } +#endif + + if ( PortalMPGameRules()->IsTeamplay() == false ) + { + pPortalPlayer->SetPlayerModel(); + + const char *pszCurrentModelName = modelinfo->GetModelName( pPlayer->GetModel() ); + + char szReturnString[128]; + Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName ); + + ClientPrint( pPlayer, HUD_PRINTTALK, szReturnString ); + } + else + { + if ( Q_stristr( szModelName, "models/human") ) + { + pPlayer->ChangeTeam( TEAM_REBELS ); + } + else + { + pPlayer->ChangeTeam( TEAM_COMBINE ); + } + } + } + if ( sv_report_client_settings.GetInt() == 1 ) + { + UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pPlayer->GetPlayerName(), engine->GetClientConVarValue( pPlayer->entindex(), "cl_cmdrate" )); + } + + BaseClass::ClientSettingsChanged( pPlayer ); +#endif + +} + +int CPortalMPGameRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) +{ +#ifndef CLIENT_DLL + // half life multiplay has a simple concept of Player Relationships. + // you are either on another player's team, or you are not. + if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false ) + return GR_NOTTEAMMATE; + + if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) ) + { + return GR_TEAMMATE; + } +#endif + + return GR_NOTTEAMMATE; +} + +const char *CPortalMPGameRules::GetGameDescription( void ) +{ + return "PMP Test"; +} + + +float CPortalMPGameRules::GetMapRemainingTime() +{ + // if timelimit is disabled, return 0 + if ( mp_timelimit.GetInt() <= 0 ) + return 0; + + // timelimit is in minutes + + float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime; + + return timeleft; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPortalMPGameRules::Precache( void ) +{ + CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" ); +} + +bool CPortalMPGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) +{ + if ( collisionGroup0 > collisionGroup1 ) + { + // swap so that lowest is always first + swap(collisionGroup0,collisionGroup1); + } + + if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && + collisionGroup1 == COLLISION_GROUP_WEAPON ) + { + return false; + } + + return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); + +} + +bool CPortalMPGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args ) +{ + +#ifndef CLIENT_DLL + if( BaseClass::ClientCommand( pEdict, args ) ) + return true; + + + CBasePlayer *pPlayer = (CBasePlayer *) pEdict; + + if ( pPlayer->ClientCommand( args ) ) + return true; +#endif + + return false; +} + +// shared ammo definition +// JAY: Trying to make a more physical bullet response +#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) +#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) + +// exaggerate all of the forces, but use real numbers to keep them consistent +#define BULLET_IMPULSE_EXAGGERATION 3.5 +// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s +#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION) + + +CAmmoDef *GetAmmoDef() +{ + static CAmmoDef def; + static bool bInitted = false; + + if ( !bInitted ) + { + bInitted = true; + + def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_ar2", "sk_npc_dmg_ar2", "sk_max_ar2", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("AlyxGun", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_alyxgun", "sk_npc_dmg_alyxgun", "sk_max_alyxgun", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_pistol", "sk_npc_dmg_pistol", "sk_max_pistol", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_smg1", "sk_npc_dmg_smg1", "sk_max_smg1", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_357", "sk_npc_dmg_357", "sk_max_357", BULLET_IMPULSE(800, 5000), 0 ); + def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_crossbow", "sk_npc_dmg_crossbow", "sk_max_crossbow", BULLET_IMPULSE(800, 8000), 0 ); + + def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, "sk_plr_dmg_buckshot", "sk_npc_dmg_buckshot", "sk_max_buckshot", BULLET_IMPULSE(400, 1200), 0 ); + def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, "sk_plr_dmg_rpg_round", "sk_npc_dmg_rpg_round", "sk_max_rpg_round", 0, 0 ); + def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_smg1_grenade", "sk_npc_dmg_smg1_grenade", "sk_max_smg1_grenade", 0, 0 ); + def.AddAmmoType("SniperRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_plr_dmg_sniper_round", "sk_npc_dmg_sniper_round", "sk_max_sniper_round", BULLET_IMPULSE(650, 6000), 0 ); + def.AddAmmoType("SniperPenetratedRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_dmg_sniper_penetrate_plr", "sk_dmg_sniper_penetrate_npc", "sk_max_sniper_round", BULLET_IMPULSE(150, 6000), 0 ); + def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_grenade", 0, 0); + def.AddAmmoType("Thumper", DMG_SONIC, TRACER_NONE, 10, 10, 2, 0, 0 ); + def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 ); + def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, NULL, NULL, NULL, 0, 0 ); + def.AddAmmoType("GaussEnergy", DMG_SHOCK, TRACER_NONE, "sk_jeep_gauss_damage", "sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s + def.AddAmmoType("CombineCannon", DMG_BULLET, TRACER_LINE, "sk_npc_dmg_gunship_to_plr", "sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s + def.AddAmmoType("AirboatGun", DMG_AIRBOAT, TRACER_LINE, "sk_plr_dmg_airboat", "sk_npc_dmg_airboat", NULL, BULLET_IMPULSE(10, 600), 0 ); + def.AddAmmoType("StriderMinigun", DMG_BULLET, TRACER_LINE, 5, 5, 15, 1.0 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 1.0kg weight at 750 ft/s + def.AddAmmoType("HelicopterGun", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_npc_dmg_helicopter_to_plr", "sk_npc_dmg_helicopter", "sk_max_smg1", BULLET_IMPULSE(400, 1225), AMMO_FORCE_DROP_IF_CARRIED | AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER ); + def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, "sk_max_ar2_altfire", 0, 0 ); +#ifdef HL2_EPISODIC + def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); + def.AddAmmoType("HunterGun", DMG_BULLET, TRACER_LINE, "sk_hunter_dmg", "sk_hunter_dmg", "sk_hunter_max_round", BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("CombineHeavyCannon", DMG_BLAST, TRACER_LINE, 40, 40, NULL, 1.5 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 100 kg weight at 750 ft/s +#endif // HL2_EPISODIC + } + + return &def; +} + +#ifdef CLIENT_DLL + +ConVar cl_autowepswitch( + "cl_autowepswitch", + "1", + FCVAR_ARCHIVE | FCVAR_USERINFO, + "Automatically switch to picked up weapons (if more powerful)" ); + +#else + + +bool CPortalMPGameRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ) +{ + if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() ) + { + // Player has an active item, so let's check cl_autowepswitch. + const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" ); + if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 ) + { + return false; + } + } + + return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon ); +} + +#endif + +#ifndef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: Damage (applied per second) value of the npc_laser_turret +//----------------------------------------------------------------------------- +float CPortalMPGameRules::GetLaserTurretDamage( void ) +{ + switch( GetSkillLevel() ) + { + case SKILL_EASY: + return 120.0f; + + case SKILL_MEDIUM: + return 200.0f; + + case SKILL_HARD: + return 200.0f; + + default: + return 100.0f; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Movement speed of the turret. +//----------------------------------------------------------------------------- +float CPortalMPGameRules::GetLaserTurretMoveSpeed( void ) +{ + switch( GetSkillLevel() ) + { + case SKILL_EASY: + return 0.6f; + + case SKILL_MEDIUM: + return 0.8f; + + case SKILL_HARD: + return 1.0f; + + default: + return 0.7f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Damage value of the npc_rocket_turret +//----------------------------------------------------------------------------- +float CPortalMPGameRules::GetRocketTurretDamage( void ) +{ + switch( GetSkillLevel() ) + { + case SKILL_EASY: + return 120.0f; + + case SKILL_MEDIUM: + return 200.0f; + + case SKILL_HARD: + return 200.0f; + + default: + return 100.0f; + } +} + + +bool FindInList( const char **pStrings, const char *pToFind ); + +void CPortalMPGameRules::RestartGame() +{ + // bounds check + if ( mp_timelimit.GetInt() < 0 ) + { + mp_timelimit.SetValue( 0 ); + } + m_flGameStartTime = gpGlobals->curtime; + if ( !IsFinite( m_flGameStartTime.Get() ) ) + { + Warning( "Trying to set a NaN game start time\n" ); + m_flGameStartTime.GetForModify() = 0.0f; + } + + CleanUpMap(); + + // now respawn all players + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->Holster(); + } + pPlayer->RemoveAllItems( true ); + respawn( pPlayer, false ); +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled player reset" ) +#if 0 + pPlayer->Reset(); +#endif + } + + // Respawn entities (glass, doors, etc..) + + CTeam *pRebels = GetGlobalTeam( TEAM_REBELS ); + CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE ); + + if ( pRebels ) + { + pRebels->SetScore( 0 ); + } + + if ( pCombine ) + { + pCombine->SetScore( 0 ); + } + + m_flIntermissionEndTime = 0; + m_flRestartGameTime = 0.0; + m_bCompleteReset = false; + + IGameEvent * event = gameeventmanager->CreateEvent( "round_start" ); + if ( event ) + { + event->SetInt("fraglimit", 0 ); + event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted + + event->SetString("objective","DEATHMATCH"); + + gameeventmanager->FireEvent( event ); + } +} + +void CPortalMPGameRules::CleanUpMap() +{ + // Recreate all the map entities from the map data (preserving their indices), + // then remove everything else except the players. + + // Get rid of all entities except players. + CBaseEntity *pCur = gEntList.FirstEnt(); + while ( pCur ) + { + CBaseCombatWeapon *pWeapon = dynamic_cast< CBaseCombatWeapon* >( pCur ); + // Weapons with owners don't want to be removed.. + if ( pWeapon ) + { + if ( !pWeapon->GetOwner() || !pWeapon->GetOwner()->IsPlayer() ) + { + UTIL_Remove( pCur ); + } + } + // remove entities that has to be restored on roundrestart (breakables etc) + else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) + { + UTIL_Remove( pCur ); + } + + pCur = gEntList.NextEnt( pCur ); + } + + // Really remove the entities so we can have access to their slots below. + gEntList.CleanupDeleteList(); + + // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that + // could kill respawning CTs + g_EventQueue.Clear(); + +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled entity parsing" ) +#if 0 + // Now reload the map entities. + class CHL2MPMapEntityFilter : public IMapEntityFilter + { + public: + virtual bool ShouldCreateEntity( const char *pClassname ) + { + // Don't recreate the preserved entities. + if ( !FindInList( s_PreserveEnts, pClassname ) ) + { + return true; + } + else + { + // Increment our iterator since it's not going to call CreateNextEntity for this ent. + if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); + + return false; + } + } + + + virtual CBaseEntity* CreateNextEntity( const char *pClassname ) + { + if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) + { + // This shouldn't be possible. When we loaded the map, it should have used + // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list + // with the same list of entities we're referring to here. + Assert( false ); + return NULL; + } + else + { + CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. + + if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) ) + { + // Doh! The entity was delete and its slot was reused. + // Just use any old edict slot. This case sucks because we lose the baseline. + return CreateEntityByName( pClassname ); + } + else + { + // Cool, the slot where this entity was is free again (most likely, the entity was + // freed above). Now create an entity with this specific index. + return CreateEntityByName( pClassname, ref.m_iEdict ); + } + } + } + + public: + int m_iIterator; // Iterator into g_MapEntityRefs. + }; + + CHL2MPMapEntityFilter filter; + filter.m_iIterator = g_MapEntityRefs.Head(); + + // DO NOT CALL SPAWN ON info_node ENTITIES! + + MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); +#endif +} + +void CPortalMPGameRules::CheckRestartGame( void ) +{ + // Restart the game if specified by the server + int iRestartDelay = mp_restartgame.GetInt(); + + if ( iRestartDelay > 0 ) + { + if ( iRestartDelay > 60 ) + iRestartDelay = 60; + + + // let the players know + char strRestartDelay[64]; + Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay ); + UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); + UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); + + m_flRestartGameTime = gpGlobals->curtime + iRestartDelay; + m_bCompleteReset = true; + mp_restartgame.SetValue( 0 ); + } + +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled ready restart" ) +#if 0 + if( mp_readyrestart.GetBool() ) + { + m_bAwaitingReadyRestart = true; + m_bHeardAllPlayersReady = false; + + + const char *pszReadyString = mp_ready_signal.GetString(); + + + // Don't let them put anything malicious in there + if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 ) + { + pszReadyString = "ready"; + } + + IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + mp_readyrestart.SetValue( 0 ); + + // cancel any restart round in progress + m_flRestartGameTime = -1; + } +#endif +} + +void CPortalMPGameRules::CheckAllPlayersReady( void ) +{ +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled ready restart" ) +#if 0 + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + if ( !pPlayer->IsReady() ) + return; + } +#endif + m_bHeardAllPlayersReady = true; +} + +#endif
\ No newline at end of file diff --git a/game/shared/portal/portal_mp_gamerules.h b/game/shared/portal/portal_mp_gamerules.h new file mode 100644 index 0000000..d95f215 --- /dev/null +++ b/game/shared/portal/portal_mp_gamerules.h @@ -0,0 +1,173 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game rules for Portal multiplayer testing. +// +//=============================================================================// + +#ifndef PORTAL_MP +#pragma message( __FILE__ "(" __LINE__AS_STRING ") : error custom: This file should not be included anywhere except in the portal multiplayer testing builds" ) +#endif + +#ifndef PORTAL_MP_GAMERULES_H +#define PORTAL_MP_GAMERULES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "gamerules.h" +//#include "hl2mp_gamerules.h" +//#include "multiplay_gamerules.h" +#include "teamplay_gamerules.h" + +class CPortal_Player; + +#ifdef CLIENT_DLL +#define CPortalMPGameRules C_PortalMPGameRules +#define CPortalMPGameRulesProxy C_PortalMPGameRulesProxy +#endif + + +enum +{ + TEAM_COMBINE = 2, + TEAM_REBELS, +}; + + +class CPortalMPGameRulesProxy : public CGameRulesProxy +{ +public: + DECLARE_CLASS( CPortalMPGameRulesProxy, CGameRulesProxy ); + DECLARE_NETWORKCLASS(); +}; + +class PortalMPViewVectors : public CViewVectors +{ +public: + PortalMPViewVectors( + Vector vView, + Vector vHullMin, + Vector vHullMax, + Vector vDuckHullMin, + Vector vDuckHullMax, + Vector vDuckView, + Vector vObsHullMin, + Vector vObsHullMax, + Vector vDeadViewHeight, + Vector vCrouchTraceMin, + Vector vCrouchTraceMax ) : + CViewVectors( + vView, + vHullMin, + vHullMax, + vDuckHullMin, + vDuckHullMax, + vDuckView, + vObsHullMin, + vObsHullMax, + vDeadViewHeight ) + { + m_vCrouchTraceMin = vCrouchTraceMin; + m_vCrouchTraceMax = vCrouchTraceMax; + } + + Vector m_vCrouchTraceMin; + Vector m_vCrouchTraceMax; +}; + +class CPortalMPGameRules : public CTeamplayRules +{ +public: + //DECLARE_CLASS( CPortalGameRules, CSingleplayRules ); + //DECLARE_CLASS( CPortalGameRules, CMultiplayRules ); + DECLARE_CLASS( CPortalMPGameRules, CTeamplayRules ); + +#ifdef CLIENT_DLL + DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. +#else + DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. +#endif + + CPortalMPGameRules( void ); + virtual ~CPortalMPGameRules( void ); + + virtual void Precache( void ); + virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 ); + virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args ); + + virtual float FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ); + virtual float FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ); + virtual Vector VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ); + virtual int WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ); + virtual void Think( void ); + virtual void CreateStandardEntities( void ); + virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); + virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); + virtual void GoToIntermission( void ); + virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + virtual const char *GetGameDescription( void ); + // derive this function if you mod uses encrypted weapon info files + virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"x9Ke0BY7"; } + virtual const CViewVectors* GetViewVectors() const; + const PortalMPViewVectors* GetPortalMPViewVectors() const; + + float GetMapRemainingTime(); + void CleanUpMap(); + void CheckRestartGame(); + void RestartGame(); + +#ifndef CLIENT_DLL + virtual Vector VecItemRespawnSpot( CItem *pItem ); + virtual QAngle VecItemRespawnAngles( CItem *pItem ); + virtual float FlItemRespawnTime( CItem *pItem ); + virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ); + virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ); + + void AddLevelDesignerPlacedObject( CBaseEntity *pEntity ); + void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ); + void ManageObjectRelocation( void ); + + virtual float GetLaserTurretDamage( void ); + virtual float GetLaserTurretMoveSpeed( void ); + virtual float GetRocketTurretDamage( void ); +#endif + virtual void ClientDisconnected( edict_t *pClient ); + + bool CheckGameOver( void ); + bool IsIntermission( void ); + + void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + + + bool IsTeamplay( void ) { return m_bTeamPlayEnabled; } + void CheckAllPlayersReady( void ); + +private: + + CNetworkVar( bool, m_bTeamPlayEnabled ); + CNetworkVar( float, m_flGameStartTime ); + CUtlVector<EHANDLE> m_hRespawnableItemsAndWeapons; + float m_tmNextPeriodicThink; + float m_flRestartGameTime; + bool m_bCompleteReset; + bool m_bAwaitingReadyRestart; + bool m_bHeardAllPlayersReady; +}; + + +//----------------------------------------------------------------------------- +// Gets us at the Half-Life 2 game rules +//----------------------------------------------------------------------------- +inline CPortalMPGameRules* PortalMPGameRules() +{ + return static_cast<CPortalMPGameRules*>(g_pGameRules); +} + +inline CPortalMPGameRules* PortalGameRules() +{ + return static_cast<CPortalMPGameRules*>(g_pGameRules); +} + + + +#endif // PORTAL_MP_GAMERULES_H diff --git a/game/shared/portal/portal_player_shared.cpp b/game/shared/portal/portal_player_shared.cpp new file mode 100644 index 0000000..3735da2 --- /dev/null +++ b/game/shared/portal/portal_player_shared.cpp @@ -0,0 +1,926 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" + +#ifdef CLIENT_DLL +#include "c_portal_player.h" +#include "prediction.h" +#define CRecipientFilter C_RecipientFilter +#else +#include "portal_player.h" +#include "ai_basenpc.h" +#include "portal_gamestats.h" +#include "util.h" +#endif + +#include "engine/IEngineSound.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" + +acttable_t unarmedActtable[] = +{ + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, +}; + +const char *g_pszChellConcepts[] = +{ + "CONCEPT_CHELL_IDLE", + "CONCEPT_CHELL_DEAD", +}; + +extern ConVar sv_footsteps; +extern ConVar sv_debug_player_use; + +extern float IntervalDistance( float x, float x0, float x1 ); + + +//----------------------------------------------------------------------------- +// Consider the weapon's built-in accuracy, this character's proficiency with +// the weapon, and the status of the target. Use this information to determine +// how accurately to shoot at the target. +//----------------------------------------------------------------------------- +Vector CPortal_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) +{ + if ( pWeapon ) + return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT ); + + return VECTOR_CONE_15DEGREES; +} + +void CPortal_Player::GetStepSoundVelocities( float *velwalk, float *velrun ) +{ + // UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!! + if ( ( GetFlags() & FL_DUCKING ) || ( GetMoveType() == MOVETYPE_LADDER ) ) + { + *velwalk = 10; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow + *velrun = 60; + } + else + { + *velwalk = 90; + *velrun = 220; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : step - +// fvol - +// force - force sound to play +//----------------------------------------------------------------------------- +void CPortal_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) +{ +#ifndef CLIENT_DLL + IncrementStepsTaken(); +#endif + + BaseClass::PlayStepSound( vecOrigin, psurface, fvol, force ); +} + +Activity CPortal_Player::TranslateActivity( Activity baseAct, bool *pRequired /* = NULL */ ) +{ + Activity translated = baseAct; + + if ( GetActiveWeapon() ) + { + translated = GetActiveWeapon()->ActivityOverride( baseAct, pRequired ); + } + else if ( unarmedActtable ) + { + acttable_t *pTable = unarmedActtable; + int actCount = ARRAYSIZE(unarmedActtable); + + for ( int i = 0; i < actCount; i++, pTable++ ) + { + if ( baseAct == pTable->baseAct ) + { + translated = (Activity)pTable->weaponAct; + } + } + } + else if (pRequired) + { + *pRequired = false; + } + + return translated; +} + +CWeaponPortalBase* CPortal_Player::GetActivePortalWeapon() const +{ + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( pWeapon ) + { + return dynamic_cast< CWeaponPortalBase* >( pWeapon ); + } + else + { + return NULL; + } +} + +CBaseEntity *CPortal_Player::FindUseEntity() +{ + Vector forward, up; + EyeVectors( &forward, NULL, &up ); + + trace_t tr; + // Search for objects in a sphere (tests for entities that are not solid, yet still useable) + Vector searchCenter = EyePosition(); + + // NOTE: Some debris objects are useable too, so hit those as well + // A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too. + int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP; + + UTIL_TraceLine( searchCenter, searchCenter + forward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr ); + // try the hit entity if there is one, or the ground entity if there isn't. + CBaseEntity *pNearest = NULL; + CBaseEntity *pObject = tr.m_pEnt; + + // TODO: Removed because we no longer have ghost animatings. We may need similar code that clips rays against transformed objects. +//#ifndef CLIENT_DLL +// // Check for ghost animatings (these aren't hit in the normal trace because they aren't solid) +// if ( !IsUseableEntity(pObject, 0) ) +// { +// Ray_t rayGhostAnimating; +// rayGhostAnimating.Init( searchCenter, searchCenter + forward * 1024 ); +// +// CBaseEntity *list[1024]; +// int nCount = UTIL_EntitiesAlongRay( list, 1024, rayGhostAnimating, 0 ); +// +// // Loop through all entities along the pick up ray +// for ( int i = 0; i < nCount; i++ ) +// { +// CGhostAnimating *pGhostAnimating = dynamic_cast<CGhostAnimating*>( list[i] ); +// +// // If the entity is a ghost animating +// if( pGhostAnimating ) +// { +// trace_t trGhostAnimating; +// enginetrace->ClipRayToEntity( rayGhostAnimating, MASK_ALL, pGhostAnimating, &trGhostAnimating ); +// +// if ( trGhostAnimating.fraction < tr.fraction ) +// { +// // If we're not grabbing the clipped ghost +// VPlane plane = pGhostAnimating->GetLocalClipPlane(); +// UTIL_Portal_PlaneTransform( pGhostAnimating->GetCloneTransform(), plane, plane ); +// if ( plane.GetPointSide( trGhostAnimating.endpos ) != SIDE_FRONT ) +// { +// tr = trGhostAnimating; +// pObject = tr.m_pEnt; +// } +// } +// } +// } +// } +//#endif + + int count = 0; + // UNDONE: Might be faster to just fold this range into the sphere query + const int NUM_TANGENTS = 7; + while ( !IsUseableEntity(pObject, 0) && count < NUM_TANGENTS) + { + // trace a box at successive angles down + // 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15 + const float tangents[NUM_TANGENTS] = { 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f }; + Vector down = forward - tangents[count]*up; + VectorNormalize(down); + UTIL_TraceHull( searchCenter, searchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr ); + pObject = tr.m_pEnt; + count++; + } + float nearestDot = CONE_90_DEGREES; + if ( IsUseableEntity(pObject, 0) ) + { + Vector delta = tr.endpos - tr.startpos; + float centerZ = CollisionProp()->WorldSpaceCenter().z; + delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z ); + float dist = delta.Length(); + if ( dist < PLAYER_USE_RADIUS ) + { +#ifndef CLIENT_DLL + + if ( sv_debug_player_use.GetBool() ) + { + NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); + NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); + } + + if ( pObject->MyNPCPointer() && pObject->MyNPCPointer()->IsPlayerAlly( this ) ) + { + // If about to select an NPC, do a more thorough check to ensure + // that we're selecting the right one from a group. + pObject = DoubleCheckUseNPC( pObject, searchCenter, forward ); + } + + g_PortalGameStats.Event_PlayerUsed( searchCenter, forward, pObject ); +#endif + + return pObject; + } + } + +#ifndef CLIENT_DLL + CBaseEntity *pFoundByTrace = pObject; +#endif + + // check ground entity first + // if you've got a useable ground entity, then shrink the cone of this search to 45 degrees + // otherwise, search out in a 90 degree cone (hemisphere) + if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) ) + { + pNearest = GetGroundEntity(); + nearestDot = CONE_45_DEGREES; + } + + for ( CEntitySphereQuery sphere( searchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) + { + if ( !pObject ) + continue; + + if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) + continue; + + // see if it's more roughly in front of the player than previous guess + Vector point; + pObject->CollisionProp()->CalcNearestPoint( searchCenter, &point ); + + Vector dir = point - searchCenter; + VectorNormalize(dir); + float dot = DotProduct( dir, forward ); + + // Need to be looking at the object more or less + if ( dot < 0.8 ) + continue; + + if ( dot > nearestDot ) + { + // Since this has purely been a radius search to this point, we now + // make sure the object isn't behind glass or a grate. + trace_t trCheckOccluded; + UTIL_TraceLine( searchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded ); + + if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject ) + { + pNearest = pObject; + nearestDot = dot; + } + } + } + +#ifndef CLIENT_DLL + if ( !pNearest ) + { + // Haven't found anything near the player to use, nor any NPC's at distance. + // Check to see if the player is trying to select an NPC through a rail, fence, or other 'see-though' volume. + trace_t trAllies; + UTIL_TraceLine( searchCenter, searchCenter + forward * PLAYER_USE_RADIUS, MASK_OPAQUE_AND_NPCS, this, COLLISION_GROUP_NONE, &trAllies ); + + if ( trAllies.m_pEnt && IsUseableEntity( trAllies.m_pEnt, 0 ) && trAllies.m_pEnt->MyNPCPointer() && trAllies.m_pEnt->MyNPCPointer()->IsPlayerAlly( this ) ) + { + // This is an NPC, take it! + pNearest = trAllies.m_pEnt; + } + } + + if ( pNearest && pNearest->MyNPCPointer() && pNearest->MyNPCPointer()->IsPlayerAlly( this ) ) + { + pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); + } + + if ( sv_debug_player_use.GetBool() ) + { + if ( !pNearest ) + { + NDebugOverlay::Line( searchCenter, tr.endpos, 255, 0, 0, true, 30 ); + NDebugOverlay::Cross3D( tr.endpos, 16, 255, 0, 0, true, 30 ); + } + else if ( pNearest == pFoundByTrace ) + { + NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); + NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); + } + else + { + NDebugOverlay::Box( pNearest->WorldSpaceCenter(), Vector(-8, -8, -8), Vector(8, 8, 8), 0, 255, 0, true, 30 ); + } + } + + g_PortalGameStats.Event_PlayerUsed( searchCenter, forward, pNearest ); +#endif + + return pNearest; +} + +CBaseEntity* CPortal_Player::FindUseEntityThroughPortal( void ) +{ + Vector forward, up; + EyeVectors( &forward, NULL, &up ); + + CProp_Portal *pPortal = GetHeldObjectPortal(); + + trace_t tr; + // Search for objects in a sphere (tests for entities that are not solid, yet still useable) + Vector searchCenter = EyePosition(); + + Vector vTransformedForward, vTransformedUp, vTransformedSearchCenter; + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + UTIL_Portal_PointTransform( matThisToLinked, searchCenter, vTransformedSearchCenter ); + UTIL_Portal_VectorTransform( matThisToLinked, forward, vTransformedForward ); + UTIL_Portal_VectorTransform( matThisToLinked, up, vTransformedUp ); + + + // NOTE: Some debris objects are useable too, so hit those as well + // A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too. + int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP; + + //UTIL_TraceLine( vTransformedSearchCenter, vTransformedSearchCenter + vTransformedForward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr ); + Ray_t rayLinked; + rayLinked.Init( searchCenter, searchCenter + forward * 1024 ); + UTIL_PortalLinked_TraceRay( pPortal, rayLinked, useableContents, this, COLLISION_GROUP_NONE, &tr ); + + // try the hit entity if there is one, or the ground entity if there isn't. + CBaseEntity *pNearest = NULL; + CBaseEntity *pObject = tr.m_pEnt; + int count = 0; + // UNDONE: Might be faster to just fold this range into the sphere query + const int NUM_TANGENTS = 7; + while ( !IsUseableEntity(pObject, 0) && count < NUM_TANGENTS) + { + // trace a box at successive angles down + // 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15 + const float tangents[NUM_TANGENTS] = { 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f }; + Vector down = vTransformedForward - tangents[count]*vTransformedUp; + VectorNormalize(down); + UTIL_TraceHull( vTransformedSearchCenter, vTransformedSearchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr ); + pObject = tr.m_pEnt; + count++; + } + float nearestDot = CONE_90_DEGREES; + if ( IsUseableEntity(pObject, 0) ) + { + Vector delta = tr.endpos - tr.startpos; + float centerZ = CollisionProp()->WorldSpaceCenter().z; + delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z ); + float dist = delta.Length(); + if ( dist < PLAYER_USE_RADIUS ) + { +#ifndef CLIENT_DLL + + if ( pObject->MyNPCPointer() && pObject->MyNPCPointer()->IsPlayerAlly( this ) ) + { + // If about to select an NPC, do a more thorough check to ensure + // that we're selecting the right one from a group. + pObject = DoubleCheckUseNPC( pObject, vTransformedSearchCenter, vTransformedForward ); + } +#endif + + return pObject; + } + } + + // check ground entity first + // if you've got a useable ground entity, then shrink the cone of this search to 45 degrees + // otherwise, search out in a 90 degree cone (hemisphere) + if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) ) + { + pNearest = GetGroundEntity(); + nearestDot = CONE_45_DEGREES; + } + + for ( CEntitySphereQuery sphere( vTransformedSearchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) + { + if ( !pObject ) + continue; + + if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) + continue; + + // see if it's more roughly in front of the player than previous guess + Vector point; + pObject->CollisionProp()->CalcNearestPoint( vTransformedSearchCenter, &point ); + + Vector dir = point - vTransformedSearchCenter; + VectorNormalize(dir); + float dot = DotProduct( dir, vTransformedForward ); + + // Need to be looking at the object more or less + if ( dot < 0.8 ) + continue; + + if ( dot > nearestDot ) + { + // Since this has purely been a radius search to this point, we now + // make sure the object isn't behind glass or a grate. + trace_t trCheckOccluded; + UTIL_TraceLine( vTransformedSearchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded ); + + if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject ) + { + pNearest = pObject; + nearestDot = dot; + } + } + } + +#ifndef CLIENT_DLL + if ( !pNearest ) + { + // Haven't found anything near the player to use, nor any NPC's at distance. + // Check to see if the player is trying to select an NPC through a rail, fence, or other 'see-though' volume. + trace_t trAllies; + UTIL_TraceLine( vTransformedSearchCenter, vTransformedSearchCenter + vTransformedForward * PLAYER_USE_RADIUS, MASK_OPAQUE_AND_NPCS, this, COLLISION_GROUP_NONE, &trAllies ); + + if ( trAllies.m_pEnt && IsUseableEntity( trAllies.m_pEnt, 0 ) && trAllies.m_pEnt->MyNPCPointer() && trAllies.m_pEnt->MyNPCPointer()->IsPlayerAlly( this ) ) + { + // This is an NPC, take it! + pNearest = trAllies.m_pEnt; + } + } + + if ( pNearest && pNearest->MyNPCPointer() && pNearest->MyNPCPointer()->IsPlayerAlly( this ) ) + { + pNearest = DoubleCheckUseNPC( pNearest, vTransformedSearchCenter, vTransformedForward ); + } + +#endif + + return pNearest; +} + + +#if 0 + +//========================== +// ANIMATION CODE +//========================== + +// Below this many degrees, slow down turning rate linearly +#define FADE_TURN_DEGREES 45.0f +// After this, need to start turning feet +#define MAX_TORSO_ANGLE 90.0f +// Below this amount, don't play a turning animation/perform IK +#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f + +static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." ); +extern ConVar sv_backspeed; +extern ConVar mp_feetyawrate; +extern ConVar mp_facefronttime; +extern ConVar mp_ik; + +CPlayerAnimState::CPlayerAnimState( CPortal_Player *outer ) + : m_pOuter( outer ) +{ + m_flGaitYaw = 0.0f; + m_flGoalFeetYaw = 0.0f; + m_flCurrentFeetYaw = 0.0f; + m_flCurrentTorsoYaw = 0.0f; + m_flLastYaw = 0.0f; + m_flLastTurnTime = 0.0f; + m_flTurnCorrectionTime = 0.0f; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::Update() +{ + m_angRender = GetOuter()->GetLocalAngles(); + + ComputePoseParam_BodyYaw(); + ComputePoseParam_BodyPitch( GetOuter()->GetModelPtr() ); + ComputePoseParam_BodyLookYaw(); + + ComputePlaybackRate(); + +#ifdef CLIENT_DLL + GetOuter()->UpdateLookAt(); +#endif + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePlaybackRate() +{ + // Determine ideal playback rate + Vector vel; + GetOuterAbsVelocity( vel ); + + float speed = vel.Length2D(); + + bool isMoving = ( speed > 0.5f ) ? true : false; + + float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() ); + + if ( isMoving && ( maxspeed > 0.0f ) ) + { + float flFactor = 1.0f; + + // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below + GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed ); + + // BUG BUG: + // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed + } + else + { + GetOuter()->SetPlaybackRate( 1.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBasePlayer +//----------------------------------------------------------------------------- +CPortal_Player *CPlayerAnimState::GetOuter() +{ + return m_pOuter; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CPlayerAnimState::EstimateYaw( void ) +{ + float dt = gpGlobals->frametime; + + if ( !dt ) + { + return; + } + + Vector est_velocity; + QAngle angles; + + GetOuterAbsVelocity( est_velocity ); + + angles = GetOuter()->GetLocalAngles(); + + if ( est_velocity[1] == 0 && est_velocity[0] == 0 ) + { + float flYawDiff = angles[YAW] - m_flGaitYaw; + flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; + if (flYawDiff > 180) + flYawDiff -= 360; + if (flYawDiff < -180) + flYawDiff += 360; + + if (dt < 0.25) + flYawDiff *= dt * 4; + else + flYawDiff *= dt; + + m_flGaitYaw += flYawDiff; + m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360; + } + else + { + m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI); + + if (m_flGaitYaw > 180) + m_flGaitYaw = 180; + else if (m_flGaitYaw < -180) + m_flGaitYaw = -180; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override for backpeddling +// Input : dt - +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePoseParam_BodyYaw( void ) +{ + int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" ); + if ( iYaw < 0 ) + return; + + // view direction relative to movement + float flYaw; + + EstimateYaw(); + + QAngle angles = GetOuter()->GetLocalAngles(); + float ang = angles[ YAW ]; + if ( ang > 180.0f ) + { + ang -= 360.0f; + } + else if ( ang < -180.0f ) + { + ang += 360.0f; + } + + // calc side to side turning + flYaw = ang - m_flGaitYaw; + // Invert for mapping into 8way blend + flYaw = -flYaw; + flYaw = flYaw - (int)(flYaw / 360) * 360; + + if (flYaw < -180) + { + flYaw = flYaw + 360; + } + else if (flYaw > 180) + { + flYaw = flYaw - 360; + } + + GetOuter()->SetPoseParameter( iYaw, flYaw ); + +#ifndef CLIENT_DLL + //Adrian: Make the model's angle match the legs so the hitboxes match on both sides. + GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ) +{ + // Get pitch from v_angle + float flPitch = GetOuter()->GetLocalAngles()[ PITCH ]; + + if ( flPitch > 180.0f ) + { + flPitch -= 360.0f; + } + flPitch = clamp( flPitch, -90, 90 ); + + QAngle absangles = GetOuter()->GetAbsAngles(); + absangles.x = 0.0f; + m_angRender = absangles; + + // See if we have a blender for pitch + GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", -flPitch ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : goal - +// maxrate - +// dt - +// current - +// Output : int +//----------------------------------------------------------------------------- +int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current ) +{ + int direction = TURN_NONE; + + float anglediff = goal - current; + float anglediffabs = fabs( anglediff ); + + anglediff = AngleNormalize( anglediff ); + + float scale = 1.0f; + if ( anglediffabs <= FADE_TURN_DEGREES ) + { + scale = anglediffabs / FADE_TURN_DEGREES; + // Always do at least a bit of the turn ( 1% ) + scale = clamp( scale, 0.01f, 1.0f ); + } + + float maxmove = maxrate * dt * scale; + + if ( fabs( anglediff ) < maxmove ) + { + current = goal; + } + else + { + if ( anglediff > 0 ) + { + current += maxmove; + direction = TURN_LEFT; + } + else + { + current -= maxmove; + direction = TURN_RIGHT; + } + } + + current = AngleNormalize( current ); + + return direction; +} + +void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void ) +{ + QAngle absangles = GetOuter()->GetAbsAngles(); + absangles.y = AngleNormalize( absangles.y ); + m_angRender = absangles; + + // See if we even have a blender for pitch + int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); + if ( upper_body_yaw < 0 ) + { + return; + } + + // Assume upper and lower bodies are aligned and that we're not turning + float flGoalTorsoYaw = 0.0f; + int turning = TURN_NONE; + float turnrate = 360.0f; + + Vector vel; + + GetOuterAbsVelocity( vel ); + + bool isMoving = ( vel.Length() > 1.0f ) ? true : false; + + if ( !isMoving ) + { + // Just stopped moving, try and clamp feet + if ( m_flLastTurnTime <= 0.0f ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; + // Snap feet to be perfectly aligned with torso/eyes + m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + m_flCurrentFeetYaw = m_flGoalFeetYaw; + m_nTurningInPlace = TURN_NONE; + } + + // If rotating in place, update stasis timer + if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; + } + + if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) + { + m_flLastTurnTime = gpGlobals->curtime; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); + + QAngle eyeAngles = GetOuter()->GetAnimEyeAngles(); + QAngle vAngle = GetOuter()->GetLocalAngles(); + + // See how far off current feetyaw is from true yaw + float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; + yawdelta = AngleNormalize( yawdelta ); + + bool rotated_too_far = false; + + float yawmagnitude = fabs( yawdelta ); + + // If too far, then need to turn in place + if ( yawmagnitude > 45 ) + { + rotated_too_far = true; + } + + // Standing still for a while, rotate feet around to face forward + // Or rotated too far + // FIXME: Play an in place turning animation + if ( rotated_too_far || + ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) ) + { + m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + m_flLastTurnTime = gpGlobals->curtime; + + /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw; + if ( yd > 0 ) + { + m_nTurningInPlace = TURN_RIGHT; + } + else if ( yd < 0 ) + { + m_nTurningInPlace = TURN_LEFT; + } + else + { + m_nTurningInPlace = TURN_NONE; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); + yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/ + + } + + // Snap upper body into position since the delta is already smoothed for the feet + flGoalTorsoYaw = yawdelta; + m_flCurrentTorsoYaw = flGoalTorsoYaw; + } + else + { + m_flLastTurnTime = 0.0f; + m_nTurningInPlace = TURN_NONE; + m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + flGoalTorsoYaw = 0.0f; + m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; + } + + + if ( turning == TURN_NONE ) + { + m_nTurningInPlace = turning; + } + + if ( m_nTurningInPlace != TURN_NONE ) + { + // If we're close to finishing the turn, then turn off the turning animation + if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION ) + { + m_nTurningInPlace = TURN_NONE; + } + } + + // Rotate entire body into position + absangles = GetOuter()->GetAbsAngles(); + absangles.y = m_flCurrentFeetYaw; + m_angRender = absangles; + + GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) ); + + /* + // FIXME: Adrian, what is this? + int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" ); + + if ( body_yaw >= 0 ) + { + GetOuter()->SetPoseParameter( body_yaw, 30 ); + } + */ + +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : activity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity ) +{ + // Not even standing still, sigh + if ( activity != ACT_IDLE ) + return activity; + + // Not turning + switch ( m_nTurningInPlace ) + { + default: + case TURN_NONE: + return activity; + /* + case TURN_RIGHT: + return ACT_TURNRIGHT45; + case TURN_LEFT: + return ACT_TURNLEFT45; + */ + case TURN_RIGHT: + case TURN_LEFT: + return mp_ik.GetBool() ? ACT_TURN : activity; + } + + Assert( 0 ); + return activity; +} + +const QAngle& CPlayerAnimState::GetRenderAngles() +{ + return m_angRender; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CPlayerAnimState::Teleport( Vector *pOldOrigin, QAngle *pOldAngles ) +{ + QAngle absangles = GetOuter()->GetAbsAngles(); + absangles.x = 0.0f; + m_angRender = absangles; + + m_flCurrentFeetYaw = m_flGoalFeetYaw = m_flLastYaw = m_angRender.y; + m_flLastTurnTime = 0.0f; + m_nTurningInPlace = TURN_NONE; +} + +void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel ) +{ +#if defined( CLIENT_DLL ) + GetOuter()->EstimateAbsVelocity( vel ); +#else + vel = GetOuter()->GetAbsVelocity(); +#endif +} +#endif // #if 0
\ No newline at end of file diff --git a/game/shared/portal/portal_player_shared.h b/game/shared/portal/portal_player_shared.h new file mode 100644 index 0000000..fd204b2 --- /dev/null +++ b/game/shared/portal/portal_player_shared.h @@ -0,0 +1,38 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef PORTAL_PLAYER_SHARED_H +#define PORTAL_PLAYER_SHARED_H +#pragma once + +#define PORTAL_PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f) +#include "studio.h" + + +enum +{ + PLAYER_SOUNDS_CITIZEN = 0, + PLAYER_SOUNDS_COMBINESOLDIER, + PLAYER_SOUNDS_METROPOLICE, + PLAYER_SOUNDS_MAX, +}; + +enum +{ + CONCEPT_CHELL_IDLE, + CONCEPT_CHELL_DEAD, +}; + +extern const char *g_pszChellConcepts[]; +int GetChellConceptIndexFromString( const char *pszConcept ); + +#if defined( CLIENT_DLL ) +#define CPortal_Player C_Portal_Player +#endif + + +#endif //PORTAL_PLAYER_SHARED_h diff --git a/game/shared/portal/portal_playeranimstate.cpp b/game/shared/portal/portal_playeranimstate.cpp new file mode 100644 index 0000000..9010ee3 --- /dev/null +++ b/game/shared/portal/portal_playeranimstate.cpp @@ -0,0 +1,310 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "tier0/vprof.h" +#include "animation.h" +#include "studio.h" +#include "apparent_velocity_helper.h" +#include "utldict.h" +#include "portal_playeranimstate.h" +#include "base_playeranimstate.h" + +#ifdef CLIENT_DLL +#include "c_portal_player.h" +#include "c_weapon_portalgun.h" +#else +#include "portal_player.h" +#include "weapon_portalgun.h" +#endif + +#define PORTAL_RUN_SPEED 320.0f +#define PORTAL_CROUCHWALK_SPEED 110.0f + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// Output : CMultiPlayerAnimState* +//----------------------------------------------------------------------------- +CPortalPlayerAnimState* CreatePortalPlayerAnimState( CPortal_Player *pPlayer ) +{ + // Setup the movement data. + MultiPlayerMovementData_t movementData; + movementData.m_flBodyYawRate = 720.0f; + movementData.m_flRunSpeed = PORTAL_RUN_SPEED; + movementData.m_flWalkSpeed = -1; + movementData.m_flSprintSpeed = -1.0f; + + // Create animation state for this player. + CPortalPlayerAnimState *pRet = new CPortalPlayerAnimState( pPlayer, movementData ); + + // Specific Portal player initialization. + pRet->InitPortal( pPlayer ); + + return pRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +CPortalPlayerAnimState::CPortalPlayerAnimState() +{ + m_pPortalPlayer = NULL; + + // Don't initialize Portal specific variables here. Init them in InitPortal() +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// &movementData - +//----------------------------------------------------------------------------- +CPortalPlayerAnimState::CPortalPlayerAnimState( CBasePlayer *pPlayer, MultiPlayerMovementData_t &movementData ) +: CMultiPlayerAnimState( pPlayer, movementData ) +{ + m_pPortalPlayer = NULL; + + // Don't initialize Portal specific variables here. Init them in InitPortal() +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +CPortalPlayerAnimState::~CPortalPlayerAnimState() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize Portal specific animation state. +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CPortalPlayerAnimState::InitPortal( CPortal_Player *pPlayer ) +{ + m_pPortalPlayer = pPlayer; + m_bInAirWalk = false; + m_flHoldDeployedPoseUntilTime = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPortalPlayerAnimState::ClearAnimationState( void ) +{ + m_bInAirWalk = false; + + BaseClass::ClearAnimationState(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : actDesired - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CPortalPlayerAnimState::TranslateActivity( Activity actDesired ) +{ + Activity translateActivity = BaseClass::TranslateActivity( actDesired ); + + if ( GetPortalPlayer()->GetActiveWeapon() ) + { + translateActivity = GetPortalPlayer()->GetActiveWeapon()->ActivityOverride( translateActivity, NULL ); + } + + return translateActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : event - +//----------------------------------------------------------------------------- +void CPortalPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + Activity iWeaponActivity = ACT_INVALID; + + switch( event ) + { + case PLAYERANIMEVENT_ATTACK_PRIMARY: + case PLAYERANIMEVENT_ATTACK_SECONDARY: + { + CPortal_Player *pPlayer = GetPortalPlayer(); + if ( !pPlayer ) + return; + + CWeaponPortalBase *pWpn = pPlayer->GetActivePortalWeapon(); + + if ( pWpn ) + { + // Weapon primary fire. + if ( GetBasePlayer()->GetFlags() & FL_DUCKING ) + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_CROUCH_PRIMARYFIRE ); + } + else + { + RestartGesture( GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_STAND_PRIMARYFIRE ); + } + + iWeaponActivity = ACT_VM_PRIMARYATTACK; + } + else // unarmed player + { + + } + + break; + } + + default: + { + BaseClass::DoAnimationEvent( event, nData ); + break; + } + } + +#ifdef CLIENT_DLL + // Make the weapon play the animation as well + if ( iWeaponActivity != ACT_INVALID ) + { + CBaseCombatWeapon *pWeapon = GetPortalPlayer()->GetActiveWeapon(); + if ( pWeapon ) + { + pWeapon->SendWeaponAnim( iWeaponActivity ); + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPortalPlayerAnimState::Teleport( const Vector *pNewOrigin, const QAngle *pNewAngles, CPortal_Player* pPlayer ) +{ + QAngle absangles = pPlayer->GetAbsAngles(); + m_angRender = absangles; + m_angRender.x = m_angRender.z = 0.0f; + if ( pPlayer ) + { + // Snap the yaw pose parameter lerping variables to face new angles. + m_flCurrentFeetYaw = m_flGoalFeetYaw = m_flEyeYaw = pPlayer->EyeAngles()[YAW]; + } +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *idealActivity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPortalPlayerAnimState::HandleMoving( Activity &idealActivity ) +{ + float flSpeed = GetOuterXYSpeed(); + + // If we move, cancel the deployed anim hold + if ( flSpeed > MOVING_MINIMUM_SPEED ) + { + m_flHoldDeployedPoseUntilTime = 0.0; + idealActivity = ACT_MP_RUN; + } + + else if ( m_flHoldDeployedPoseUntilTime > gpGlobals->curtime ) + { + // Unless we move, hold the deployed pose for a number of seconds after being deployed + idealActivity = ACT_MP_DEPLOYED_IDLE; + } + else + { + return BaseClass::HandleMoving( idealActivity ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *idealActivity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPortalPlayerAnimState::HandleDucking( Activity &idealActivity ) +{ + if ( GetBasePlayer()->m_Local.m_bDucking || GetBasePlayer()->m_Local.m_bDucked ) + { + if ( GetOuterXYSpeed() < MOVING_MINIMUM_SPEED ) + { + idealActivity = ACT_MP_CROUCH_IDLE; + } + else + { + idealActivity = ACT_MP_CROUCHWALK; + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +bool CPortalPlayerAnimState::HandleJumping( Activity &idealActivity ) +{ + Vector vecVelocity; + GetOuterAbsVelocity( vecVelocity ); + + if ( ( vecVelocity.z > 300.0f || m_bInAirWalk ) ) + { + // Check to see if we were in an airwalk and now we are basically on the ground. + if ( GetBasePlayer()->GetFlags() & FL_ONGROUND ) + { + m_bInAirWalk = false; + RestartMainSequence(); + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND ); + } + else + { + // In an air walk. + idealActivity = ACT_MP_AIRWALK; + m_bInAirWalk = true; + } + } + // Jumping. + else + { + if ( m_bJumping ) + { + if ( m_bFirstJumpFrame ) + { + m_bFirstJumpFrame = false; + RestartMainSequence(); // Reset the animation. + } + + // Don't check if he's on the ground for a sec.. sometimes the client still has the + // on-ground flag set right when the message comes in. + else if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f ) + { + if ( GetBasePlayer()->GetFlags() & FL_ONGROUND ) + { + m_bJumping = false; + RestartMainSequence(); + RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND ); + } + } + + // if we're still jumping + if ( m_bJumping ) + { + idealActivity = ACT_MP_JUMP_START; + } + } + } + + if ( m_bJumping || m_bInAirWalk ) + return true; + + return false; +}
\ No newline at end of file diff --git a/game/shared/portal/portal_playeranimstate.h b/game/shared/portal/portal_playeranimstate.h new file mode 100644 index 0000000..1ebbfbc --- /dev/null +++ b/game/shared/portal/portal_playeranimstate.h @@ -0,0 +1,85 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PORTAL_PLAYERANIMSTATE_H +#define PORTAL_PLAYERANIMSTATE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "convar.h" +#include "multiplayer_animstate.h" + + +#if defined( CLIENT_DLL ) + class C_Portal_Player; + #define CPortal_Player C_Portal_Player +#else + class CPortal_Player; +#endif + +//enum PlayerAnimEvent_t +//{ +// PLAYERANIMEVENT_FIRE_GUN=0, +// PLAYERANIMEVENT_THROW_GRENADE, +// PLAYERANIMEVENT_ROLL_GRENADE, +// PLAYERANIMEVENT_JUMP, +// PLAYERANIMEVENT_RELOAD, +// PLAYERANIMEVENT_SECONDARY_ATTACK, +// +// PLAYERANIMEVENT_HS_NONE, +// PLAYERANIMEVENT_CANCEL_GESTURES, // cancel current gesture +// +// PLAYERANIMEVENT_COUNT +//}; + +// ------------------------------------------------------------------------------------------------ // +// CPlayerAnimState declaration. +// ------------------------------------------------------------------------------------------------ // +class CPortalPlayerAnimState : public CMultiPlayerAnimState +{ +public: + + DECLARE_CLASS( CPortalPlayerAnimState, CMultiPlayerAnimState ); + + CPortalPlayerAnimState(); + CPortalPlayerAnimState( CBasePlayer *pPlayer, MultiPlayerMovementData_t &movementData ); + ~CPortalPlayerAnimState(); + + void InitPortal( CPortal_Player *pPlayer ); + CPortal_Player *GetPortalPlayer( void ) { return m_pPortalPlayer; } + + virtual void ClearAnimationState(); + + virtual Activity TranslateActivity( Activity actDesired ); + + void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 ); + + void Teleport( const Vector *pNewOrigin, const QAngle *pNewAngles, CPortal_Player* pPlayer ); + + bool HandleMoving( Activity &idealActivity ); + bool HandleJumping( Activity &idealActivity ); + bool HandleDucking( Activity &idealActivity ); + +private: + + CPortal_Player *m_pPortalPlayer; + bool m_bInAirWalk; + + float m_flHoldDeployedPoseUntilTime; +}; + + +CPortalPlayerAnimState* CreatePortalPlayerAnimState( CPortal_Player *pPlayer ); + + +// If this is set, then the game code needs to make sure to send player animation events +// to the local player if he's the one being watched. +extern ConVar cl_showanimstate; + + +#endif // PORTAL_PLAYERANIMSTATE_H diff --git a/game/shared/portal/portal_shareddefs.cpp b/game/shared/portal/portal_shareddefs.cpp new file mode 100644 index 0000000..ebb2260 --- /dev/null +++ b/game/shared/portal/portal_shareddefs.cpp @@ -0,0 +1,13 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" +#include "portal_shareddefs.h" + +char *g_ppszPortalPassThroughMaterials[] = +{ + "lights/light_orange001", + NULL, +}; diff --git a/game/shared/portal/portal_shareddefs.h b/game/shared/portal/portal_shareddefs.h new file mode 100644 index 0000000..3e15a11 --- /dev/null +++ b/game/shared/portal/portal_shareddefs.h @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PORTAL_SHAREDDEFS_H +#define PORTAL_SHAREDDEFS_H +#ifdef _WIN32 +#pragma once +#endif + + +#define PORTAL_HALF_WIDTH 32.0f +#define PORTAL_HALF_HEIGHT 54.0f +#define PORTAL_HALF_DEPTH 2.0f +#define PORTAL_BUMP_FORGIVENESS 2.0f + +#define PORTAL_ANALOG_SUCCESS_NO_BUMP 1.0f +#define PORTAL_ANALOG_SUCCESS_BUMPED 0.3f +#define PORTAL_ANALOG_SUCCESS_CANT_FIT 0.1f +#define PORTAL_ANALOG_SUCCESS_CLEANSER 0.028f +#define PORTAL_ANALOG_SUCCESS_OVERLAP_LINKED 0.027f +#define PORTAL_ANALOG_SUCCESS_NEAR 0.0265f +#define PORTAL_ANALOG_SUCCESS_INVALID_VOLUME 0.026f +#define PORTAL_ANALOG_SUCCESS_INVALID_SURFACE 0.025f +#define PORTAL_ANALOG_SUCCESS_PASSTHROUGH_SURFACE 0.0f + +#define MIN_FLING_SPEED 300 + +#define PORTAL_HIDE_PLAYER_RAGDOLL 1 + +enum PortalFizzleType_t +{ + PORTAL_FIZZLE_SUCCESS = 0, // Placed fine (no fizzle) + PORTAL_FIZZLE_CANT_FIT, + PORTAL_FIZZLE_OVERLAPPED_LINKED, + PORTAL_FIZZLE_BAD_VOLUME, + PORTAL_FIZZLE_BAD_SURFACE, + PORTAL_FIZZLE_KILLED, + PORTAL_FIZZLE_CLEANSER, + PORTAL_FIZZLE_CLOSE, + PORTAL_FIZZLE_NEAR_BLUE, + PORTAL_FIZZLE_NEAR_RED, + PORTAL_FIZZLE_NONE, + + NUM_PORTAL_FIZZLE_TYPES +}; + + +enum PortalPlacedByType +{ + PORTAL_PLACED_BY_FIXED = 0, + PORTAL_PLACED_BY_PEDESTAL, + PORTAL_PLACED_BY_PLAYER +}; + +enum PortalLevelStatType +{ + PORTAL_LEVEL_STAT_NUM_PORTALS = 0, + PORTAL_LEVEL_STAT_NUM_STEPS, + PORTAL_LEVEL_STAT_NUM_SECONDS, + + PORTAL_LEVEL_STAT_TOTAL +}; + +enum PortalChallengeType +{ + PORTAL_CHALLENGE_NONE = 0, + PORTAL_CHALLENGE_PORTALS, + PORTAL_CHALLENGE_STEPS, + PORTAL_CHALLENGE_TIME, + + PORTAL_CHALLENGE_TOTAL +}; + +extern char *g_ppszPortalPassThroughMaterials[]; + +#endif // PORTAL_SHAREDDEFS_H diff --git a/game/shared/portal/portal_usermessages.cpp b/game/shared/portal/portal_usermessages.cpp new file mode 100644 index 0000000..d30b7e0 --- /dev/null +++ b/game/shared/portal/portal_usermessages.cpp @@ -0,0 +1,64 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "usermessages.h" +#include "shake.h" +#include "voice_gamemgr.h" + +// NVNT include to register in haptic user messages +#include "haptics/haptic_msgs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void RegisterUserMessages() +{ + //copy/paste from hl2 + usermessages->Register( "Geiger", 1 ); + usermessages->Register( "Train", 1 ); + usermessages->Register( "HudText", -1 ); + usermessages->Register( "SayText", -1 ); + usermessages->Register( "SayText2", -1 ); + usermessages->Register( "TextMsg", -1 ); + usermessages->Register( "HudMsg", -1 ); + usermessages->Register( "ResetHUD", 1); // called every respawn + usermessages->Register( "GameTitle", 0 ); + usermessages->Register( "ItemPickup", -1 ); + usermessages->Register( "ShowMenu", -1 ); + usermessages->Register( "Shake", 13 ); + usermessages->Register( "Fade", 10 ); + usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu + usermessages->Register( "Rumble", 3 ); // Send a rumble to a controller + usermessages->Register( "Battery", 2 ); + usermessages->Register( "Damage", 18 ); // BUG: floats are sent for coords, no variable bitfields in hud & fixed size Msg + usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 ); + usermessages->Register( "RequestState", 0 ); + usermessages->Register( "CloseCaption", -1 ); // Show a caption (by string id number)(duration in 10th of a second) + usermessages->Register( "HintText", -1 ); // Displays hint text display + usermessages->Register( "KeyHintText", -1 ); // Displays hint text display + usermessages->Register( "SquadMemberDied", 0 ); + usermessages->Register( "AmmoDenied", 2 ); + usermessages->Register( "CreditsMsg", 1 ); + usermessages->Register( "CreditsPortalMsg", 1 ); + usermessages->Register( "LogoTimeMsg", 4 ); + usermessages->Register( "AchievementEvent", -1 ); + + + //new stuff for portal + usermessages->Register( "EntityPortalled", sizeof( long ) + sizeof( long ) + sizeof( Vector ) + sizeof( QAngle ) ); //something got teleported through a portal + usermessages->Register( "KillCam", -1 ); + + // Voting + usermessages->Register( "CallVoteFailed", 1 ); + usermessages->Register( "VoteStart", -1 ); + usermessages->Register( "VotePass", -1 ); + usermessages->Register( "VoteFailed", 2 ); + usermessages->Register( "VoteSetup", -1 ); // Initiates client-side voting UI + + // NVNT register haptic user messages + RegisterHapticMessages(); +}
\ No newline at end of file diff --git a/game/shared/portal/portal_util_shared.cpp b/game/shared/portal/portal_util_shared.cpp new file mode 100644 index 0000000..7e61178 --- /dev/null +++ b/game/shared/portal/portal_util_shared.cpp @@ -0,0 +1,1774 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "portal_util_shared.h" +#include "prop_portal_shared.h" +#include "portal_shareddefs.h" +#include "portal_collideable_enumerator.h" +#include "beam_shared.h" +#include "collisionutils.h" +#include "util_shared.h" +#ifndef CLIENT_DLL + #include "util.h" + #include "ndebugoverlay.h" + #include "env_debughistory.h" +#else + #include "c_portal_player.h" +#endif +#include "PortalSimulation.h" + +bool g_bAllowForcePortalTrace = false; +bool g_bForcePortalTrace = false; +bool g_bBulletPortalTrace = false; + +ConVar sv_portal_trace_vs_world ("sv_portal_trace_vs_world", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment world geometry" ); +ConVar sv_portal_trace_vs_displacements ("sv_portal_trace_vs_displacements", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment displacement geometry" ); +ConVar sv_portal_trace_vs_holywall ("sv_portal_trace_vs_holywall", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment carved wall" ); +ConVar sv_portal_trace_vs_staticprops ("sv_portal_trace_vs_staticprops", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment static prop geometry" ); +ConVar sv_use_find_closest_passable_space ("sv_use_find_closest_passable_space", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Enables heavy-handed player teleporting stuck fix code." ); +ConVar sv_use_transformed_collideables("sv_use_transformed_collideables", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Disables traces against remote portal moving entities using transforms to bring them into local space." ); +class CTransformedCollideable : public ICollideable //wraps an existing collideable, but transforms everything that pertains to world space by another transform +{ +public: + VMatrix m_matTransform; //the transformation we apply to the wrapped collideable + VMatrix m_matInvTransform; //cached inverse of m_matTransform + + ICollideable *m_pWrappedCollideable; //the collideable we're transforming without it knowing + + struct CTC_ReferenceVars_t + { + Vector m_vCollisionOrigin; + QAngle m_qCollisionAngles; + matrix3x4_t m_matCollisionToWorldTransform; + matrix3x4_t m_matRootParentToWorldTransform; + }; + + mutable CTC_ReferenceVars_t m_ReferencedVars; //when returning a const reference, it needs to point to something, so here we go + + //abstract functions which require no transforms, just pass them along to the wrapped collideable + virtual IHandleEntity *GetEntityHandle() { return m_pWrappedCollideable->GetEntityHandle(); } + virtual const Vector& OBBMinsPreScaled() const { return m_pWrappedCollideable->OBBMinsPreScaled(); } + virtual const Vector& OBBMaxsPreScaled() const { return m_pWrappedCollideable->OBBMaxsPreScaled(); } + virtual const Vector& OBBMins() const { return m_pWrappedCollideable->OBBMins(); } + virtual const Vector& OBBMaxs() const { return m_pWrappedCollideable->OBBMaxs(); } + virtual int GetCollisionModelIndex() { return m_pWrappedCollideable->GetCollisionModelIndex(); } + virtual const model_t* GetCollisionModel() { return m_pWrappedCollideable->GetCollisionModel(); } + virtual SolidType_t GetSolid() const { return m_pWrappedCollideable->GetSolid(); } + virtual int GetSolidFlags() const { return m_pWrappedCollideable->GetSolidFlags(); } + virtual IClientUnknown* GetIClientUnknown() { return m_pWrappedCollideable->GetIClientUnknown(); } + virtual int GetCollisionGroup() const { return m_pWrappedCollideable->GetCollisionGroup(); } + virtual bool ShouldTouchTrigger( int triggerSolidFlags ) const { return m_pWrappedCollideable->ShouldTouchTrigger(triggerSolidFlags); } + + //slightly trickier functions + virtual void WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const; + virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + virtual const Vector& GetCollisionOrigin() const; + virtual const QAngle& GetCollisionAngles() const; + virtual const matrix3x4_t& CollisionToWorldTransform() const; + virtual void WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ); + virtual const matrix3x4_t *GetRootParentToWorldTransform() const; +}; + +void CTransformedCollideable::WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const +{ + m_pWrappedCollideable->WorldSpaceTriggerBounds( pVecWorldMins, pVecWorldMaxs ); + + if( pVecWorldMins ) + *pVecWorldMins = m_matTransform * (*pVecWorldMins); + + if( pVecWorldMaxs ) + *pVecWorldMaxs = m_matTransform * (*pVecWorldMaxs); +} + +bool CTransformedCollideable::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + //TODO: Transform the ray by inverse matTransform and transform the trace results by matTransform? AABB Errors arise by transforming the ray. + return m_pWrappedCollideable->TestCollision( ray, fContentsMask, tr ); +} + +bool CTransformedCollideable::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + //TODO: Transform the ray by inverse matTransform and transform the trace results by matTransform? AABB Errors arise by transforming the ray. + return m_pWrappedCollideable->TestHitboxes( ray, fContentsMask, tr ); +} + +const Vector& CTransformedCollideable::GetCollisionOrigin() const +{ + m_ReferencedVars.m_vCollisionOrigin = m_matTransform * m_pWrappedCollideable->GetCollisionOrigin(); + return m_ReferencedVars.m_vCollisionOrigin; +} + +const QAngle& CTransformedCollideable::GetCollisionAngles() const +{ + m_ReferencedVars.m_qCollisionAngles = TransformAnglesToWorldSpace( m_pWrappedCollideable->GetCollisionAngles(), m_matTransform.As3x4() ); + return m_ReferencedVars.m_qCollisionAngles; +} + +const matrix3x4_t& CTransformedCollideable::CollisionToWorldTransform() const +{ + //1-2 order correct? + ConcatTransforms( m_matTransform.As3x4(), m_pWrappedCollideable->CollisionToWorldTransform(), m_ReferencedVars.m_matCollisionToWorldTransform ); + return m_ReferencedVars.m_matCollisionToWorldTransform; +} + +void CTransformedCollideable::WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ) +{ + if( (pVecMins == NULL) && (pVecMaxs == NULL) ) + return; + + Vector vMins, vMaxs; + m_pWrappedCollideable->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); + + TransformAABB( m_matTransform.As3x4(), vMins, vMaxs, vMins, vMaxs ); + + if( pVecMins ) + *pVecMins = vMins; + if( pVecMaxs ) + *pVecMaxs = vMaxs; +} + +const matrix3x4_t* CTransformedCollideable::GetRootParentToWorldTransform() const +{ + const matrix3x4_t *pWrappedVersion = m_pWrappedCollideable->GetRootParentToWorldTransform(); + if( pWrappedVersion == NULL ) + return NULL; + + ConcatTransforms( m_matTransform.As3x4(), *pWrappedVersion, m_ReferencedVars.m_matRootParentToWorldTransform ); + return &m_ReferencedVars.m_matRootParentToWorldTransform; +} + +Color UTIL_Portal_Color( int iPortal ) +{ + switch ( iPortal ) + { + case 0: + // GRAVITY BEAM + return Color( 242, 202, 167, 255 ); + + case 1: + // PORTAL 1 + return Color( 64, 160, 255, 255 ); + + case 2: + // PORTAL 2 + return Color( 255, 160, 32, 255 ); + } + + return Color( 255, 255, 255, 255 ); +} + +void UTIL_Portal_Trace_Filter( CTraceFilterSimpleClassnameList *traceFilterPortalShot ) +{ + traceFilterPortalShot->AddClassnameToIgnore( "prop_physics" ); + traceFilterPortalShot->AddClassnameToIgnore( "func_physbox" ); + traceFilterPortalShot->AddClassnameToIgnore( "npc_portal_turret_floor" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_energy_ball" ); + traceFilterPortalShot->AddClassnameToIgnore( "npc_security_camera" ); + traceFilterPortalShot->AddClassnameToIgnore( "player" ); + traceFilterPortalShot->AddClassnameToIgnore( "simple_physics_prop" ); + traceFilterPortalShot->AddClassnameToIgnore( "simple_physics_brush" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_ragdoll" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_glados_core" ); + traceFilterPortalShot->AddClassnameToIgnore( "updateitem2" ); +} + + +CProp_Portal* UTIL_Portal_FirstAlongRay( const Ray_t &ray, float &fMustBeCloserThan ) +{ + CProp_Portal *pIntersectedPortal = NULL; + + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount != 0 ) + { + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->IsActivedAndLinked() ) + { + float fIntersection = UTIL_IntersectRayWithPortal( ray, pTempPortal ); + if( fIntersection >= 0.0f && fIntersection < fMustBeCloserThan ) + { + //within range, now check directionality + if( pTempPortal->m_plane_Origin.normal.Dot( ray.m_Delta ) < 0.0f ) + { + //qualifies for consideration, now it just has to compete for closest + pIntersectedPortal = pTempPortal; + fMustBeCloserThan = fIntersection; + } + } + } + } + } + + return pIntersectedPortal; +} + + +bool UTIL_Portal_TraceRay_Bullets( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + if( !pPortal || !pPortal->IsActivedAndLinked() ) + { + //not in a portal environment, use regular traces + enginetrace->TraceRay( ray, fMask, pTraceFilter, pTrace ); + return false; + } + + trace_t trReal; + + enginetrace->TraceRay( ray, fMask, pTraceFilter, &trReal ); + + Vector vRayNormal = ray.m_Delta; + VectorNormalize( vRayNormal ); + + Vector vPortalForward; + pPortal->GetVectors( &vPortalForward, 0, 0 ); + + // If the ray isn't going into the front of the portal, just use the real trace + if ( vPortalForward.Dot( vRayNormal ) > 0.0f ) + { + *pTrace = trReal; + return false; + } + + // If the real trace collides before the portal plane, just use the real trace + float fPortalFraction = UTIL_IntersectRayWithPortal( ray, pPortal ); + + if ( fPortalFraction == -1.0f || trReal.fraction + 0.0001f < fPortalFraction ) + { + // Didn't intersect or the real trace intersected closer + *pTrace = trReal; + return false; + } + + Ray_t rayPostPortal; + rayPostPortal = ray; + rayPostPortal.m_Start = ray.m_Start + ray.m_Delta * fPortalFraction; + rayPostPortal.m_Delta = ray.m_Delta * ( 1.0f - fPortalFraction ); + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + + Ray_t rayTransformed; + UTIL_Portal_RayTransform( matThisToLinked, rayPostPortal, rayTransformed ); + + // After a bullet traces through a portal it can hit the player that fired it + CTraceFilterSimple *pSimpleFilter = dynamic_cast<CTraceFilterSimple*>(pTraceFilter); + const IHandleEntity *pPassEntity = NULL; + if ( pSimpleFilter ) + { + pPassEntity = pSimpleFilter->GetPassEntity(); + pSimpleFilter->SetPassEntity( 0 ); + } + + trace_t trPostPortal; + enginetrace->TraceRay( rayTransformed, fMask, pTraceFilter, &trPostPortal ); + + if ( pSimpleFilter ) + { + pSimpleFilter->SetPassEntity( pPassEntity ); + } + + //trPostPortal.startpos = ray.m_Start; + UTIL_Portal_PointTransform( matThisToLinked, ray.m_Start, trPostPortal.startpos ); + trPostPortal.fraction = trPostPortal.fraction * ( 1.0f - fPortalFraction ) + fPortalFraction; + + *pTrace = trPostPortal; + + return true; +} + +CProp_Portal* UTIL_Portal_TraceRay_Beam( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, float *pfFraction ) +{ + // Do a regular trace + trace_t tr; + UTIL_TraceLine( ray.m_Start, ray.m_Start + ray.m_Delta, fMask, pTraceFilter, &tr ); + float fMustBeCloserThan = tr.fraction + 0.0001f; + + CProp_Portal *pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + + *pfFraction = fMustBeCloserThan; //will be real trace distance if it didn't hit a portal + return pIntersectedPortal; +} + + +bool UTIL_Portal_Trace_Beam( const CBeam *pBeam, Vector &vecStart, Vector &vecEnd, Vector &vecIntersectionStart, Vector &vecIntersectionEnd, ITraceFilter *pTraceFilter ) +{ + vecStart = pBeam->GetAbsStartPos(); + vecEnd = pBeam->GetAbsEndPos(); + + // Trace to see if we've intersected a portal + float fEndFraction; + Ray_t rayBeam; + + bool bIsReversed = ( pBeam->GetBeamFlags() & FBEAM_REVERSED ) != 0x0; + + if ( !bIsReversed ) + rayBeam.Init( vecStart, vecEnd ); + else + rayBeam.Init( vecEnd, vecStart ); + + CProp_Portal *pPortal = UTIL_Portal_TraceRay_Beam( rayBeam, MASK_SHOT, pTraceFilter, &fEndFraction ); + + // If we intersected a portal we need to modify the start and end points to match the actual trace through portal drawing extents + if ( !pPortal ) + return false; + + // Modify the start and end points to match the actual trace through portal drawing extents + vecStart = rayBeam.m_Start; + + Vector vecIntersection = rayBeam.m_Start + rayBeam.m_Delta * fEndFraction; + + int iNumLoops = 0; + + // Loop through the portals (at most 16 times) + while ( pPortal && iNumLoops < 16 ) + { + // Get the point that we hit a portal or wall + vecIntersectionStart = vecIntersection; + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + + // Get the transformed positions of the sub beam in the other portal's space + UTIL_Portal_PointTransform( matThisToLinked, vecIntersectionStart, vecIntersectionEnd ); + UTIL_Portal_PointTransform( matThisToLinked, rayBeam.m_Start + rayBeam.m_Delta, vecEnd ); + + CTraceFilterSkipClassname traceFilter( pPortal->m_hLinkedPortal, "prop_energy_ball", COLLISION_GROUP_NONE ); + + rayBeam.Init( vecIntersectionEnd, vecEnd ); + pPortal = UTIL_Portal_TraceRay_Beam( rayBeam, MASK_SHOT, &traceFilter, &fEndFraction ); + vecIntersection = rayBeam.m_Start + rayBeam.m_Delta * fEndFraction; + + ++iNumLoops; + } + + vecEnd = vecIntersection; + + return true; +} + + +void UTIL_Portal_TraceRay_With( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + //check to see if the player is theoretically in a portal environment + if( !pPortal || !pPortal->m_PortalSimulator.IsReadyToSimulate() ) + { + //not in a portal environment, use regular traces + enginetrace->TraceRay( ray, fMask, pTraceFilter, pTrace ); + } + else + { + + trace_t RealTrace; + enginetrace->TraceRay( ray, fMask, pTraceFilter, &RealTrace ); + + trace_t PortalTrace; + UTIL_Portal_TraceRay( pPortal, ray, fMask, pTraceFilter, &PortalTrace, bTraceHolyWall ); + + if( !g_bForcePortalTrace && !RealTrace.startsolid && PortalTrace.fraction <= RealTrace.fraction ) + { + *pTrace = RealTrace; + return; + } + + if ( g_bAllowForcePortalTrace ) + { + g_bForcePortalTrace = true; + } + + *pTrace = PortalTrace; + + // If this ray has a delta, make sure its towards the portal before we try to trace across portals + Vector vDirection = ray.m_Delta; + VectorNormalize( vDirection ); + Vector vPortalForward; + pPortal->GetVectors( &vPortalForward, 0, 0 ); + + float flDot = -1.0f; + if ( ray.m_IsSwept ) + { + flDot = vDirection.Dot( vPortalForward ); + } + + // TODO: Translate extents of rays properly, tracing extruded box rays across portals causes collision bugs + // Until this is fixed, we'll only test true rays across portals + if ( flDot < 0.0f && /*PortalTrace.fraction == 1.0f &&*/ ray.m_IsRay) + { + // Check if we're hitting stuff on the other side of the portal + trace_t PortalLinkedTrace; + UTIL_PortalLinked_TraceRay( pPortal, ray, fMask, pTraceFilter, &PortalLinkedTrace, bTraceHolyWall ); + + if ( PortalLinkedTrace.fraction < pTrace->fraction ) + { + // Only collide with the cross-portal objects if this trace crossed a portal + if ( UTIL_DidTraceTouchPortals( ray, PortalLinkedTrace ) ) + { + *pTrace = PortalLinkedTrace; + } + } + } + + if( pTrace->fraction < 1.0f ) + { + pTrace->contents = RealTrace.contents; + pTrace->surface = RealTrace.surface; + } + + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Tests if a ray touches the surface of any portals +// Input : ray - the ray to be tested against portal surfaces +// trace - a filled-in trace corresponding to the parameter ray +// Output : bool - false if the 'ray' parameter failed to hit any portal surface +// pOutLocal - the portal touched (if any) +// pOutRemote - the portal linked to the portal touched +//----------------------------------------------------------------------------- +bool UTIL_DidTraceTouchPortals( const Ray_t& ray, const trace_t& trace, CProp_Portal** pOutLocal, CProp_Portal** pOutRemote ) +{ + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + { + if( pOutLocal ) + *pOutLocal = NULL; + + if( pOutRemote ) + *pOutRemote = NULL; + + return false; + } + + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + CProp_Portal *pIntersectedPortal = NULL; + + if( ray.m_IsSwept ) + { + float fMustBeCloserThan = trace.fraction + 0.0001f; + + pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + } + + if( (pIntersectedPortal == NULL) && !ray.m_IsRay ) + { + //haven't hit anything yet, try again with box tests + + Vector ptRayEndPoint = trace.endpos - ray.m_StartOffset; // The trace added the start offset to the end position, so remove it for the box test + CProp_Portal **pBoxIntersectsPortals = (CProp_Portal **)stackalloc( sizeof(CProp_Portal *) * iPortalCount ); + int iBoxIntersectsPortalsCount = 0; + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( (pTempPortal->m_bActivated) && + (pTempPortal->m_hLinkedPortal.Get() != NULL) ) + { + if( UTIL_IsBoxIntersectingPortal( ptRayEndPoint, ray.m_Extents, pTempPortal, 0.00f ) ) + { + pBoxIntersectsPortals[iBoxIntersectsPortalsCount] = pTempPortal; + ++iBoxIntersectsPortalsCount; + } + } + } + + if( iBoxIntersectsPortalsCount > 0 ) + { + pIntersectedPortal = pBoxIntersectsPortals[0]; + + if( iBoxIntersectsPortalsCount > 1 ) + { + //hit more than one, use the closest + float fDistToBeat = (ptRayEndPoint - pIntersectedPortal->GetAbsOrigin()).LengthSqr(); + + for( int i = 1; i != iBoxIntersectsPortalsCount; ++i ) + { + float fDist = (ptRayEndPoint - pBoxIntersectsPortals[i]->GetAbsOrigin()).LengthSqr(); + if( fDist < fDistToBeat ) + { + pIntersectedPortal = pBoxIntersectsPortals[i]; + fDistToBeat = fDist; + } + } + } + } + } + + if( pIntersectedPortal == NULL ) + { + if( pOutLocal ) + *pOutLocal = NULL; + + if( pOutRemote ) + *pOutRemote = NULL; + + return false; + } + else + { + // Record the touched portals and return + if( pOutLocal ) + *pOutLocal = pIntersectedPortal; + + if( pOutRemote ) + *pOutRemote = pIntersectedPortal->m_hLinkedPortal.Get(); + + return true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Redirects the trace to either a trace that uses portal environments, or if a +// global boolean is set, trace with a special bullets trace. +// NOTE: UTIL_Portal_TraceRay_With will use the default world trace if it gets a NULL portal pointer +// Input : &ray - the ray to use to trace +// fMask - collision mask +// *pTraceFilter - customizable filter on the trace +// *pTrace - trace struct to fill with output info +//----------------------------------------------------------------------------- +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + float fMustBeCloserThan = 2.0f; + CProp_Portal *pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + + if ( g_bBulletPortalTrace ) + { + if ( UTIL_Portal_TraceRay_Bullets( pIntersectedPortal, ray, fMask, pTraceFilter, pTrace, bTraceHolyWall ) ) + return pIntersectedPortal; + + // Bullet didn't actually go through portal + return NULL; + + } + else + { + UTIL_Portal_TraceRay_With( pIntersectedPortal, ray, fMask, pTraceFilter, pTrace, bTraceHolyWall ); + return pIntersectedPortal; + } +} + +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + return UTIL_Portal_TraceRay( ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + + +//----------------------------------------------------------------------------- +// Purpose: This version of traceray only traces against the portal environment of the specified portal. +// Input : *pPortal - the portal whose physics we will trace against +// &ray - the ray to trace with +// fMask - collision mask +// *pTraceFilter - customizable filter to determine what it hits +// *pTrace - the trace struct to fill in with results +// bTraceHolyWall - if this trace is to test against the 'holy wall' geometry +//----------------------------------------------------------------------------- +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); +#endif + Assert( pPortal->m_PortalSimulator.IsReadyToSimulate() ); //a trace shouldn't make it down this far if the portal is incapable of changing the results of the trace + + CTraceFilterHitAll traceFilterHitAll; + if ( !pTraceFilter ) + { + pTraceFilter = &traceFilterHitAll; + } + + pTrace->fraction = 2.0f; + pTrace->startsolid = true; + pTrace->allsolid = true; + + trace_t TempTrace; + int counter; + + const CPortalSimulator &portalSimulator = pPortal->m_PortalSimulator; + CPortalSimulator *pLinkedPortalSimulator = portalSimulator.GetLinkedPortalSimulator(); + + //bool bTraceDisplacements = sv_portal_trace_vs_displacements.GetBool(); + bool bTraceStaticProps = sv_portal_trace_vs_staticprops.GetBool(); + if( sv_portal_trace_vs_holywall.GetBool() == false ) + bTraceHolyWall = false; + + bool bTraceTransformedGeometry = ( (pLinkedPortalSimulator != NULL) && bTraceHolyWall && portalSimulator.RayIsInPortalHole( ray ) ); + + bool bCopyBackBrushTraceData = false; + + + + // Traces vs world + if( pTraceFilter->GetTraceType() != TRACE_ENTITIES_ONLY ) + { + //trace_t RealTrace; + //enginetrace->TraceRay( ray, fMask, pTraceFilter, &RealTrace ); + if( portalSimulator.m_DataAccess.Simulation.Static.World.Brushes.pCollideable && sv_portal_trace_vs_world.GetBool() ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, pTrace ); + bCopyBackBrushTraceData = true; + } + + if( bTraceHolyWall ) + { + if( portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &TempTrace ); + + if( (TempTrace.startsolid == false) && (TempTrace.fraction < pTrace->fraction) ) //never allow something to be stuck in the tube, it's more of a last-resort guide than a real collideable + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + + if( portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + + //if( portalSimulator.m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable && sv_portal_trace_vs_world.GetBool() ) + if( bTraceTransformedGeometry && pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) + { + physcollision->TraceBox( ray, pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + } + + if( bCopyBackBrushTraceData ) + { + pTrace->surface = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->contents = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->m_pEnt = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + + bCopyBackBrushTraceData = false; + } + } + + // Traces vs entities + if( pTraceFilter->GetTraceType() != TRACE_WORLD_ONLY ) + { + bool bFilterStaticProps = (pTraceFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS); + + //solid entities + CPortalCollideableEnumerator enumerator( pPortal ); + partition->EnumerateElementsAlongRay( PARTITION_ENGINE_SOLID_EDICTS | PARTITION_ENGINE_STATIC_PROPS, ray, false, &enumerator ); + for( counter = 0; counter != enumerator.m_iHandleCount; ++counter ) + { + if( staticpropmgr->IsStaticProp( enumerator.m_pHandles[counter] ) ) + { + //if( bFilterStaticProps && !pTraceFilter->ShouldHitEntity( enumerator.m_pHandles[counter], fMask ) ) + continue; //static props are handled separately, with clipped versions + } + else if ( !pTraceFilter->ShouldHitEntity( enumerator.m_pHandles[counter], fMask ) ) + { + continue; + } + + enginetrace->ClipRayToEntity( ray, fMask, enumerator.m_pHandles[counter], &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + *pTrace = TempTrace; + } + + + + + if( bTraceStaticProps ) + { + //local clipped static props + { + int iLocalStaticCount = portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 && portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = vec3_origin; + QAngle qTransform = vec3_angle; + + do + { + if( (!bFilterStaticProps) || pTraceFilter->ShouldHitEntity( pCurrentProp->pSourceProp, fMask ) ) + { + physcollision->TraceBox( ray, pCurrentProp->pCollide, vTransform, qTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + + if( bTraceHolyWall ) + { + //remote clipped static props transformed into our wall space + if( bTraceTransformedGeometry && (pTraceFilter->GetTraceType() != TRACE_WORLD_ONLY) && sv_portal_trace_vs_staticprops.GetBool() ) + { + int iLocalStaticCount = pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform; + QAngle qTransform = portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform; + + do + { + if( (!bFilterStaticProps) || pTraceFilter->ShouldHitEntity( pCurrentProp->pSourceProp, fMask ) ) + { + physcollision->TraceBox( ray, pCurrentProp->pCollide, vTransform, qTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + } + } + } + + if( pTrace->fraction > 1.0f ) //this should only happen if there was absolutely nothing to trace against + { + //AssertMsg( 0, "Nothing to trace against" ); + memset( pTrace, 0, sizeof( trace_t ) ); + pTrace->fraction = 1.0f; + pTrace->startpos = ray.m_Start - ray.m_StartOffset; + pTrace->endpos = pTrace->startpos + ray.m_Delta; + } + else if ( pTrace->fraction < 0 ) + { + // For all brush traces, use the 'portal backbrush' surface surface contents + // BUGBUG: Doing this is a great solution because brushes near a portal + // will have their contents and surface properties homogenized to the brush the portal ray hit. + pTrace->contents = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->surface = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->m_pEnt = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + } +} + +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + UTIL_Portal_TraceRay( pPortal, ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + +//----------------------------------------------------------------------------- +// Purpose: Trace a ray 'past' a portal's surface, hitting objects in the linked portal's collision environment +// Input : *pPortal - The portal being traced 'through' +// &ray - The ray being traced +// fMask - trace mask to cull results +// *pTraceFilter - trace filter to cull results +// *pTrace - Empty trace to return the result (value will be overwritten) +//----------------------------------------------------------------------------- +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); +#endif + // Transform the specified ray to the remote portal's space + Ray_t rayTransformed; + UTIL_Portal_RayTransform( pPortal->MatrixThisToLinked(), ray, rayTransformed ); + + AssertMsg ( ray.m_IsRay, "Ray with extents across portal tracing not implemented!" ); + + const CPortalSimulator &portalSimulator = pPortal->m_PortalSimulator; + CProp_Portal *pLinkedPortal = (CProp_Portal*)(pPortal->m_hLinkedPortal.Get()); + if( (pLinkedPortal == NULL) || (portalSimulator.RayIsInPortalHole( ray ) == false) ) + { + memset( pTrace, 0, sizeof(trace_t)); + pTrace->fraction = 1.0f; + pTrace->fractionleftsolid = 0; + + pTrace->contents = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->surface = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->m_pEnt = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + return; + } + UTIL_Portal_TraceRay( pLinkedPortal, rayTransformed, fMask, pTraceFilter, pTrace, bTraceHolyWall ); + + // Transform the ray's start, end and plane back into this portal's space, + // because we react to the collision as it is displayed, and the image is displayed with this local portal's orientation. + VMatrix matLinkedToThis = pLinkedPortal->MatrixThisToLinked(); + UTIL_Portal_PointTransform( matLinkedToThis, pTrace->startpos, pTrace->startpos ); + UTIL_Portal_PointTransform( matLinkedToThis, pTrace->endpos, pTrace->endpos ); + UTIL_Portal_PlaneTransform( matLinkedToThis, pTrace->plane, pTrace->plane ); +} + +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + UTIL_PortalLinked_TraceRay( pPortal, ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + +//----------------------------------------------------------------------------- +// Purpose: A version of trace entity which detects portals and translates the trace through portals +//----------------------------------------------------------------------------- +void UTIL_Portal_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, + unsigned int mask, ITraceFilter *pFilter, trace_t *pTrace ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); + Assert( pEntity->IsPlayer() ); + + CPortalSimulator *pPortalSimulator = NULL; + if( pEntity->IsPlayer() ) + { + C_Prop_Portal *pPortal = ((C_Portal_Player *)pEntity)->m_hPortalEnvironment.Get(); + if( pPortal ) + pPortalSimulator = &pPortal->m_PortalSimulator; + } +#else + CPortalSimulator *pPortalSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); +#endif + + memset( pTrace, 0, sizeof(trace_t)); + pTrace->fraction = 1.0f; + pTrace->fractionleftsolid = 0; + + ICollideable* pCollision = enginetrace->GetCollideable( pEntity ); + + // If main is simulating this object, trace as UTIL_TraceEntity would + trace_t realTrace; + QAngle qCollisionAngles = pCollision->GetCollisionAngles(); + enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, qCollisionAngles, mask, pFilter, &realTrace ); + + // For the below box test, we need to add the tolerance onto the extents, because the underlying + // box on plane side test doesn't use the parameter tolerance. + float flTolerance = 0.1f; + Vector vEntExtents = pEntity->WorldAlignSize() * 0.5 + Vector ( flTolerance, flTolerance, flTolerance ); + Vector vColCenter = realTrace.endpos + ( pEntity->WorldAlignMaxs() + pEntity->WorldAlignMins() ) * 0.5f; + + // If this entity is not simulated in a portal environment, trace as normal + if( pPortalSimulator == NULL ) + { + // If main is simulating this object, trace as UTIL_TraceEntity would + *pTrace = realTrace; + } + else + { + CPortalSimulator *pLinkedPortalSimulator = pPortalSimulator->GetLinkedPortalSimulator(); + + Ray_t entRay; + entRay.Init( vecAbsStart, vecAbsEnd, pCollision->OBBMins(), pCollision->OBBMaxs() ); + +#if 0 // this trace for brush ents made sense at one time, but it's 'overcolliding' during portal transitions (bugzilla#25) + if( realTrace.m_pEnt && (realTrace.m_pEnt->GetMoveType() != MOVETYPE_NONE) ) //started by hitting something moving which wouldn't be detected in the following traces + { + float fFirstPortalFraction = 2.0f; + CProp_Portal *pFirstPortal = UTIL_Portal_FirstAlongRay( entRay, fFirstPortalFraction ); + + if ( !pFirstPortal ) + *pTrace = realTrace; + else + { + Vector vFirstPortalForward; + pFirstPortal->GetVectors( &vFirstPortalForward, NULL, NULL ); + if ( vFirstPortalForward.Dot( realTrace.endpos - pFirstPortal->GetAbsOrigin() ) > 0.0f ) + *pTrace = realTrace; + } + } +#endif + + // We require both environments to be active in order to trace against them + Assert ( pCollision ); + if ( !pCollision ) + { + return; + } + + // World, displacements and holy wall are stored in separate collideables + // Traces against each and keep the closest intersection (if any) + trace_t tempTrace; + + // Hit the world + if ( pFilter->GetTraceType() != TRACE_ENTITIES_ONLY ) + { + if( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable && + sv_portal_trace_vs_world.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + + //if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable && + if( pLinkedPortalSimulator && + pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable && + sv_portal_trace_vs_world.GetBool() && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + + if ( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + if( tempTrace.fraction == 0.0f ) + tempTrace.startsolid = true; + + if( tempTrace.fractionleftsolid == 1.0f ) + tempTrace.allsolid = true; + + *pTrace = tempTrace; + } + } + + if ( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if( (tempTrace.startsolid == false) && (tempTrace.fraction < pTrace->fraction) ) //never allow something to be stuck in the tube, it's more of a last-resort guide than a real collideable + { + *pTrace = tempTrace; + } + } + + // For all brush traces, use the 'portal backbrush' surface surface contents + // BUGBUG: Doing this is a great solution because brushes near a portal + // will have their contents and surface properties homogenized to the brush the portal ray hit. + if ( pTrace->startsolid || (pTrace->fraction < 1.0f) ) + { + pTrace->surface = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->contents = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->m_pEnt = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + } + } + + // Trace vs entities + if ( pFilter->GetTraceType() != TRACE_WORLD_ONLY ) + { + if( sv_portal_trace_vs_staticprops.GetBool() && (pFilter->GetTraceType() != TRACE_ENTITIES_ONLY) ) + { + bool bFilterStaticProps = (pFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS); + + //local clipped static props + { + int iLocalStaticCount = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 && pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = vec3_origin; + QAngle qTransform = vec3_angle; + + do + { + if( (!bFilterStaticProps) || pFilter->ShouldHitEntity( pCurrentProp->pSourceProp, mask ) ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pCurrentProp->pCollide, vTransform, qTransform, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pCurrentProp->pCollide, vTransform, qTransform, &tempTrace ); + + if( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + + if( pLinkedPortalSimulator && pPortalSimulator->EntityIsInPortalHole( pEntity ) ) + { + +#ifndef CLIENT_DLL + if( sv_use_transformed_collideables.GetBool() ) //if this never gets turned off, it should be removed before release + { + //moving entities near the remote portal + CBaseEntity *pEnts[1024]; + int iEntCount = pLinkedPortalSimulator->GetMoveableOwnedEntities( pEnts, 1024 ); + + CTransformedCollideable transformedCollideable; + transformedCollideable.m_matTransform = pLinkedPortalSimulator->m_DataAccess.Placement.matThisToLinked; + transformedCollideable.m_matInvTransform = pLinkedPortalSimulator->m_DataAccess.Placement.matLinkedToThis; + for( int i = 0; i != iEntCount; ++i ) + { + CBaseEntity *pRemoteEntity = pEnts[i]; + if( pRemoteEntity->GetSolid() == SOLID_NONE ) + continue; + + transformedCollideable.m_pWrappedCollideable = pRemoteEntity->GetCollideable(); + Assert( transformedCollideable.m_pWrappedCollideable != NULL ); + + //enginetrace->ClipRayToCollideable( entRay, mask, &transformedCollideable, pTrace ); + + enginetrace->ClipRayToCollideable( entRay, mask, &transformedCollideable, &tempTrace ); + if( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + } +#endif //#ifndef CLIENT_DLL + } + } + } + + if( pTrace->fraction == 1.0f ) + { + memset( pTrace, 0, sizeof( trace_t ) ); + pTrace->fraction = 1.0f; + pTrace->startpos = vecAbsStart; + pTrace->endpos = vecAbsEnd; + } +//#endif + } +} + +void UTIL_Portal_PointTransform( const VMatrix matThisToLinked, const Vector &ptSource, Vector &ptTransformed ) +{ + ptTransformed = matThisToLinked * ptSource; +} + +void UTIL_Portal_VectorTransform( const VMatrix matThisToLinked, const Vector &vSource, Vector &vTransformed ) +{ + vTransformed = matThisToLinked.ApplyRotation( vSource ); +} + +void UTIL_Portal_AngleTransform( const VMatrix matThisToLinked, const QAngle &qSource, QAngle &qTransformed ) +{ + qTransformed = TransformAnglesToWorldSpace( qSource, matThisToLinked.As3x4() ); +} + +void UTIL_Portal_RayTransform( const VMatrix matThisToLinked, const Ray_t &raySource, Ray_t &rayTransformed ) +{ + rayTransformed = raySource; + + UTIL_Portal_PointTransform( matThisToLinked, raySource.m_Start, rayTransformed.m_Start ); + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_StartOffset, rayTransformed.m_StartOffset ); + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_Delta, rayTransformed.m_Delta ); + + //BUGBUG: Extents are axis aligned, so rotating it won't necessarily give us what we're expecting + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_Extents, rayTransformed.m_Extents ); + + //HACKHACK: Negative extents hang in traces, make each positive because we rotated it above + if ( rayTransformed.m_Extents.x < 0.0f ) + { + rayTransformed.m_Extents.x = -rayTransformed.m_Extents.x; + } + if ( rayTransformed.m_Extents.y < 0.0f ) + { + rayTransformed.m_Extents.y = -rayTransformed.m_Extents.y; + } + if ( rayTransformed.m_Extents.z < 0.0f ) + { + rayTransformed.m_Extents.z = -rayTransformed.m_Extents.z; + } + +} + +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const cplane_t &planeSource, cplane_t &planeTransformed ) +{ + planeTransformed = planeSource; + + Vector vTrans; + UTIL_Portal_VectorTransform( matThisToLinked, planeSource.normal, planeTransformed.normal ); + planeTransformed.dist = planeSource.dist * DotProduct( planeTransformed.normal, planeTransformed.normal ); + planeTransformed.dist += DotProduct( planeTransformed.normal, matThisToLinked.GetTranslation( vTrans ) ); +} + +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const VPlane &planeSource, VPlane &planeTransformed ) +{ + Vector vTranformedNormal; + float fTransformedDist; + + Vector vTrans; + UTIL_Portal_VectorTransform( matThisToLinked, planeSource.m_Normal, vTranformedNormal ); + fTransformedDist = planeSource.m_Dist * DotProduct( vTranformedNormal, vTranformedNormal ); + fTransformedDist += DotProduct( vTranformedNormal, matThisToLinked.GetTranslation( vTrans ) ); + + planeTransformed.Init( vTranformedNormal, fTransformedDist ); +} + +void UTIL_Portal_Triangles( const Vector &ptPortalCenter, const QAngle &qPortalAngles, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ) +{ + // Get points to make triangles + Vector vRight, vUp; + AngleVectors( qPortalAngles, NULL, &vRight, &vUp ); + + Vector vTopEdge = vUp * PORTAL_HALF_HEIGHT; + Vector vBottomEdge = -vTopEdge; + Vector vRightEdge = vRight * PORTAL_HALF_WIDTH; + Vector vLeftEdge = -vRightEdge; + + Vector vTopLeft = ptPortalCenter + vTopEdge + vLeftEdge; + Vector vTopRight = ptPortalCenter + vTopEdge + vRightEdge; + Vector vBottomLeft = ptPortalCenter + vBottomEdge + vLeftEdge; + Vector vBottomRight = ptPortalCenter + vBottomEdge + vRightEdge; + + // Make triangles + pvTri1[ 0 ] = vTopRight; + pvTri1[ 1 ] = vTopLeft; + pvTri1[ 2 ] = vBottomLeft; + + pvTri2[ 0 ] = vTopRight; + pvTri2[ 1 ] = vBottomLeft; + pvTri2[ 2 ] = vBottomRight; +} + +void UTIL_Portal_Triangles( const CProp_Portal *pPortal, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ) +{ + UTIL_Portal_Triangles( pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), pvTri1, pvTri2 ); +} + +float UTIL_Portal_DistanceThroughPortal( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ) +{ + return FastSqrt( UTIL_Portal_DistanceThroughPortalSqr( pPortal, vPoint1, vPoint2 ) ); +} + +float UTIL_Portal_DistanceThroughPortalSqr( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ) +{ + if ( !pPortal || !pPortal->m_bActivated ) + return -1.0f; + + CProp_Portal *pPortalLinked = pPortal->m_hLinkedPortal; + if ( !pPortalLinked || !pPortalLinked->m_bActivated ) + return -1.0f; + + return vPoint1.DistToSqr( pPortal->GetAbsOrigin() ) + pPortalLinked->GetAbsOrigin().DistToSqr( vPoint2 ); +} + +float UTIL_Portal_ShortestDistance( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out /*= NULL*/, bool bRequireStraightLine /*= false*/ ) +{ + return FastSqrt( UTIL_Portal_ShortestDistanceSqr( vPoint1, vPoint2, pShortestDistPortal_Out, bRequireStraightLine ) ); +} + +float UTIL_Portal_ShortestDistanceSqr( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out /*= NULL*/, bool bRequireStraightLine /*= false*/ ) +{ + float fMinDist = vPoint1.DistToSqr( vPoint2 ); + + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + { + if( pShortestDistPortal_Out ) + *pShortestDistPortal_Out = NULL; + + return fMinDist; + } + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + CProp_Portal *pShortestDistPortal = NULL; + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->m_bActivated ) + { + CProp_Portal *pLinkedPortal = pTempPortal->m_hLinkedPortal.Get(); + if( pLinkedPortal != NULL ) + { + Vector vPoint1Transformed = pTempPortal->MatrixThisToLinked() * vPoint1; + + float fDirectDist = vPoint1Transformed.DistToSqr( vPoint2 ); + if( fDirectDist < fMinDist ) + { + //worth investigating further + //find out if it's a straight line through the portal, or if we have to wrap around a corner + float fPoint1TransformedDist = pLinkedPortal->m_plane_Origin.normal.Dot( vPoint1Transformed ) - pLinkedPortal->m_plane_Origin.dist; + float fPoint2Dist = pLinkedPortal->m_plane_Origin.normal.Dot( vPoint2 ) - pLinkedPortal->m_plane_Origin.dist; + + bool bStraightLine = true; + if( (fPoint1TransformedDist > 0.0f) || (fPoint2Dist < 0.0f) ) //straight line through portal impossible, part of the line has to backtrack to get to the portal surface + bStraightLine = false; + + if( bStraightLine ) //if we're not already doing some crazy wrapping, find an intersection point + { + float fTotalDist = fPoint2Dist - fPoint1TransformedDist; //fPoint1TransformedDist is known to be negative + Vector ptPlaneIntersection; + + if( fTotalDist != 0.0f ) + { + float fInvTotalDist = 1.0f / fTotalDist; + ptPlaneIntersection = (vPoint1Transformed * (fPoint2Dist * fInvTotalDist)) + (vPoint2 * ((-fPoint1TransformedDist) * fInvTotalDist)); + } + else + { + ptPlaneIntersection = vPoint1Transformed; + } + + Vector vRight, vUp; + pLinkedPortal->GetVectors( NULL, &vRight, &vUp ); + + Vector ptLinkedCenter = pLinkedPortal->GetAbsOrigin(); + Vector vCenterToIntersection = ptPlaneIntersection - ptLinkedCenter; + float fRight = vRight.Dot( vCenterToIntersection ); + float fUp = vUp.Dot( vCenterToIntersection ); + + float fAbsRight = fabs( fRight ); + float fAbsUp = fabs( fUp ); + if( (fAbsRight > PORTAL_HALF_WIDTH) || + (fAbsUp > PORTAL_HALF_HEIGHT) ) + bStraightLine = false; + + if( bStraightLine == false ) + { + if( bRequireStraightLine ) + continue; + + //find the offending extent and shorten both extents to bring it into the portal quad + float fNormalizer; + if( fAbsRight > PORTAL_HALF_WIDTH ) + { + fNormalizer = fAbsRight/PORTAL_HALF_WIDTH; + + if( fAbsUp > PORTAL_HALF_HEIGHT ) + { + float fUpNormalizer = fAbsUp/PORTAL_HALF_HEIGHT; + if( fUpNormalizer > fNormalizer ) + fNormalizer = fUpNormalizer; + } + } + else + { + fNormalizer = fAbsUp/PORTAL_HALF_HEIGHT; + } + + vCenterToIntersection *= (1.0f/fNormalizer); + ptPlaneIntersection = ptLinkedCenter + vCenterToIntersection; + + float fWrapDist = vPoint1Transformed.DistToSqr( ptPlaneIntersection ) + vPoint2.DistToSqr( ptPlaneIntersection ); + if( fWrapDist < fMinDist ) + { + fMinDist = fWrapDist; + pShortestDistPortal = pTempPortal; + } + } + else + { + //it's a straight shot from point 1 to 2 through the portal + fMinDist = fDirectDist; + pShortestDistPortal = pTempPortal; + } + } + else + { + if( bRequireStraightLine ) + continue; + + //do some crazy wrapped line intersection algorithm + + //for now, just do the cheap and easy solution + float fWrapDist = vPoint1.DistToSqr( pTempPortal->GetAbsOrigin() ) + pLinkedPortal->GetAbsOrigin().DistToSqr( vPoint2 ); + if( fWrapDist < fMinDist ) + { + fMinDist = fWrapDist; + pShortestDistPortal = pTempPortal; + } + } + } + } + } + } + + return fMinDist; +} + +void UTIL_Portal_AABB( const CProp_Portal *pPortal, Vector &vMin, Vector &vMax ) +{ + Vector vOrigin = pPortal->GetAbsOrigin(); + QAngle qAngles = pPortal->GetAbsAngles(); + + Vector vOBBForward; + Vector vOBBRight; + Vector vOBBUp; + + AngleVectors( qAngles, &vOBBForward, &vOBBRight, &vOBBUp ); + + //scale the extents to usable sizes + vOBBForward *= PORTAL_HALF_DEPTH; + vOBBRight *= PORTAL_HALF_WIDTH; + vOBBUp *= PORTAL_HALF_HEIGHT; + + vOrigin -= vOBBForward + vOBBRight + vOBBUp; + + vOBBForward *= 2.0f; + vOBBRight *= 2.0f; + vOBBUp *= 2.0f; + + vMin = vMax = vOrigin; + + for( int i = 1; i != 8; ++i ) + { + Vector ptTest = vOrigin; + if( i & (1 << 0) ) ptTest += vOBBForward; + if( i & (1 << 1) ) ptTest += vOBBRight; + if( i & (1 << 2) ) ptTest += vOBBUp; + + if( ptTest.x < vMin.x ) vMin.x = ptTest.x; + if( ptTest.y < vMin.y ) vMin.y = ptTest.y; + if( ptTest.z < vMin.z ) vMin.z = ptTest.z; + if( ptTest.x > vMax.x ) vMax.x = ptTest.x; + if( ptTest.y > vMax.y ) vMax.y = ptTest.y; + if( ptTest.z > vMax.z ) vMax.z = ptTest.z; + } +} + +float UTIL_IntersectRayWithPortal( const Ray_t &ray, const CProp_Portal *pPortal ) +{ + if ( !pPortal || !pPortal->m_bActivated ) + { + return -1.0f; + } + + Vector vForward; + pPortal->GetVectors( &vForward, NULL, NULL ); + + // Discount rays not coming from the front of the portal + float fDot = DotProduct( vForward, ray.m_Delta ); + if ( fDot > 0.0f ) + { + return -1.0f; + } + + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( pPortal, pvTri1, pvTri2 ); + + float fT; + + // Test triangle 1 + fT = IntersectRayWithTriangle( ray, pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], false ); + + // If there was an intersection return the T + if ( fT >= 0.0f ) + return fT; + + // Return the result of collision with the other face triangle + return IntersectRayWithTriangle( ray, pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], false ); +} + +bool UTIL_IntersectRayWithPortalOBB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ) +{ + return IntersectRayWithOBB( ray, pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs, 0.0f, pTrace ); +} + +bool UTIL_IntersectRayWithPortalOBBAsAABB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ) +{ + Vector vAABBMins, vAABBMaxs; + + UTIL_Portal_AABB( pPortal, vAABBMins, vAABBMaxs ); + + return IntersectRayWithBox( ray, vAABBMins, vAABBMaxs, 0.0f, pTrace ); +} + +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const Vector &ptPortalCenter, const QAngle &qPortalAngles, float flTolerance ) +{ + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( ptPortalCenter, qPortalAngles, pvTri1, pvTri2 ); + + cplane_t plane; + + ComputeTrianglePlane( pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], plane.normal, plane.dist ); + plane.type = PLANE_ANYZ; + plane.signbits = SignbitsForPlane( &plane ); + + if ( IsBoxIntersectingTriangle( vecBoxCenter, vecBoxExtents, pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], plane, flTolerance ) ) + { + return true; + } + + ComputeTrianglePlane( pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], plane.normal, plane.dist ); + plane.type = PLANE_ANYZ; + plane.signbits = SignbitsForPlane( &plane ); + + return IsBoxIntersectingTriangle( vecBoxCenter, vecBoxExtents, pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], plane, flTolerance ); +} + +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const CProp_Portal *pPortal, float flTolerance ) +{ + if( pPortal == NULL ) + return false; + + return UTIL_IsBoxIntersectingPortal( vecBoxCenter, vecBoxExtents, pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), flTolerance ); +} + +CProp_Portal *UTIL_IntersectEntityExtentsWithPortal( const CBaseEntity *pEntity ) +{ + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + return NULL; + + Vector vMin, vMax; + pEntity->CollisionProp()->WorldSpaceAABB( &vMin, &vMax ); + Vector ptCenter = ( vMin + vMax ) * 0.5f; + Vector vExtents = ( vMax - vMin ) * 0.5f; + + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->m_bActivated && + (pTempPortal->m_hLinkedPortal.Get() != NULL) && + UTIL_IsBoxIntersectingPortal( ptCenter, vExtents, pTempPortal ) ) + { + return pPortals[i]; + } + } + + return NULL; +} + +void UTIL_Portal_NDebugOverlay( const Vector &ptPortalCenter, const QAngle &qPortalAngles, int r, int g, int b, int a, bool noDepthTest, float duration ) +{ +#ifndef CLIENT_DLL + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( ptPortalCenter, qPortalAngles, pvTri1, pvTri2 ); + + NDebugOverlay::Triangle( pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], r, g, b, a, noDepthTest, duration ); + NDebugOverlay::Triangle( pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], r, g, b, a, noDepthTest, duration ); +#endif //#ifndef CLIENT_DLL +} + +void UTIL_Portal_NDebugOverlay( const CProp_Portal *pPortal, int r, int g, int b, int a, bool noDepthTest, float duration ) +{ +#ifndef CLIENT_DLL + UTIL_Portal_NDebugOverlay( pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), r, g, b, a, noDepthTest, duration ); +#endif //#ifndef CLIENT_DLL +} + + +bool FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask ) //assumes the object is already in a mostly passable space +{ + if ( sv_use_find_closest_passable_space.GetBool() == false ) + return true; + + // Don't ever do this to entities with a move parent + if ( pEntity->GetMoveParent() ) + return true; + +#ifndef CLIENT_DLL + ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "RUNNING FIND CLOSEST PASSABLE SPACE on %s..\n", pEntity->GetDebugName() ) ); +#endif + + Vector ptExtents[8]; //ordering is going to be like 3 bits, where 0 is a min on the related axis, and 1 is a max on the same axis, axis order x y z + + float fExtentsValidation[8]; //some points are more valid than others, and this is our measure + + + Vector vEntityMaxs;// = pEntity->WorldAlignMaxs(); + Vector vEntityMins;// = pEntity->WorldAlignMins(); + CCollisionProperty *pEntityCollision = pEntity->CollisionProp(); + pEntityCollision->WorldSpaceAABB( &vEntityMins, &vEntityMaxs ); + + + + Vector ptEntityCenter = ((vEntityMins + vEntityMaxs) / 2.0f); + vEntityMins -= ptEntityCenter; + vEntityMaxs -= ptEntityCenter; + + Vector ptEntityOriginalCenter = ptEntityCenter; + + ptEntityCenter.z += 0.001f; //to satisfy m_IsSwept on first pass + + int iEntityCollisionGroup = pEntity->GetCollisionGroup(); + + trace_t traces[2]; + Ray_t entRay; + //entRay.Init( ptEntityCenter, ptEntityCenter, vEntityMins, vEntityMaxs ); + entRay.m_Extents = vEntityMaxs; + entRay.m_IsRay = false; + entRay.m_IsSwept = true; + entRay.m_StartOffset = vec3_origin; + + Vector vOriginalExtents = vEntityMaxs; + + Vector vGrowSize = vEntityMaxs / 101.0f; + vEntityMaxs -= vGrowSize; + vEntityMins += vGrowSize; + + + Ray_t testRay; + testRay.m_Extents = vGrowSize; + testRay.m_IsRay = false; + testRay.m_IsSwept = true; + testRay.m_StartOffset = vec3_origin; + + + + unsigned int iFailCount; + for( iFailCount = 0; iFailCount != 100; ++iFailCount ) + { + entRay.m_Start = ptEntityCenter; + entRay.m_Delta = ptEntityOriginalCenter - ptEntityCenter; + + UTIL_TraceRay( entRay, fMask, pEntity, iEntityCollisionGroup, &traces[0] ); + if( traces[0].startsolid == false ) + { + Vector vNewPos = traces[0].endpos + (pEntity->GetAbsOrigin() - ptEntityOriginalCenter); +#ifdef CLIENT_DLL + pEntity->SetAbsOrigin( vNewPos ); +#else + pEntity->Teleport( &vNewPos, NULL, NULL ); +#endif + return true; //current placement worked + } + + bool bExtentInvalid[8]; + for( int i = 0; i != 8; ++i ) + { + fExtentsValidation[i] = 0.0f; + ptExtents[i] = ptEntityCenter; + ptExtents[i].x += ((i & (1<<0)) ? vEntityMaxs.x : vEntityMins.x); + ptExtents[i].y += ((i & (1<<1)) ? vEntityMaxs.y : vEntityMins.y); + ptExtents[i].z += ((i & (1<<2)) ? vEntityMaxs.z : vEntityMins.z); + + bExtentInvalid[i] = enginetrace->PointOutsideWorld( ptExtents[i] ); + } + + unsigned int counter, counter2; + for( counter = 0; counter != 7; ++counter ) + { + for( counter2 = counter + 1; counter2 != 8; ++counter2 ) + { + + testRay.m_Delta = ptExtents[counter2] - ptExtents[counter]; + + if( bExtentInvalid[counter] ) + traces[0].startsolid = true; + else + { + testRay.m_Start = ptExtents[counter]; + UTIL_TraceRay( testRay, fMask, pEntity, iEntityCollisionGroup, &traces[0] ); + } + + if( bExtentInvalid[counter2] ) + traces[1].startsolid = true; + else + { + testRay.m_Start = ptExtents[counter2]; + testRay.m_Delta = -testRay.m_Delta; + UTIL_TraceRay( testRay, fMask, pEntity, iEntityCollisionGroup, &traces[1] ); + } + + float fDistance = testRay.m_Delta.Length(); + + for( int i = 0; i != 2; ++i ) + { + int iExtent = (i==0)?(counter):(counter2); + + if( traces[i].startsolid ) + { + fExtentsValidation[iExtent] -= 100.0f; + } + else + { + fExtentsValidation[iExtent] += traces[i].fraction * fDistance; + } + } + } + } + + Vector vNewOriginDirection( 0.0f, 0.0f, 0.0f ); + float fTotalValidation = 0.0f; + for( counter = 0; counter != 8; ++counter ) + { + if( fExtentsValidation[counter] > 0.0f ) + { + vNewOriginDirection += (ptExtents[counter] - ptEntityCenter) * fExtentsValidation[counter]; + fTotalValidation += fExtentsValidation[counter]; + } + } + + if( fTotalValidation != 0.0f ) + { + ptEntityCenter += (vNewOriginDirection / fTotalValidation); + + //increase sizing + testRay.m_Extents += vGrowSize; + vEntityMaxs -= vGrowSize; + vEntityMins = -vEntityMaxs; + } + else + { + //no point was valid, apply the indecisive vector + ptEntityCenter += vIndecisivePush; + + //reset sizing + testRay.m_Extents = vGrowSize; + vEntityMaxs = vOriginalExtents; + vEntityMins = -vEntityMaxs; + } + } + + // X360TBD: Hits in portal devtest + AssertMsg( IsX360() || iFailCount != 100, "FindClosestPassableSpace() failure." ); + return false; +} + +bool UTIL_Portal_EntityIsInPortalHole( const CProp_Portal *pPortal, CBaseEntity *pEntity ) +{ + CCollisionProperty *pCollisionProp = pEntity->CollisionProp(); + Vector vMins = pCollisionProp->OBBMins(); + Vector vMaxs = pCollisionProp->OBBMaxs(); + Vector vForward, vUp, vRight; + AngleVectors( pCollisionProp->GetCollisionAngles(), &vForward, &vRight, &vUp ); + Vector ptOrigin = pEntity->GetAbsOrigin(); + + Vector ptOBBCenter = pEntity->GetAbsOrigin() + (vMins + vMaxs * 0.5f); + Vector vExtents = (vMaxs - vMins) * 0.5f; + + vForward *= vExtents.x; + vRight *= vExtents.y; + vUp *= vExtents.z; + + Vector vPortalForward, vPortalRight, vPortalUp; + pPortal->GetVectors( &vPortalForward, &vPortalRight, &vPortalUp ); + Vector ptPortalCenter = pPortal->GetAbsOrigin(); + + return OBBHasFullyContainedIntersectionWithQuad( vForward, vRight, vUp, ptOBBCenter, + vPortalForward, vPortalForward.Dot( ptPortalCenter ), ptPortalCenter, + vPortalRight, PORTAL_HALF_WIDTH + 1.0f, vPortalUp, PORTAL_HALF_HEIGHT + 1.0f ); +} + + +#ifdef CLIENT_DLL +void UTIL_TransformInterpolatedAngle( CInterpolatedVar< QAngle > &qInterped, matrix3x4_t matTransform, bool bSkipNewest ) +{ + int iHead = qInterped.GetHead(); + if( !qInterped.IsValidIndex( iHead ) ) + return; + +#ifdef DBGFLAG_ASSERT + float fHeadTime; + qInterped.GetHistoryValue( iHead, fHeadTime ); +#endif + + float fTime; + QAngle *pCurrent; + int iCurrent; + + if( bSkipNewest ) + iCurrent = qInterped.GetNext( iHead ); + else + iCurrent = iHead; + + while( (pCurrent = qInterped.GetHistoryValue( iCurrent, fTime )) != NULL ) + { + Assert( (fTime <= fHeadTime) || (iCurrent == iHead) ); //asserting that head is always newest + + if( fTime < gpGlobals->curtime ) + *pCurrent = TransformAnglesToWorldSpace( *pCurrent, matTransform ); + + iCurrent = qInterped.GetNext( iCurrent ); + if( iCurrent == iHead ) + break; + } + + qInterped.Interpolate( gpGlobals->curtime ); +} + +void UTIL_TransformInterpolatedPosition( CInterpolatedVar< Vector > &vInterped, VMatrix matTransform, bool bSkipNewest ) +{ + int iHead = vInterped.GetHead(); + if( !vInterped.IsValidIndex( iHead ) ) + return; + +#ifdef DBGFLAG_ASSERT + float fHeadTime; + vInterped.GetHistoryValue( iHead, fHeadTime ); +#endif + + float fTime; + Vector *pCurrent; + int iCurrent; + + if( bSkipNewest ) + iCurrent = vInterped.GetNext( iHead ); + else + iCurrent = iHead; + + while( (pCurrent = vInterped.GetHistoryValue( iCurrent, fTime )) != NULL ) + { + Assert( (fTime <= fHeadTime) || (iCurrent == iHead) ); + + if( fTime < gpGlobals->curtime ) + *pCurrent = matTransform * (*pCurrent); + + iCurrent = vInterped.GetNext( iCurrent ); + if( iCurrent == iHead ) + break; + } + + vInterped.Interpolate( gpGlobals->curtime ); +} +#endif + + +#ifndef CLIENT_DLL + +void CC_Debug_FixMyPosition( void ) +{ + CBaseEntity *pPlayer = UTIL_GetCommandClient(); + + FindClosestPassableSpace( pPlayer, vec3_origin ); +} + +static ConCommand debug_fixmyposition("debug_fixmyposition", CC_Debug_FixMyPosition, "Runs FindsClosestPassableSpace() on player.", FCVAR_CHEAT ); +#endif diff --git a/game/shared/portal/portal_util_shared.h b/game/shared/portal/portal_util_shared.h new file mode 100644 index 0000000..8eac1bb --- /dev/null +++ b/game/shared/portal/portal_util_shared.h @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PORTAL_UTIL_SHARED_H +#define PORTAL_UTIL_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + +#include "engine/IEngineTrace.h" + +extern bool g_bBulletPortalTrace; + +#ifdef CLIENT_DLL + #include "client_class.h" + #include "interpolatedvar.h" + class C_Prop_Portal; + typedef C_Prop_Portal CProp_Portal; + class C_Beam; + typedef C_Beam CBeam; +#else + class CProp_Portal; + class CBeam; +#endif + +Color UTIL_Portal_Color( int iPortal ); + +void UTIL_Portal_Trace_Filter( class CTraceFilterSimpleClassnameList *traceFilterPortalShot ); + +CProp_Portal* UTIL_Portal_FirstAlongRay( const Ray_t &ray, float &fMustBeCloserThan ); + +bool UTIL_Portal_TraceRay_Bullets( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true ); +CProp_Portal* UTIL_Portal_TraceRay_Beam( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, float *pfFraction ); +bool UTIL_Portal_Trace_Beam( const CBeam *pBeam, Vector &vecStart, Vector &vecEnd, Vector &vecIntersectionStart, Vector &vecIntersectionEnd, ITraceFilter *pTraceFilter ); + +void UTIL_Portal_TraceRay_With( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true ); +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true ); //traces a ray normally, then sees if portals have anything to say about it +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall = true ); + +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true ); //traces against a specific portal's environment, does no *real* tracing +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall = true ); + +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true ); //traces against a specific portal's environment, does no *real* tracing +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall = true ); + +// tests if a ray's trace hits any portals +bool UTIL_DidTraceTouchPortals ( const Ray_t& ray, const trace_t& trace, CProp_Portal** pOutLocal = NULL, CProp_Portal** pOutRemote = NULL ); + +// Version of the TraceEntity functions which trace through portals +void UTIL_Portal_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, + unsigned int mask, ITraceFilter *pFilter, trace_t *ptr ); + +void UTIL_Portal_PointTransform( const VMatrix matThisToLinked, const Vector &ptSource, Vector &ptTransformed ); +void UTIL_Portal_VectorTransform( const VMatrix matThisToLinked, const Vector &vSource, Vector &vTransformed ); +void UTIL_Portal_AngleTransform( const VMatrix matThisToLinked, const QAngle &qSource, QAngle &qTransformed ); +void UTIL_Portal_RayTransform( const VMatrix matThisToLinked, const Ray_t &raySource, Ray_t &rayTransformed ); +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const cplane_t &planeSource, cplane_t &planeTransformed ); +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const VPlane &planeSource, VPlane &planeTransformed ); + +void UTIL_Portal_Triangles( const Vector &ptPortalCenter, const QAngle &qPortalAngles, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ); +void UTIL_Portal_Triangles( const CProp_Portal *pPortal, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ); +void UTIL_Portal_AABB( const CProp_Portal *pPortal, Vector &vMin, Vector &vMax ); + +float UTIL_Portal_DistanceThroughPortal( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ); +float UTIL_Portal_DistanceThroughPortalSqr( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ); +float UTIL_Portal_ShortestDistance( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out = NULL, bool bRequireStraightLine = false ); +float UTIL_Portal_ShortestDistanceSqr( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out = NULL, bool bRequireStraightLine = false ); + +//----------------------------------------------------------------------------- +// +// UTIL_IntersectRayWithPortal +// +// Intersects a ray with a portal, returns distance t along ray. +// t will be less than zero if no intersection occurred +// +//----------------------------------------------------------------------------- +float UTIL_IntersectRayWithPortal( const Ray_t &ray, const CProp_Portal *pPortal ); + +bool UTIL_IntersectRayWithPortalOBB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ); +bool UTIL_IntersectRayWithPortalOBBAsAABB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ); + +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const Vector &ptPortalCenter, const QAngle &qPortalAngles, float flTolerance = 0.0f ); +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const CProp_Portal *pPortal, float flTolerance = 0.0f ); + +CProp_Portal *UTIL_IntersectEntityExtentsWithPortal( const CBaseEntity *pEntity ); + +void UTIL_Portal_NDebugOverlay( const Vector &ptPortalCenter, const QAngle &qPortalAngles, int r, int g, int b, int a, bool noDepthTest, float duration ); +void UTIL_Portal_NDebugOverlay( const CProp_Portal *pPortal, int r, int g, int b, int a, bool noDepthTest, float duration ); + +bool FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask = MASK_SOLID ); //assumes the object is already in a mostly passable space + +#ifdef CLIENT_DLL +void UTIL_TransformInterpolatedAngle( CInterpolatedVar< QAngle > &qInterped, matrix3x4_t matTransform, bool bSkipNewest ); +void UTIL_TransformInterpolatedPosition( CInterpolatedVar< Vector > &vInterped, VMatrix matTransform, bool bSkipNewest ); +#endif + +bool UTIL_Portal_EntityIsInPortalHole( const CProp_Portal *pPortal, CBaseEntity *pEntity ); + +#endif //#ifndef PORTAL_UTIL_SHARED_H + diff --git a/game/shared/portal/portal_weapon_parse.cpp b/game/shared/portal/portal_weapon_parse.cpp new file mode 100644 index 0000000..ed6edfd --- /dev/null +++ b/game/shared/portal/portal_weapon_parse.cpp @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include <KeyValues.h> +#include "portal_weapon_parse.h" +#include "ammodef.h" + +FileWeaponInfo_t* CreateWeaponInfo() +{ + return new CPortalSWeaponInfo; +} + + + +CPortalSWeaponInfo::CPortalSWeaponInfo() +{ + m_iPlayerDamage = 0; +} + + +void CPortalSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) +{ + BaseClass::Parse( pKeyValuesData, szWeaponName ); + + m_iPlayerDamage = pKeyValuesData->GetInt( "damage", 0 ); +} + + diff --git a/game/shared/portal/portal_weapon_parse.h b/game/shared/portal/portal_weapon_parse.h new file mode 100644 index 0000000..8a1ac0c --- /dev/null +++ b/game/shared/portal/portal_weapon_parse.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PORTAL_WEAPON_PARSE_H +#define PORTAL_WEAPON_PARSE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "weapon_parse.h" +#include "networkvar.h" + + +//-------------------------------------------------------------------------------------------------------- +class CPortalSWeaponInfo : public FileWeaponInfo_t +{ +public: + DECLARE_CLASS_GAMEROOT( CPortalSWeaponInfo, FileWeaponInfo_t ); + + CPortalSWeaponInfo(); + + virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName ); + + +public: + + int m_iPlayerDamage; +}; + + +#endif // PORTAL_WEAPON_PARSE_H diff --git a/game/shared/portal/prop_portal_shared.cpp b/game/shared/portal/prop_portal_shared.cpp new file mode 100644 index 0000000..f7756b2 --- /dev/null +++ b/game/shared/portal/prop_portal_shared.cpp @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "prop_portal_shared.h" +#include "portal_shareddefs.h" + +#ifdef CLIENT_DLL +#include "c_basedoor.h" +#endif + +CUtlVector<CProp_Portal *> CProp_Portal_Shared::AllPortals; + +const Vector CProp_Portal_Shared::vLocalMins( 0.0f, -PORTAL_HALF_WIDTH, -PORTAL_HALF_HEIGHT ); +const Vector CProp_Portal_Shared::vLocalMaxs( 64.0f, PORTAL_HALF_WIDTH, PORTAL_HALF_HEIGHT ); + +void CProp_Portal_Shared::UpdatePortalTransformationMatrix( const matrix3x4_t &localToWorld, const matrix3x4_t &remoteToWorld, VMatrix *pMatrix ) +{ + VMatrix matPortal1ToWorldInv, matPortal2ToWorld, matRotation; + + //inverse of this + MatrixInverseTR( localToWorld, matPortal1ToWorldInv ); + + //180 degree rotation about up + matRotation.Identity(); + matRotation.m[0][0] = -1.0f; + matRotation.m[1][1] = -1.0f; + + //final + matPortal2ToWorld = remoteToWorld; + *pMatrix = matPortal2ToWorld * matRotation * matPortal1ToWorldInv; +} + +static char *g_pszPortalNonTeleportable[] = +{ + "func_door", + "func_door_rotating", + "prop_door_rotating", + "func_tracktrain", + //"env_ghostanimating", + "physicsshadowclone" +}; + +bool CProp_Portal_Shared::IsEntityTeleportable( CBaseEntity *pEntity ) +{ + + do + { + +#ifdef CLIENT_DLL + //client + + if( dynamic_cast<C_BaseDoor *>(pEntity) != NULL ) + return false; + +#else + //server + + for( int i = 0; i != ARRAYSIZE(g_pszPortalNonTeleportable); ++i ) + { + if( FClassnameIs( pEntity, g_pszPortalNonTeleportable[i] ) ) + return false; + } + +#endif + + Assert( pEntity != pEntity->GetMoveParent() ); + pEntity = pEntity->GetMoveParent(); + } while( pEntity ); + + return true; +} + + + + diff --git a/game/shared/portal/prop_portal_shared.h b/game/shared/portal/prop_portal_shared.h new file mode 100644 index 0000000..ba4b7b0 --- /dev/null +++ b/game/shared/portal/prop_portal_shared.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROP_PORTAL_SHARED_H +#define PROP_PORTAL_SHARED_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" + +#ifdef CLIENT_DLL +#include "c_prop_portal.h" +#else +#include "prop_portal.h" +#endif + +// CProp_Portal enum for the portal corners (if a user wants a specific corner) +enum PortalCorners_t { PORTAL_DOWN_RIGHT = 0, PORTAL_DOWN_LEFT, PORTAL_UP_RIGHT, PORTAL_UP_LEFT }; + +class CProp_Portal_Shared //defined as a class to make intellisense more intelligent +{ +public: + static void UpdatePortalTransformationMatrix( const matrix3x4_t &localToWorld, const matrix3x4_t &remoteToWorld, VMatrix *pMatrix ); + + static bool IsEntityTeleportable( CBaseEntity *pEntity ); + //static CProp_Portal *GetPortal1( bool bCreateIfNotFound = false ); + //static CProp_Portal *GetPortal2( bool bCreateIfNotFound = false ); + + static const Vector vLocalMins; + static const Vector vLocalMaxs; + +#ifdef CLIENT_DLL + static CUtlVector<C_Prop_Portal *> AllPortals; //an array of existing portal entities +#else + static CUtlVector<CProp_Portal *> AllPortals; //an array of existing portal entities +#endif //#ifdef CLIENT_DLL +}; + + + + + +#endif //#ifndef PROP_PORTAL_SHARED_H
\ No newline at end of file diff --git a/game/shared/portal/weapon_portalbase.cpp b/game/shared/portal/weapon_portalbase.cpp new file mode 100644 index 0000000..e3125ee --- /dev/null +++ b/game/shared/portal/weapon_portalbase.cpp @@ -0,0 +1,442 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "in_buttons.h" +#include "takedamageinfo.h" +#include "ammodef.h" +#include "portal_gamerules.h" + + +#ifdef CLIENT_DLL +extern IVModelInfoClient* modelinfo; +#else +extern IVModelInfo* modelinfo; +#endif + + +#if defined( CLIENT_DLL ) + + #include "vgui/ISurface.h" + #include "vgui_controls/Controls.h" + #include "c_portal_player.h" + #include "hud_crosshair.h" + #include "PortalRender.h" + +#else + + #include "portal_player.h" + #include "vphysics/constraints.h" + +#endif + +#include "weapon_portalbase.h" + + +// ----------------------------------------------------------------------------- // +// Global functions. +// ----------------------------------------------------------------------------- // + +bool IsAmmoType( int iAmmoType, const char *pAmmoName ) +{ + return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; +} + +static const char * s_WeaponAliasInfo[] = +{ + "none", // WEAPON_NONE = 0, + + //Melee + "shotgun", //WEAPON_AMERKNIFE, + + NULL, // end of list marker +}; + + +// ----------------------------------------------------------------------------- // +// CWeaponPortalBase tables. +// ----------------------------------------------------------------------------- // + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalBase, DT_WeaponPortalBase ) + +BEGIN_NETWORK_TABLE( CWeaponPortalBase, DT_WeaponPortalBase ) + +#ifdef CLIENT_DLL + +#else + // world weapon models have no aminations + // SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ), +// SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), +// SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ), +#endif + +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponPortalBase ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_portal_base, CWeaponPortalBase ); + + +#ifdef GAME_DLL + + BEGIN_DATADESC( CWeaponPortalBase ) + + END_DATADESC() + +#endif + +// ----------------------------------------------------------------------------- // +// CWeaponPortalBase implementation. +// ----------------------------------------------------------------------------- // +CWeaponPortalBase::CWeaponPortalBase() +{ + SetPredictionEligible( true ); + AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. + + m_flNextResetCheckTime = 0.0f; +} + + +bool CWeaponPortalBase::IsPredicted() const +{ + return false; +} + +void CWeaponPortalBase::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ ) +{ +#ifdef CLIENT_DLL + + // If we have some sounds from the weapon classname.txt file, play a random one of them + const char *shootsound = GetWpnData().aShootSounds[ sound_type ]; + if ( !shootsound || !shootsound[0] ) + return; + + CBroadcastRecipientFilter filter; // this is client side only + if ( !te->CanPredict() ) + return; + + CBaseEntity::EmitSound( filter, GetPlayerOwner()->entindex(), shootsound, &GetPlayerOwner()->GetAbsOrigin() ); +#else + BaseClass::WeaponSound( sound_type, soundtime ); +#endif +} + + +CBasePlayer* CWeaponPortalBase::GetPlayerOwner() const +{ + return dynamic_cast< CBasePlayer* >( GetOwner() ); +} + +CPortal_Player* CWeaponPortalBase::GetPortalPlayerOwner() const +{ + return dynamic_cast< CPortal_Player* >( GetOwner() ); +} + +#ifdef CLIENT_DLL + +void CWeaponPortalBase::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( GetPredictable() && !ShouldPredict() ) + ShutdownPredictable(); +} + +int CWeaponPortalBase::DrawModel( int flags ) +{ + if ( !m_bReadyToDraw ) + return 0; + + if ( GetOwner() && (GetOwner() == C_BasePlayer::GetLocalPlayer()) && !g_pPortalRender->IsRenderingPortal() && !C_BasePlayer::ShouldDrawLocalPlayer() ) + return 0; + + //Sometimes the return value of ShouldDrawLocalPlayer() fluctuates too often to draw the correct model all the time, so this is a quick fix if it's changed too fast + int iOriginalIndex = GetModelIndex(); + bool bChangeModelBack = false; + + int iWorldModelIndex = GetWorldModelIndex(); + if( iOriginalIndex != iWorldModelIndex ) + { + SetModelIndex( iWorldModelIndex ); + bChangeModelBack = true; + } + + int iRetVal = BaseClass::DrawModel( flags ); + + if( bChangeModelBack ) + SetModelIndex( iOriginalIndex ); + + return iRetVal; +} + +bool CWeaponPortalBase::ShouldDraw( void ) +{ + if ( !GetOwner() || GetOwner() != C_BasePlayer::GetLocalPlayer() ) + return true; + + if ( !IsActiveByLocalPlayer() ) + return false; + + //if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() && materials->GetRenderTarget() == 0 ) + // return false; + + return true; +} + +bool CWeaponPortalBase::ShouldPredict() +{ + if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) + return true; + + return BaseClass::ShouldPredict(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the weapon's crosshair +//----------------------------------------------------------------------------- +void CWeaponPortalBase::DrawCrosshair() +{ + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( !player ) + return; + + Color clr = gHUD.m_clrNormal; + + CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair ); + if ( !crosshair ) + return; + + // Check to see if the player is in VGUI mode... + if (player->IsInVGuiInputMode()) + { + CHudTexture *pArrow = gHUD.GetIcon( "arrow" ); + + crosshair->SetCrosshair( pArrow, gHUD.m_clrNormal ); + return; + } + + // Find out if this weapon's auto-aimed onto a target + bool bOnTarget = ( m_iState == WEAPON_IS_ONTARGET ); + + if ( player->GetFOV() >= 90 ) + { + // normal crosshairs + if ( bOnTarget && GetWpnData().iconAutoaim ) + { + clr[3] = 255; + + crosshair->SetCrosshair( GetWpnData().iconAutoaim, clr ); + } + else if ( GetWpnData().iconCrosshair ) + { + clr[3] = 255; + crosshair->SetCrosshair( GetWpnData().iconCrosshair, clr ); + } + else + { + crosshair->ResetCrosshair(); + } + } + else + { + Color white( 255, 255, 255, 255 ); + + // zoomed crosshairs + if (bOnTarget && GetWpnData().iconZoomedAutoaim) + crosshair->SetCrosshair(GetWpnData().iconZoomedAutoaim, white); + else if ( GetWpnData().iconZoomedCrosshair ) + crosshair->SetCrosshair( GetWpnData().iconZoomedCrosshair, white ); + else + crosshair->ResetCrosshair(); + } +} + +void CWeaponPortalBase::DoAnimationEvents( CStudioHdr *pStudioHdr ) +{ + // HACK: Because this model renders view and world models in the same frame + // it's using the wrong studio model when checking the sequences. + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); + if ( pPlayer && pPlayer->GetActiveWeapon() == this ) + { + C_BaseViewModel *pViewModel = pPlayer->GetViewModel(); + if ( pViewModel ) + { + pStudioHdr = pViewModel->GetModelPtr(); + } + } + + if ( pStudioHdr ) + { + BaseClass::DoAnimationEvents( pStudioHdr ); + } +} + +void CWeaponPortalBase::GetRenderBounds( Vector& theMins, Vector& theMaxs ) +{ + if ( IsRagdoll() ) + { + m_pRagdoll->GetRagdollBounds( theMins, theMaxs ); + } + else if ( GetModel() ) + { + CStudioHdr *pStudioHdr = NULL; + + // HACK: Because this model renders view and world models in the same frame + // it's using the wrong studio model when checking the sequences. + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); + if ( pPlayer && pPlayer->GetActiveWeapon() == this ) + { + C_BaseViewModel *pViewModel = pPlayer->GetViewModel(); + if ( pViewModel ) + { + pStudioHdr = pViewModel->GetModelPtr(); + } + } + else + { + pStudioHdr = GetModelPtr(); + } + + if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() || GetSequence() == -1 ) + { + theMins = vec3_origin; + theMaxs = vec3_origin; + return; + } + if (!VectorCompare( vec3_origin, pStudioHdr->view_bbmin() ) || !VectorCompare( vec3_origin, pStudioHdr->view_bbmax() )) + { + // clipping bounding box + VectorCopy ( pStudioHdr->view_bbmin(), theMins); + VectorCopy ( pStudioHdr->view_bbmax(), theMaxs); + } + else + { + // movement bounding box + VectorCopy ( pStudioHdr->hull_min(), theMins); + VectorCopy ( pStudioHdr->hull_max(), theMaxs); + } + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( GetSequence() ); + VectorMin( seqdesc.bbmin, theMins, theMins ); + VectorMax( seqdesc.bbmax, theMaxs, theMaxs ); + } + else + { + theMins = vec3_origin; + theMaxs = vec3_origin; + } +} + + +#else + +void CWeaponPortalBase::Spawn() +{ + BaseClass::Spawn(); + + // Set this here to allow players to shoot dropped weapons + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + // Use less bloat for the collision box for this weapon. (bug 43800) + CollisionProp()->UseTriggerBounds( true, 20 ); +} + +void CWeaponPortalBase:: Materialize( void ) +{ + if ( IsEffectActive( EF_NODRAW ) ) + { + // changing from invisible state to visible. + EmitSound( "AlyxEmp.Charge" ); + + RemoveEffects( EF_NODRAW ); + DoMuzzleFlash(); + } + + if ( HasSpawnFlags( SF_NORESPAWN ) == false ) + { + VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ); + SetMoveType( MOVETYPE_VPHYSICS ); + + //PortalRules()->AddLevelDesignerPlacedObject( this ); + } + + if ( HasSpawnFlags( SF_NORESPAWN ) == false ) + { + if ( GetOriginalSpawnOrigin() == vec3_origin ) + { + m_vOriginalSpawnOrigin = GetAbsOrigin(); + m_vOriginalSpawnAngles = GetAbsAngles(); + } + } + + SetPickupTouch(); + + SetThink (NULL); +} + +#endif + +const CPortalSWeaponInfo &CWeaponPortalBase::GetPortalWpnData() const +{ + const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); + const CPortalSWeaponInfo *pPortalInfo; + + #ifdef _DEBUG + pPortalInfo = dynamic_cast< const CPortalSWeaponInfo* >( pWeaponInfo ); + Assert( pPortalInfo ); + #else + pPortalInfo = static_cast< const CPortalSWeaponInfo* >( pWeaponInfo ); + #endif + + return *pPortalInfo; +} +void CWeaponPortalBase::FireBullets( const FireBulletsInfo_t &info ) +{ + FireBulletsInfo_t modinfo = info; + + modinfo.m_iPlayerDamage = GetPortalWpnData().m_iPlayerDamage; + + BaseClass::FireBullets( modinfo ); +} + + +#if defined( CLIENT_DLL ) + +#include "c_te_effect_dispatch.h" + +#define NUM_MUZZLE_FLASH_TYPES 4 + +bool CWeaponPortalBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) +{ + return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); +} + + +void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ) +{ + QAngle final = in + punch; + + //Clip each component + for ( int i = 0; i < 3; i++ ) + { + if ( final[i] > clip[i] ) + { + final[i] = clip[i]; + } + else if ( final[i] < -clip[i] ) + { + final[i] = -clip[i]; + } + + //Return the result + in[i] = final[i] - punch[i]; + } +} + +#endif + diff --git a/game/shared/portal/weapon_portalbase.h b/game/shared/portal/weapon_portalbase.h new file mode 100644 index 0000000..28c4fee --- /dev/null +++ b/game/shared/portal/weapon_portalbase.h @@ -0,0 +1,132 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_PORTALBASE_H +#define WEAPON_PORTALBASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basecombatweapon_shared.h" +#include "portal_weapon_parse.h" + +#if defined( CLIENT_DLL ) + #define CWeaponPortalBase C_WeaponPortalBase + void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ); +#endif + +class CPortal_Player; + +// These are the names of the ammo types that go in the CAmmoDefs and that the +// weapon script files reference. + +// Given an ammo type (like from a weapon's GetPrimaryAmmoType()), this compares it +// against the ammo name you specify. +// MIKETODO: this should use indexing instead of searching and strcmp()'ing all the time. +bool IsAmmoType( int iAmmoType, const char *pAmmoName ); + +typedef enum +{ + WEAPON_NONE = 0, + + //Melee + WEAPON_CROWBAR, + + //Special + WEAPON_PORTALGUN, + WEAPON_PHYSCANNON, + + //Pistols + WEAPON_PISTOL, + WEAPON_357, + + //Machineguns + WEAPON_SMG, + WEAPON_AR2, + + //Grenades + WEAPON_FRAG, + WEAPON_BUGBAIT, + + //Other + WEAPON_SHOTGUN, + WEAPON_CROSSBOW, + WEAPON_RPG, + + WEAPON_MAX, // number of weapons weapon index + +} PortalWeaponID; + +class CWeaponPortalBase : public CBaseCombatWeapon +{ +public: + DECLARE_CLASS( CWeaponPortalBase, CBaseCombatWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponPortalBase(); + + #ifdef GAME_DLL + DECLARE_DATADESC(); + + void SendReloadSoundEvent( void ); + + void Materialize( void ); + #endif + + // All predicted weapons need to implement and return true + virtual bool IsPredicted() const; + + CBasePlayer* GetPlayerOwner() const; + CPortal_Player* GetPortalPlayerOwner() const; + + // Get specific Portal weapon ID (ie: WEAPON_PORTALGUN, etc) + virtual PortalWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + + void WeaponSound( WeaponSound_t sound_type, float soundtime = 0.0f ); + + CPortalSWeaponInfo const &GetPortalWpnData() const; + + + virtual void FireBullets( const FireBulletsInfo_t &info ); + +public: + #if defined( CLIENT_DLL ) + + virtual int DrawModel( int flags ); + virtual bool ShouldDraw( void ); + virtual bool ShouldDrawCrosshair( void ) { return true; } + virtual bool ShouldPredict(); + virtual void OnDataChanged( DataUpdateType_t type ); + virtual void DrawCrosshair(); + + virtual void DoAnimationEvents( CStudioHdr *pStudio ); + virtual void GetRenderBounds( Vector& theMins, Vector& theMaxs ); + + virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ); + + #else + + virtual void Spawn(); + + #endif + + float m_flPrevAnimTime; + float m_flNextResetCheckTime; + + Vector GetOriginalSpawnOrigin( void ) { return m_vOriginalSpawnOrigin; } + QAngle GetOriginalSpawnAngles( void ) { return m_vOriginalSpawnAngles; } + +private: + + CWeaponPortalBase( const CWeaponPortalBase & ); + + Vector m_vOriginalSpawnOrigin; + QAngle m_vOriginalSpawnAngles; +}; + + +#endif // WEAPON_PORTALBASE_H diff --git a/game/shared/portal/weapon_portalbasecombatweapon.cpp b/game/shared/portal/weapon_portalbasecombatweapon.cpp new file mode 100644 index 0000000..6008e14 --- /dev/null +++ b/game/shared/portal/weapon_portalbasecombatweapon.cpp @@ -0,0 +1,422 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_portalbasecombatweapon.h" + +#include "portal_player_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( baseportalcombatweapon, CBasePortalCombatWeapon ); + +IMPLEMENT_NETWORKCLASS_ALIASED( BasePortalCombatWeapon , DT_BasePortalCombatWeapon ) + +BEGIN_NETWORK_TABLE( CBasePortalCombatWeapon , DT_BasePortalCombatWeapon ) +#if !defined( CLIENT_DLL ) +// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), +#else +// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), +#endif +END_NETWORK_TABLE() + + +#if !defined( CLIENT_DLL ) + +#include "globalstate.h" + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CBasePortalCombatWeapon ) + + DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ), + DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ), + +END_DATADESC() + +#endif + +BEGIN_PREDICTION_DATA( CBasePortalCombatWeapon ) +END_PREDICTION_DATA() + +extern ConVar sk_auto_reload_time; + +CBasePortalCombatWeapon::CBasePortalCombatWeapon( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePortalCombatWeapon::ItemHolsterFrame( void ) +{ + BaseClass::ItemHolsterFrame(); + + // Must be player held + if ( GetOwner() && GetOwner()->IsPlayer() == false ) + return; + + // We can't be active + if ( GetOwner()->GetActiveWeapon() == this ) + return; + + // If it's been longer than three seconds, reload + if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) + { + // Just load the clip with no animations + FinishReload(); + m_flHolsterTime = gpGlobals->curtime; + } +} + +bool CBasePortalCombatWeapon::CanLower() +{ + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Drops the weapon into a lowered pose +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePortalCombatWeapon::Lower( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Brings the weapon up to the ready position +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePortalCombatWeapon::Ready( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = false; + m_flRaiseTime = gpGlobals->curtime + 0.5f; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePortalCombatWeapon::Deploy( void ) +{ + // If we should be lowered, deploy in the lowered position + // We have to ask the player if the last time it checked, the weapon was lowered + if ( GetOwner() && GetOwner()->IsPlayer() ) + { + CPortal_Player *pPlayer = assert_cast<CPortal_Player*>( GetOwner() ); + if ( pPlayer->IsWeaponLowered() ) + { + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE ) + { + if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) ) + { + m_bLowered = true; + + // Stomp the next attack time to fix the fact that the lower idles are long + pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; + return true; + } + } + } + } + + m_bLowered = false; + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePortalCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + if ( BaseClass::Holster( pSwitchingTo ) ) + { + m_flHolsterTime = gpGlobals->curtime; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePortalCombatWeapon::WeaponShouldBeLowered( void ) +{ + // Can't be in the middle of another animation + if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && + GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) + return false; + + if ( m_bLowered ) + return true; + +#if !defined( CLIENT_DLL ) + + if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON ) + return true; + +#endif + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Allows the weapon to choose proper weapon idle animation +//----------------------------------------------------------------------------- +void CBasePortalCombatWeapon::WeaponIdle( void ) +{ + //See if we should idle high or low + if ( WeaponShouldBeLowered() ) + { + // Move to lowered position if we're not there yet + if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED + && GetActivity() != ACT_TRANSITION ) + { + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + // Keep idling low + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + } + else + { + // See if we need to raise immediately + if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + } +} + +#if defined( CLIENT_DLL ) + +#define HL2_BOB_CYCLE_MIN 1.0f +#define HL2_BOB_CYCLE_MAX 0.45f +#define HL2_BOB 0.002f +#define HL2_BOB_UP 0.5f + +extern float g_lateralBob; +extern float g_verticalBob; + +static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); +static ConVar cl_bob( "cl_bob","0.002" ); +static ConVar cl_bobup( "cl_bobup","0.5" ); + +// Register these cvars if needed for easy tweaking +static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CBasePortalCombatWeapon::CalcViewmodelBob( void ) +{ + static float bobtime; + static float lastbobtime; + float cycle; + + CBasePlayer *player = ToBasePlayer( GetOwner() ); + //Assert( player ); + + //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it + + if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) + { + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f;// just use old value + } + + //Find the speed of the player + float speed = player->GetLocalVelocity().Length2D(); + + //FIXME: This maximum speed value must come from the server. + // MaxSpeed() is not sufficient for dealing with sprinting - jdw + + speed = clamp( speed, -320, 320 ); + + float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); + + bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; + lastbobtime = gpGlobals->curtime; + + //Calculate the vertical bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX; + cycle /= HL2_BOB_CYCLE_MAX; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_verticalBob = speed*0.005f; + g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); + + g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); + + //Calculate the lateral bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2; + cycle /= HL2_BOB_CYCLE_MAX*2; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_lateralBob = speed*0.005f; + g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); + g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBasePortalCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ + Vector forward, right; + AngleVectors( angles, &forward, &right, NULL ); + + CalcViewmodelBob(); + + // Apply bob, but scaled down to 40% + VectorMA( origin, g_verticalBob * 0.1f, forward, origin ); + + // Z bob a bit more + origin[2] += g_verticalBob * 0.1f; + + // bob the angles + angles[ ROLL ] += g_verticalBob * 0.5f; + angles[ PITCH ] -= g_verticalBob * 0.4f; + + angles[ YAW ] -= g_lateralBob * 0.3f; + + VectorMA( origin, g_lateralBob * 0.8f, right, origin ); +} + +//----------------------------------------------------------------------------- +Vector CBasePortalCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetBulletSpread( proficiency ); +} + +//----------------------------------------------------------------------------- +float CBasePortalCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetSpreadBias( proficiency ); +} +//----------------------------------------------------------------------------- + +const WeaponProficiencyInfo_t *CBasePortalCombatWeapon::GetProficiencyValues() +{ + return NULL; +} + +#else + +// Server stubs +float CBasePortalCombatWeapon::CalcViewmodelBob( void ) +{ + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBasePortalCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ +} + + +//----------------------------------------------------------------------------- +Vector CBasePortalCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + Vector baseSpread = BaseClass::GetBulletSpread( proficiency ); + + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + float flModifier = (pProficiencyValues)[ proficiency ].spreadscale; + return ( baseSpread * flModifier ); +} + +//----------------------------------------------------------------------------- +float CBasePortalCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + return (pProficiencyValues)[ proficiency ].bias; +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBasePortalCombatWeapon::GetProficiencyValues() +{ + return GetDefaultProficiencyValues(); +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBasePortalCombatWeapon::GetDefaultProficiencyValues() +{ + // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!! + static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] = + { + { 2.50, 1.0 }, + { 2.00, 1.0 }, + { 1.50, 1.0 }, + { 1.25, 1.0 }, + { 1.00, 1.0 }, + }; + + COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); + + return g_BaseWeaponProficiencyTable; +} + +#endif
\ No newline at end of file diff --git a/game/shared/portal/weapon_portalbasecombatweapon.h b/game/shared/portal/weapon_portalbasecombatweapon.h new file mode 100644 index 0000000..9811308 --- /dev/null +++ b/game/shared/portal/weapon_portalbasecombatweapon.h @@ -0,0 +1,69 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifdef CLIENT_DLL + #include "c_portal_player.h" +#else + #include "portal_player.h" +#endif + +#include "weapon_portalbase.h" + +#ifndef WEAPON_BASEPORTALCOMBATWEAPON_SHARED_H +#define WEAPON_BASEPORTALCOMBATWEAPON_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + +#if defined( CLIENT_DLL ) +#define CBasePortalCombatWeapon C_BasePortalCombatWeapon +#endif + +class CBasePortalCombatWeapon : public CWeaponPortalBase +{ +#if !defined( CLIENT_DLL ) + DECLARE_DATADESC(); +#endif + + DECLARE_CLASS( CBasePortalCombatWeapon, CWeaponPortalBase ); +public: + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CBasePortalCombatWeapon(); + + virtual bool WeaponShouldBeLowered( void ); + + bool CanLower( void ); + virtual bool Ready( void ); + virtual bool Lower( void ); + virtual bool Deploy( void ); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual void WeaponIdle( void ); + + virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ); + virtual float CalcViewmodelBob( void ); + + virtual Vector GetBulletSpread( WeaponProficiency_t proficiency ); + virtual float GetSpreadBias( WeaponProficiency_t proficiency ); + + virtual const WeaponProficiencyInfo_t *GetProficiencyValues(); + static const WeaponProficiencyInfo_t *GetDefaultProficiencyValues(); + + virtual void ItemHolsterFrame( void ); + +protected: + + bool m_bLowered; // Whether the viewmodel is raised or lowered + float m_flRaiseTime; // If lowered, the time we should raise the viewmodel + float m_flHolsterTime; // When the weapon was holstered + +private: + + CBasePortalCombatWeapon( const CBasePortalCombatWeapon & ); +}; + +#endif // WEAPON_BASEPORTALCOMBATWEAPON_SHARED_H diff --git a/game/shared/portal/weapon_portalgun_shared.cpp b/game/shared/portal/weapon_portalgun_shared.cpp new file mode 100644 index 0000000..9cbb4f4 --- /dev/null +++ b/game/shared/portal/weapon_portalgun_shared.cpp @@ -0,0 +1,456 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_portalgun_shared.h" +#include "npcevent.h" +#include "in_buttons.h" +#include "rumble_shared.h" + +#include "prop_portal_shared.h" + +#ifdef CLIENT_DLL + #define CWeaponPortalgun C_WeaponPortalgun +#endif //#ifdef CLIENT_DLL + + +acttable_t CWeaponPortalgun::m_acttable[] = +{ + { ACT_MP_STAND_IDLE, ACT_MP_STAND_PRIMARY, false }, + { ACT_MP_RUN, ACT_MP_RUN_PRIMARY, false }, + { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PRIMARY, false }, + { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PRIMARY, false }, + { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PRIMARY, false }, + { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PRIMARY, false }, + { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PRIMARY, false }, + { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PRIMARY, false }, +}; + +IMPLEMENT_ACTTABLE(CWeaponPortalgun); + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CWeaponPortalgun::CWeaponPortalgun( void ) +{ + m_bReloadsSingly = true; + + // TODO: specify these in hammer instead of assuming every gun has blue chip + m_bCanFirePortal1 = true; + m_bCanFirePortal2 = false; + + m_iLastFiredPortal = 0; + m_fCanPlacePortal1OnThisSurface = 1.0f; + m_fCanPlacePortal2OnThisSurface = 1.0f; + + m_fMinRange1 = 0.0f; + m_fMaxRange1 = MAX_TRACE_LENGTH; + m_fMinRange2 = 0.0f; + m_fMaxRange2 = MAX_TRACE_LENGTH; + + m_EffectState = (int)EFFECT_NONE; +} + +void CWeaponPortalgun::Precache() +{ + BaseClass::Precache(); + + PrecacheModel( PORTALGUN_BEAM_SPRITE ); + PrecacheModel( PORTALGUN_BEAM_SPRITE_NOZ ); + + PrecacheModel( "models/portals/portal1.mdl" ); + PrecacheModel( "models/portals/portal2.mdl" ); + + PrecacheScriptSound( "Portal.ambient_loop" ); + + PrecacheScriptSound( "Portal.open_blue" ); + PrecacheScriptSound( "Portal.open_red" ); + PrecacheScriptSound( "Portal.close_blue" ); + PrecacheScriptSound( "Portal.close_red" ); + PrecacheScriptSound( "Portal.fizzle_moved" ); + PrecacheScriptSound( "Portal.fizzle_invalid_surface" ); + PrecacheScriptSound( "Weapon_Portalgun.powerup" ); + PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" ); + +#ifndef CLIENT_DLL + PrecacheParticleSystem( "portal_1_projectile_stream" ); + PrecacheParticleSystem( "portal_1_projectile_stream_pedestal" ); + PrecacheParticleSystem( "portal_2_projectile_stream" ); + PrecacheParticleSystem( "portal_2_projectile_stream_pedestal" ); + PrecacheParticleSystem( "portal_1_charge" ); + PrecacheParticleSystem( "portal_2_charge" ); +#endif +} + +PRECACHE_WEAPON_REGISTER(weapon_portalgun); + +bool CWeaponPortalgun::ShouldDrawCrosshair( void ) +{ + return true;//( m_fCanPlacePortal1OnThisSurface > 0.5f || m_fCanPlacePortal2OnThisSurface > 0.5f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Override so only reload one shell at a time +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CWeaponPortalgun::Reload( void ) +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Play finish reload anim and fill clip +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponPortalgun::FillClip( void ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return; + + // Add them to the clip + if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) + { + if ( Clip1() < GetMaxClip1() ) + { + m_iClip1++; + pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponPortalgun::DryFire( void ) +{ + WeaponSound(EMPTY); + SendWeaponAnim( ACT_VM_DRYFIRE ); + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +void CWeaponPortalgun::SetCanFirePortal1( bool bCanFire /*= true*/ ) +{ + m_bCanFirePortal1 = bCanFire; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if ( !m_bOpenProngs ) + { + DoEffect( EFFECT_HOLDING ); + DoEffect( EFFECT_READY ); + } + + // TODO: Remove muzzle flash when there's an upgrade animation + pOwner->DoMuzzleFlash(); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + 0.25f; + m_flNextSecondaryAttack = gpGlobals->curtime + 0.25f; + + // player "shoot" animation + pOwner->SetAnimation( PLAYER_ATTACK1 ); + + pOwner->ViewPunch( QAngle( random->RandomFloat( -1, -0.5f ), random->RandomFloat( -1, 1 ), 0 ) ); + + EmitSound( "Weapon_Portalgun.powerup" ); +} + +void CWeaponPortalgun::SetCanFirePortal2( bool bCanFire /*= true*/ ) +{ + m_bCanFirePortal2 = bCanFire; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if ( !m_bOpenProngs ) + { + DoEffect( EFFECT_HOLDING ); + DoEffect( EFFECT_READY ); + } + + // TODO: Remove muzzle flash when there's an upgrade animation + pOwner->DoMuzzleFlash(); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f; + + // player "shoot" animation + pOwner->SetAnimation( PLAYER_ATTACK1 ); + + pOwner->ViewPunch( QAngle( random->RandomFloat( -1, -0.5f ), random->RandomFloat( -1, 1 ), 0 ) ); + + EmitSound( "Weapon_Portalgun.powerup" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponPortalgun::PrimaryAttack( void ) +{ + if ( !CanFirePortal1() ) + return; + + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if (!pPlayer) + { + return; + } + +#ifndef CLIENT_DLL + inputdata_t inputdata; + inputdata.pActivator = this; + inputdata.pCaller = this; + inputdata.value;//null + FirePortal1( inputdata ); + m_OnFiredPortal1.FireOutput( pPlayer, this ); + + pPlayer->RumbleEffect( RUMBLE_PORTALGUN_LEFT, 0, RUMBLE_FLAGS_NONE ); +#endif + + pPlayer->DoMuzzleFlash(); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;//SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;//SequenceDuration(); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + pPlayer->ViewPunch( QAngle( random->RandomFloat( -1, -0.5f ), random->RandomFloat( -1, 1 ), 0 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponPortalgun::SecondaryAttack( void ) +{ + if ( !CanFirePortal2() ) + return; + + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if (!pPlayer) + { + return; + } + +#ifndef CLIENT_DLL + inputdata_t inputdata; + inputdata.pActivator = this; + inputdata.pCaller = this; + inputdata.value;//null + FirePortal2( inputdata ); + m_OnFiredPortal2.FireOutput( pPlayer, this ); + pPlayer->RumbleEffect( RUMBLE_PORTALGUN_RIGHT, 0, RUMBLE_FLAGS_NONE ); +#endif + + pPlayer->DoMuzzleFlash(); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;//SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;//SequenceDuration(); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + pPlayer->ViewPunch( QAngle( random->RandomFloat( -1, -0.5f ), random->RandomFloat( -1, 1 ), 0 ) ); +} + +void CWeaponPortalgun::DelayAttack( float fDelay ) +{ + m_flNextPrimaryAttack = gpGlobals->curtime + fDelay; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPortalgun::ItemHolsterFrame( void ) +{ + // Must be player held + if ( GetOwner() && GetOwner()->IsPlayer() == false ) + return; + + // We can't be active + if ( GetOwner()->GetActiveWeapon() == this ) + return; + + // If it's been longer than three seconds, reload + if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) + { + // Reset the timer + m_flHolsterTime = gpGlobals->curtime; + + if ( GetOwner() == NULL ) + return; + + if ( m_iClip1 == GetMaxClip1() ) + return; + + // Just load the clip with no animations + int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) ); + + GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() ); + m_iClip1 += ammoFill; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPortalgun::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + DestroyEffects(); + + return BaseClass::Holster( pSwitchingTo ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPortalgun::Deploy( void ) +{ + DoEffect( EFFECT_READY ); + + bool bReturn = BaseClass::Deploy(); + + m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner ) + { + pOwner->SetNextAttack( gpGlobals->curtime ); + +#ifndef CLIENT_DLL + if( GameRules()->IsMultiplayer() ) + { + m_iPortalLinkageGroupID = pOwner->entindex(); + + Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) ); + } +#endif + } + + return bReturn; +} + +void CWeaponPortalgun::WeaponIdle( void ) +{ + //See if we should idle high or low + if ( WeaponShouldBeLowered() ) + { + // Move to lowered position if we're not there yet + if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED + && GetActivity() != ACT_TRANSITION ) + { + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + // Keep idling low + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + } + else + { + // See if we need to raise immediately + if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPortalgun::StopEffects( bool stopSound ) +{ + // Turn off our effect state + DoEffect( EFFECT_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : effectType - +//----------------------------------------------------------------------------- +void CWeaponPortalgun::DoEffect( int effectType, Vector *pos ) +{ + m_EffectState = effectType; + +#ifdef CLIENT_DLL + // Save predicted state + m_nOldEffectState = m_EffectState; +#endif + + switch( effectType ) + { + case EFFECT_READY: + DoEffectReady(); + break; + + case EFFECT_HOLDING: + DoEffectHolding(); + break; + + default: + case EFFECT_NONE: + DoEffectNone(); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Restore +//----------------------------------------------------------------------------- +void CWeaponPortalgun::OnRestore() +{ + BaseClass::OnRestore(); + + // Portalgun effects disappear through level transition, so + // just recreate any effects here + if ( m_EffectState != EFFECT_NONE ) + { + DoEffect( m_EffectState, NULL ); + } +} + + +//----------------------------------------------------------------------------- +// On Remove +//----------------------------------------------------------------------------- +void CWeaponPortalgun::UpdateOnRemove(void) +{ + DestroyEffects(); + BaseClass::UpdateOnRemove(); +} diff --git a/game/shared/portal/weapon_portalgun_shared.h b/game/shared/portal/weapon_portalgun_shared.h new file mode 100644 index 0000000..88ee0af --- /dev/null +++ b/game/shared/portal/weapon_portalgun_shared.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WEAPON_PORTALGUN_SHARED_H +#define WEAPON_PORTALGUN_SHARED_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" + +#ifdef CLIENT_DLL +#include "c_weapon_portalgun.h" +#else +#include "weapon_portalgun.h" +#endif + +#define PORTALGUN_BEAM_SPRITE "sprites/grav_beam.vmt" +#define PORTALGUN_BEAM_SPRITE_NOZ "sprites/grav_beam_noz.vmt" +#define PORTALGUN_GLOW_SPRITE "sprites/glow04_noz" +#define PORTALGUN_ENDCAP_SPRITE "sprites/grav_flare" +#define PORTALGUN_GRAV_ACTIVE_GLOW "sprites/grav_light" +#define PORTALGUN_PORTAL1_FIRED_LAST_GLOW "sprites/bluelight" +#define PORTALGUN_PORTAL2_FIRED_LAST_GLOW "sprites/orangelight" +#define PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE "sprites/portalgun_effects" +#define PORTALGUN_PORTAL_TUBE_BEAM_SPRITE "sprites/portalgun_effects" + +enum +{ + EFFECT_NONE, + EFFECT_READY, + EFFECT_HOLDING, +}; + +extern ConVar sk_auto_reload_time; + +#endif // WEAPON_PORTALGUN_SHARED_H |