summaryrefslogtreecommitdiff
path: root/game/shared/portal
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/portal
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/portal')
-rw-r--r--game/shared/portal/PortalSimulation.cpp3322
-rw-r--r--game/shared/portal/PortalSimulation.h481
-rw-r--r--game/shared/portal/StaticCollisionPolyhedronCache.cpp485
-rw-r--r--game/shared/portal/StaticCollisionPolyhedronCache.h52
-rw-r--r--game/shared/portal/achievements_portal.cpp547
-rw-r--r--game/shared/portal/env_lightrail_endpoint_shared.h69
-rw-r--r--game/shared/portal/env_portal_path_track_shared.h79
-rw-r--r--game/shared/portal/portal_collideable_enumerator.cpp103
-rw-r--r--game/shared/portal/portal_collideable_enumerator.h41
-rw-r--r--game/shared/portal/portal_game_account.h21
-rw-r--r--game/shared/portal/portal_game_account_client.h25
-rw-r--r--game/shared/portal/portal_gamemovement.cpp752
-rw-r--r--game/shared/portal/portal_gamerules.cpp1284
-rw-r--r--game/shared/portal/portal_gamerules.h116
-rw-r--r--game/shared/portal/portal_gcmessages.cpp28
-rw-r--r--game/shared/portal/portal_gcmessages.h28
-rw-r--r--game/shared/portal/portal_gcschema.cpp78
-rw-r--r--game/shared/portal/portal_gcschema.h80
-rw-r--r--game/shared/portal/portal_gcschema.sch38
-rw-r--r--game/shared/portal/portal_mp_gamerules.cpp1283
-rw-r--r--game/shared/portal/portal_mp_gamerules.h173
-rw-r--r--game/shared/portal/portal_player_shared.cpp926
-rw-r--r--game/shared/portal/portal_player_shared.h38
-rw-r--r--game/shared/portal/portal_playeranimstate.cpp310
-rw-r--r--game/shared/portal/portal_playeranimstate.h85
-rw-r--r--game/shared/portal/portal_shareddefs.cpp13
-rw-r--r--game/shared/portal/portal_shareddefs.h79
-rw-r--r--game/shared/portal/portal_usermessages.cpp64
-rw-r--r--game/shared/portal/portal_util_shared.cpp1774
-rw-r--r--game/shared/portal/portal_util_shared.h103
-rw-r--r--game/shared/portal/portal_weapon_parse.cpp32
-rw-r--r--game/shared/portal/portal_weapon_parse.h35
-rw-r--r--game/shared/portal/prop_portal_shared.cpp80
-rw-r--r--game/shared/portal/prop_portal_shared.h49
-rw-r--r--game/shared/portal/weapon_portalbase.cpp442
-rw-r--r--game/shared/portal/weapon_portalbase.h132
-rw-r--r--game/shared/portal/weapon_portalbasecombatweapon.cpp422
-rw-r--r--game/shared/portal/weapon_portalbasecombatweapon.h69
-rw-r--r--game/shared/portal/weapon_portalgun_shared.cpp456
-rw-r--r--game/shared/portal/weapon_portalgun_shared.h42
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, &params );
+
+ 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, &params );
+ 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, &params );
+
+ 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, &params );
+
+ 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, &params );
+ 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, &params );
+ 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