//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//
#include "cbase.h"
#include "portal_placement.h"
#include "portal_shareddefs.h"
#include "prop_portal_shared.h"
#include "func_noportal_volume.h"
#include "BasePropDoor.h"
#include "collisionutils.h"
#include "decals.h"
#include "physicsshadowclone.h"


#define MAXIMUM_BUMP_DISTANCE ( ( PORTAL_HALF_WIDTH * 2.0f ) * ( PORTAL_HALF_WIDTH * 2.0f ) + ( PORTAL_HALF_HEIGHT * 2.0f ) * ( PORTAL_HALF_HEIGHT * 2.0f ) ) / 2.0f


struct CPortalCornerFitData
{
	trace_t trCornerTrace;
	Vector ptIntersectionPoint;
	Vector vIntersectionDirection;
	Vector vBumpDirection;
	bool bCornerIntersection;
	bool bSoftBump;
};


CUtlVector<CBaseEntity *> g_FuncBumpingEntityList;
bool g_bBumpedByLinkedPortal;


ConVar sv_portal_placement_debug ("sv_portal_placement_debug", "0", FCVAR_REPLICATED );
ConVar sv_portal_placement_never_bump ("sv_portal_placement_never_bump", "0", FCVAR_REPLICATED | FCVAR_CHEAT );


bool IsMaterialInList( const csurface_t &surface, char *g_ppszMaterials[] )
{
	char szLowerName[ 256 ];
	Q_strcpy( szLowerName, surface.name );
	Q_strlower( szLowerName );

	int iMaterial = 0;

	while ( g_ppszMaterials[ iMaterial ] )
	{
		if ( Q_strstr( szLowerName, g_ppszMaterials[ iMaterial ] ) )
			return true;

		++iMaterial;
	}

	return false;
}

bool IsNoPortalMaterial( const csurface_t &surface )
{
	if ( surface.flags & SURF_NOPORTAL )
		return true;

	const surfacedata_t *pdata = physprops->GetSurfaceData( surface.surfaceProps );
	if ( pdata->game.material == CHAR_TEX_GLASS )
		return true;

	// Skipping all studio models
	if ( StringHasPrefix( surface.name, "**studio**" ) )
		return true;

	return false;
}

bool IsPassThroughMaterial( const csurface_t &surface )
{
	if ( surface.flags & SURF_SKY )
		return true;

	if ( IsMaterialInList( surface, g_ppszPortalPassThroughMaterials ) )
		return true;

	return false;
}


void TracePortals( const CProp_Portal *pIgnorePortal, const Vector &vForward, const Vector &vStart, const Vector &vEnd, trace_t &tr )
{
	UTIL_ClearTrace( tr );

	Ray_t ray;
	ray.Init( vStart, vEnd );

	trace_t trTemp;

	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 != pIgnorePortal && pTempPortal->m_bActivated )
			{
				Vector vOtherOrigin = pTempPortal->GetAbsOrigin();
				QAngle qOtherAngles = pTempPortal->GetAbsAngles();

				Vector vLinkedForward;
				AngleVectors( qOtherAngles, &vLinkedForward, NULL, NULL );

				// If they're not on the same face then don't worry about overlap
				if ( vForward.Dot( vLinkedForward ) < 0.95f )
					continue;

				UTIL_IntersectRayWithPortalOBBAsAABB( pTempPortal, ray, &trTemp );

				if ( trTemp.fraction < 1.0f && trTemp.fraction < tr.fraction )
				{
					tr = trTemp;
				}
			}
		}
	}
}

bool TraceBumpingEntities( const Vector &vStart, const Vector &vEnd, trace_t &tr )
{
	UTIL_ClearTrace( tr );

	// We use this so portal bumpers can't squeeze a portal into not fitting
	bool bClosestIsSoftBumper = false;

	// Trace to the surface to see if there's a rotating door in the way
	CBaseEntity *list[1024];

	Ray_t ray;
	ray.Init( vStart, vEnd );

	int nCount = UTIL_EntitiesAlongRay( list, 1024, ray, 0 );

	for ( int i = 0; i < nCount; i++ )
	{
		trace_t trTemp;
		UTIL_ClearTrace( trTemp );

		bool bSoftBumper = false;

		if ( FClassnameIs( list[i], "func_portal_bumper" ) )
		{
			bSoftBumper = true;
			enginetrace->ClipRayToEntity( ray, MASK_ALL, list[i], &trTemp );
			if ( trTemp.startsolid )
			{
				trTemp.fraction = 1.0f;
			}
		}
		else if ( FClassnameIs( list[i], "trigger_portal_cleanser" ) )
		{
			enginetrace->ClipRayToEntity( ray, MASK_ALL, list[i], &trTemp );
			if ( trTemp.startsolid )
			{
				trTemp.fraction = 1.0f;
			}
		}
		else if ( FClassnameIs( list[i], "func_noportal_volume" ) )
		{
			if ( static_cast<CFuncNoPortalVolume*>( list[i] )->IsActive() )
			{
				enginetrace->ClipRayToEntity( ray, MASK_ALL, list[i], &trTemp );

				// Bump by an extra 2 units so that the portal isn't touching the no portal volume
				Vector vDelta = trTemp.endpos - trTemp.startpos;
				float fLength = VectorNormalize( vDelta ) - 2.0f;
				if ( fLength < 0.0f )
					fLength = 0.0f;
				trTemp.fraction = fLength / ray.m_Delta.Length();
				trTemp.endpos = trTemp.startpos + vDelta * fLength;
			}
			else
				trTemp.fraction = 1.0f;
		}
		else if( FClassnameIs( list[i], "prop_door_rotating" ) )
		{
			// Check more precise door collision
			CBasePropDoor *pRotatingDoor = static_cast<CBasePropDoor *>( list[i] );

			pRotatingDoor->TestCollision( ray, 0, trTemp );
		}

		// If this is the closest and has only bumped once (for soft bumpers)
		if ( trTemp.fraction < tr.fraction && ( !bSoftBumper || !g_FuncBumpingEntityList.HasElement( list[i] ) ) )
		{
			tr = trTemp;
			bClosestIsSoftBumper = bSoftBumper;
		}
	}

	return bClosestIsSoftBumper;
}

bool TracePortalCorner( const CProp_Portal *pIgnorePortal, const Vector &vOrigin, const Vector &vCorner, const Vector &vForward, int iPlacedBy, ITraceFilter *pTraceFilterPortalShot, trace_t &tr, bool &bSoftBump )
{
	Vector vOriginToCorner = vCorner - vOrigin;

	// Check for surface edge
	trace_t trSurfaceEdge;
	UTIL_TraceLine( vOrigin - vForward, vCorner - vForward, MASK_SHOT_PORTAL, pTraceFilterPortalShot, &trSurfaceEdge );

	if ( trSurfaceEdge.startsolid )
	{
		float fTotalFraction = trSurfaceEdge.fractionleftsolid;

		while ( trSurfaceEdge.startsolid && trSurfaceEdge.fractionleftsolid > 0.0f && fTotalFraction < 1.0f )
		{
			UTIL_TraceLine( vOrigin + vOriginToCorner * ( fTotalFraction + 0.05f ) - vForward, vCorner + vOriginToCorner * ( fTotalFraction + 0.05f ) - vForward, MASK_SHOT_PORTAL, pTraceFilterPortalShot, &trSurfaceEdge );

			if ( trSurfaceEdge.startsolid )
			{
				fTotalFraction += trSurfaceEdge.fractionleftsolid + 0.05f;
			}
		}

		if ( fTotalFraction < 1.0f )
		{
			UTIL_TraceLine( vOrigin + vOriginToCorner * ( fTotalFraction + 0.05f ) - vForward, vOrigin - vForward, MASK_SHOT_PORTAL, pTraceFilterPortalShot, &trSurfaceEdge );

			if ( trSurfaceEdge.startsolid )
			{
				trSurfaceEdge.fraction = 1.0f;
			}
			else
			{
				trSurfaceEdge.fraction = fTotalFraction;
				trSurfaceEdge.plane.normal = -trSurfaceEdge.plane.normal;
			}
		}
		else
		{
			trSurfaceEdge.fraction = 1.0f;
		}
	}
	else
	{
		trSurfaceEdge.fraction = 1.0f;
	}

	// Check for enclosing wall
	trace_t trEnclosingWall;
	UTIL_TraceLine( vOrigin + vForward, vCorner + vForward, MASK_SOLID_BRUSHONLY|CONTENTS_MONSTER, pTraceFilterPortalShot, &trEnclosingWall );

	if ( trSurfaceEdge.fraction < trEnclosingWall.fraction )
	{
		trEnclosingWall.fraction = trSurfaceEdge.fraction;
		trEnclosingWall.plane.normal = trSurfaceEdge.plane.normal;
	}

	trace_t trPortal;
	trace_t trBumpingEntity;

	if ( iPlacedBy != PORTAL_PLACED_BY_FIXED )
		TracePortals( pIgnorePortal, vForward, vOrigin + vForward, vCorner + vForward, trPortal );
	else
		UTIL_ClearTrace( trPortal );

	bool bSoftBumper = TraceBumpingEntities( vOrigin + vForward, vCorner + vForward, trBumpingEntity );

	if ( trEnclosingWall.fraction >= 1.0f && trPortal.fraction >= 1.0f && trBumpingEntity.fraction >= 1.0f )
	{
		UTIL_ClearTrace( tr );
		return false;
	}

	if ( trEnclosingWall.fraction <= trPortal.fraction && trEnclosingWall.fraction <= trBumpingEntity.fraction )
	{
		tr = trEnclosingWall;
		bSoftBump = false;
	}
	else if ( trPortal.fraction <= trEnclosingWall.fraction && trPortal.fraction <= trBumpingEntity.fraction )
	{
		tr = trPortal;
		g_bBumpedByLinkedPortal = true;
		bSoftBump = false;
	}
	else if ( !trBumpingEntity.startsolid && trBumpingEntity.fraction <= trEnclosingWall.fraction && trBumpingEntity.fraction <= trPortal.fraction )
	{
		tr = trBumpingEntity;
		bSoftBump = bSoftBumper;
	}
	else
	{
		UTIL_ClearTrace( tr );
		return false;
	}

	return true;
}

Vector FindBumpVectorInCorner( const Vector &ptCorner1, const Vector &ptCorner2, const Vector &ptIntersectionPoint1, const Vector &ptIntersectionPoint2, const Vector &vIntersectionDirection1, const Vector &vIntersectionDirection2, const Vector &vIntersectionBumpDirection1, const Vector &vIntersectionBumpDirection2 )
{
	Vector ptClosestSegment1, ptClosestSegment2;
	float fT1, fT2;

	CalcLineToLineIntersectionSegment( ptIntersectionPoint1, ptIntersectionPoint1 + vIntersectionDirection1, 
		ptIntersectionPoint2, ptIntersectionPoint2 + vIntersectionDirection2,
		&ptClosestSegment1, &ptClosestSegment2, &fT1, &fT2 );

	Vector ptLineIntersection = ( ptClosestSegment1 + ptClosestSegment2 ) * 0.5f;

	// The 2 corner trace intersections and the intersection of those lines makes a triangle.
	// We want to make a similar triangle where the base is large enough to fit the edge of the portal

	// Get the the small triangle's legs and leg lengths
	Vector vShortLeg = ptIntersectionPoint1 - ptLineIntersection;
	Vector vShortLeg2 = ptIntersectionPoint2 - ptLineIntersection;

	float fShortLegLength = vShortLeg.Length();
	float fShortLeg2Length = vShortLeg2.Length();

	if ( fShortLegLength == 0.0f || fShortLeg2Length == 0.0f )
	{
		// FIXME: Our triangle is actually a point or a line, so there's nothing we can do
		return vec3_origin;
	}

	// Normalized legs
	vShortLeg /= fShortLegLength;
	vShortLeg2 /= fShortLeg2Length;

	// Check if corners are aligned with one of the legs
	Vector vCornerToCornerNorm = ptCorner2 - ptCorner1;
	VectorNormalize( vCornerToCornerNorm );

	float fPortalEdgeDotLeg = vCornerToCornerNorm.Dot( vShortLeg );
	float fPortalEdgeDotLeg2 = vCornerToCornerNorm.Dot( vShortLeg2 );

	if ( fPortalEdgeDotLeg < -0.9999f || fPortalEdgeDotLeg > 0.9999f || fPortalEdgeDotLeg2 < -0.9999f || fPortalEdgeDotLeg2 > 0.9999f )
	{
		// Do a one corner bump with corner 1
		float fBumpDistance1 = CalcDistanceToLine( ptCorner1, ptIntersectionPoint1, ptIntersectionPoint1 + vIntersectionDirection1 );

		fBumpDistance1 += PORTAL_BUMP_FORGIVENESS;

		// Do a one corner bump with corner 2
		float fBumpDistance2 = CalcDistanceToLine( ptCorner2, ptIntersectionPoint2, ptIntersectionPoint2 + vIntersectionDirection2 );

		fBumpDistance2 += PORTAL_BUMP_FORGIVENESS;

		return vIntersectionBumpDirection1 * fBumpDistance1 + vIntersectionBumpDirection2 * fBumpDistance2;
	}

	float fLegsDot = vShortLeg.Dot( vShortLeg2 );

	// Need to know if the triangle is pointing toward the portal or away from the portal
	/*bool bPointingTowardPortal = true;

	Vector vLineIntersectionToCornerNorm = ptCorner1 - ptLineIntersection;
	VectorNormalize( vLineIntersectionToCornerNorm );

	if ( vLineIntersectionToCornerNorm.Dot( vShortLeg2 ) < fLegsDot )
	{
	bPointingTowardPortal = false;
	}

	if ( !bPointingTowardPortal )*/
	{
		// Get the small triangle's base length
		float fLongBaseLength = ptCorner1.DistTo( ptCorner2 );

		// Get the large triangle's base length
		float fShortLeg2Angle = acosf( vCornerToCornerNorm.Dot( -vShortLeg ) );
		float fShortBaseAngle = acosf( fLegsDot );
		float fShortLegAngle = M_PI_F - fShortBaseAngle - fShortLeg2Angle;

		if ( sinf( fShortLegAngle ) == 0.0f )
		{
			return Vector( 1000.0f, 1000.0f, 1000.0f );
		}

		float fShortBaseLength = sinf( fShortBaseAngle ) * ( fShortLegLength / sinf( fShortLegAngle ) );

		// Avoid divide by zero
		if ( fShortBaseLength == 0.0f )
		{
			return Vector( 0.0f, 0.0f, 0.0f );
		}

		// Use ratio to get the big triangles leg length
		float fLongLegLength = fLongBaseLength * ( fShortLegLength / fShortBaseLength );

		// Get the relative point on the large triangle
		Vector ptNewCornerPos = ptLineIntersection + vShortLeg * fLongLegLength;

		// Bump by the same amount the corner has to move to fit
		return ptNewCornerPos - ptCorner1;
	}
	/*else
	{
	return Vector( 0.0f, 0.0f, 0.0f );
	}*/
}


bool FitPortalOnSurface( const CProp_Portal *pIgnorePortal, Vector &vOrigin, const Vector &vForward, const Vector &vRight, 
						 const Vector &vTopEdge, const Vector &vBottomEdge, const Vector &vRightEdge, const Vector &vLeftEdge, 
						 int iPlacedBy, ITraceFilter *pTraceFilterPortalShot, 
						 int iRecursions /*= 0*/, const CPortalCornerFitData *pPortalCornerFitData /*= 0*/, const int *p_piIntersectionIndex /*= 0*/, const int *piIntersectionCount /*= 0*/ )
{
	// Don't infinitely recurse
	if ( iRecursions >= 6 )
	{
		return false;
	}

	Vector pptCorner[ 4 ];

	// Get corner points
	pptCorner[ 0 ] = vOrigin + vTopEdge + vLeftEdge;
	pptCorner[ 1 ] = vOrigin + vTopEdge + vRightEdge;
	pptCorner[ 2 ] = vOrigin + vBottomEdge + vLeftEdge;
	pptCorner[ 3 ] = vOrigin + vBottomEdge + vRightEdge;

	// Corner data
	CPortalCornerFitData sFitData[ 4 ];
	int piIntersectionIndex[ 4 ];
	int iIntersectionCount = 0;

	// Gather data we already know
	if ( pPortalCornerFitData )
	{
		for ( int iIntersection = 0; iIntersection < 4; ++iIntersection )
		{
			sFitData[ iIntersection ] = pPortalCornerFitData[ iIntersection ];
		}
	}
	else
	{
		memset( sFitData, 0, sizeof( sFitData ) );
	}

	if ( p_piIntersectionIndex )
	{
		for ( int iIntersection = 0; iIntersection < 4; ++iIntersection )
		{
			piIntersectionIndex[ iIntersection ] = p_piIntersectionIndex[ iIntersection ];
		}
	}
	else
	{
		memset( piIntersectionIndex, 0, sizeof( piIntersectionIndex ) );
	}

	if ( piIntersectionCount )
	{
		iIntersectionCount = *piIntersectionCount;
	}

	int iOldIntersectionCount = iIntersectionCount;

	// Find intersections from center to each corner
	for ( int iIntersection = 0; iIntersection < 4; ++iIntersection )
	{
		// HACK: In weird cases intersection count can go over 3 and index outside of our arrays. Don't let this happen!
		if ( iIntersectionCount < 4 )
		{
			// Don't recompute intersection data that we already have
			if ( !sFitData[ iIntersection ].bCornerIntersection )
			{
				// Test intersection of the current corner
				sFitData[ iIntersection ].bCornerIntersection = TracePortalCorner( pIgnorePortal, vOrigin, pptCorner[ iIntersection ], vForward, iPlacedBy, pTraceFilterPortalShot, sFitData[ iIntersection ].trCornerTrace, sFitData[ iIntersection ].bSoftBump );

				// If the intersection has no normal, ignore it
				if ( sFitData[ iIntersection ].trCornerTrace.plane.normal.IsZero() )
					sFitData[ iIntersection ].bCornerIntersection = false;

				// If it intersected
				if ( sFitData[ iIntersection ].bCornerIntersection )
				{
					sFitData[ iIntersection ].ptIntersectionPoint = vOrigin + ( pptCorner[ iIntersection ] - vOrigin ) * sFitData[ iIntersection ].trCornerTrace.fraction;
					VectorNormalize( sFitData[ iIntersection ].trCornerTrace.plane.normal );
					sFitData[ iIntersection ].vIntersectionDirection = sFitData[ iIntersection ].trCornerTrace.plane.normal.Cross( vForward );
					VectorNormalize( sFitData[ iIntersection ].vIntersectionDirection );
					sFitData[ iIntersection ].vBumpDirection = vForward.Cross( sFitData[ iIntersection ].vIntersectionDirection );
					VectorNormalize( sFitData[ iIntersection ].vBumpDirection );

					piIntersectionIndex[ iIntersectionCount ] = iIntersection;

					if ( sv_portal_placement_debug.GetBool() )
					{
						for ( int iIntersection = 0; iIntersection < 4; ++iIntersection )
						{
							NDebugOverlay::Line( sFitData[ iIntersection ].ptIntersectionPoint - sFitData[ iIntersection ].vIntersectionDirection * 32.0f, 
								sFitData[ iIntersection ].ptIntersectionPoint + sFitData[ iIntersection ].vIntersectionDirection * 32.0f, 
								0, 0, 255, true, 0.5f );
						}
					}

					++iIntersectionCount;
				}
			}
			else
			{
				// We shouldn't be intersecting with any old corners
				sFitData[ iIntersection ].trCornerTrace.fraction = 1.0f;
			}
		}
	}

	for ( int iIntersection = 0; iIntersection < 4; ++iIntersection )
	{
		// Remember soft bumpers so we don't bump with it twice
		if ( sFitData[ iIntersection ].bSoftBump )
		{
			g_FuncBumpingEntityList.AddToTail( sFitData[ iIntersection ].trCornerTrace.m_pEnt );
		}
	}

	// If no new intersections were found then it already fits
	if ( iOldIntersectionCount == iIntersectionCount )
	{
		return true;
	}

	switch ( iIntersectionCount )
	{
	case 0:
		{
			// If no corners intersect it already fits
			return true;
		}
		break;

	case 1:
		{
			float fBumpDistance = CalcDistanceToLine( pptCorner[ piIntersectionIndex[ 0 ] ], 
				sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint, 
				sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint + sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection );

			fBumpDistance += PORTAL_BUMP_FORGIVENESS;

			vOrigin += sFitData[ piIntersectionIndex[ 0 ] ].vBumpDirection * fBumpDistance;

			return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
		}
		break;

	case 2:
		{
			if ( sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint == sFitData[ piIntersectionIndex[ 1 ] ].ptIntersectionPoint )
			{
				return false;
			}

			float fDot = sFitData[ piIntersectionIndex[ 0 ] ].vBumpDirection.Dot( sFitData[ piIntersectionIndex[ 1 ] ].vBumpDirection );

			// If there are parallel intersections try scooting it away from a near wall
			if ( fDot < -0.9f )
			{
				// Check if perpendicular wall is near
				trace_t trPerpWall1;
				bool bSoftBump1;
				bool bDir1 = TracePortalCorner( pIgnorePortal, vOrigin, vOrigin + sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection * PORTAL_HALF_WIDTH * 2.0f, vForward, iPlacedBy, pTraceFilterPortalShot, trPerpWall1, bSoftBump1 );

				trace_t trPerpWall2;
				bool bSoftBump2;
				bool bDir2 = TracePortalCorner( pIgnorePortal, vOrigin, vOrigin + sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection * -PORTAL_HALF_WIDTH * 2.0f, vForward, iPlacedBy, pTraceFilterPortalShot, trPerpWall2, bSoftBump2 );

				// No fit if there's blocking walls on both sides it can't fit
				if ( bDir1 && bDir2 )
				{
					if ( bSoftBump1 )
						bDir1 = false;
					else if ( bSoftBump2 )
						bDir1 = true;
					else
						return false;
				}

				// If there's no assumption to make, just pick a direction.
				if ( !bDir1 && !bDir2 )
				{
					bDir1 = true;
				}

				// Bump the portal
				if ( bDir1 )
				{
					vOrigin += sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection * -PORTAL_HALF_WIDTH;
				}
				else
				{
					vOrigin += sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection * PORTAL_HALF_WIDTH;
				}

				// Prepare data for recursion
				iIntersectionCount = 0;
				sFitData[ piIntersectionIndex[ 0 ] ].bCornerIntersection = false;
				sFitData[ piIntersectionIndex[ 1 ] ].bCornerIntersection = false;

				return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );	
			}

			// If they are the same there's an easy way
			if ( fDot > 0.9f )
			{
				// Get the closest intersection to the portal's center
				int iClosestIntersection = ( ( vOrigin.DistTo( sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint ) < vOrigin.DistTo( sFitData[ piIntersectionIndex[ 1 ] ].ptIntersectionPoint ) ) ? ( 0 ) : ( 1 ) );

				// Find the largest amount that the portal needs to bump for the corner to pass the intersection
				float pfBumpDistance[ 2 ];

				for ( int iIntersection = 0; iIntersection < 2; ++iIntersection )
				{
					pfBumpDistance[ iIntersection ] = CalcDistanceToLine( pptCorner[ piIntersectionIndex[ iIntersection ] ], 
						sFitData[ piIntersectionIndex[ iClosestIntersection ] ].ptIntersectionPoint, 
						sFitData[ piIntersectionIndex[ iClosestIntersection ] ].ptIntersectionPoint + sFitData[ piIntersectionIndex[ iClosestIntersection ] ].vIntersectionDirection );

					pfBumpDistance[	iIntersection ] += PORTAL_BUMP_FORGIVENESS;
				}

				int iLargestBump = ( ( pfBumpDistance[ 0 ] > pfBumpDistance[ 1 ] ) ? ( 0 ) : ( 1 ) );

				// Bump the portal
				vOrigin += sFitData[ piIntersectionIndex[ iClosestIntersection ] ].vBumpDirection * pfBumpDistance[ iLargestBump ];

				// If they were parallel to the intersection line don't invalidate both before recursion
				if ( pfBumpDistance[ 0 ] == pfBumpDistance[ 1 ] )
				{
					sFitData[ piIntersectionIndex[ 0 ] ].bCornerIntersection = false;
					sFitData[ piIntersectionIndex[ 1 ] ].bCornerIntersection = false;
					iIntersectionCount = 0;

					return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
				}
				else
				{
					// Prepare data for recursion
					if ( iLargestBump != iClosestIntersection )
					{
						sFitData[ piIntersectionIndex[ iLargestBump ] ] = sFitData[ piIntersectionIndex[ iClosestIntersection ] ];
					}
					sFitData[ piIntersectionIndex[ ( ( iLargestBump == 0 ) ? ( 1 ) : ( 0 ) ) ] ].bCornerIntersection = false;
					piIntersectionIndex[ 0 ] = piIntersectionIndex[ iLargestBump ];
					iIntersectionCount = 1;

					return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
				}
			}

			// Intersections are angled, bump based on math using the corner
			vOrigin += FindBumpVectorInCorner( pptCorner[ piIntersectionIndex[ 0 ] ], pptCorner[ piIntersectionIndex[ 1 ] ], 
				sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint, sFitData[ piIntersectionIndex[ 1 ] ].ptIntersectionPoint,
				sFitData[ piIntersectionIndex[ 0 ] ].vIntersectionDirection, sFitData[ piIntersectionIndex[ 1 ] ].vIntersectionDirection,
				sFitData[ piIntersectionIndex[ 0 ] ].vBumpDirection, sFitData[ piIntersectionIndex[ 1 ] ].vBumpDirection );

			return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
		}
		break;

	case 3:
		{
			// Get the relationships of the intersections
			float fDot[ 3 ];
			fDot[ 0 ] = sFitData[ piIntersectionIndex[ 0 ] ].vBumpDirection.Dot( sFitData[ piIntersectionIndex[ 1 ] ].vBumpDirection );
			fDot[ 1 ] = sFitData[ piIntersectionIndex[ 1 ] ].vBumpDirection.Dot( sFitData[ piIntersectionIndex[ 2 ] ].vBumpDirection );
			fDot[ 2 ] = sFitData[ piIntersectionIndex[ 2 ] ].vBumpDirection.Dot( sFitData[ piIntersectionIndex[ 0 ] ].vBumpDirection );

			int iSimilarWalls = 0;

			for ( int iDot = 0; iDot < 3; ++iDot )
			{
				// If there are parallel intersections try scooting it away from a near wall
				if ( fDot[ iDot ] < -0.99f )
				{
					// Check if perpendicular wall is near
					trace_t trPerpWall1;
					bool bSoftBump1;
					bool bDir1 = TracePortalCorner( pIgnorePortal, vOrigin, vOrigin + sFitData[ piIntersectionIndex[ iDot ] ].vIntersectionDirection * PORTAL_HALF_WIDTH * 2.0f, vForward, iPlacedBy, pTraceFilterPortalShot, trPerpWall1, bSoftBump1 );

					trace_t trPerpWall2;
					bool bSoftBump2;
					bool bDir2 = TracePortalCorner( pIgnorePortal, vOrigin, vOrigin + sFitData[ piIntersectionIndex[ iDot ] ].vIntersectionDirection * -PORTAL_HALF_WIDTH * 2.0f, vForward, iPlacedBy, pTraceFilterPortalShot, trPerpWall2, bSoftBump2 );

					// No fit if there's blocking walls on both sides it can't fit
					if ( bDir1 && bDir2 )
					{
						if ( bSoftBump1 )
							bDir1 = false;
						else if ( bSoftBump2 )
							bDir1 = true;
						else
							return false;
					}

					// If there's no assumption to make, just pick a direction.
					if ( !bDir1 && !bDir2 )
					{
						bDir1 = true;
					}

					// Bump the portal
					if ( bDir1 )
					{
						vOrigin += sFitData[ piIntersectionIndex[ iDot ] ].vIntersectionDirection * -PORTAL_HALF_WIDTH;
					}
					else
					{
						vOrigin += sFitData[ piIntersectionIndex[ iDot ] ].vIntersectionDirection * PORTAL_HALF_WIDTH;
					}

					// Prepare data for recursion
					iIntersectionCount = 0;
					sFitData[ piIntersectionIndex[ 0 ] ].bCornerIntersection = false;
					sFitData[ piIntersectionIndex[ 1 ] ].bCornerIntersection = false;
					sFitData[ piIntersectionIndex[ 2 ] ].bCornerIntersection = false;

					return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
				}
				// Count similar intersections
				else if ( fDot[ iDot ] > 0.99f )
				{
					++iSimilarWalls;
				}
			}

			// If no intersections are similar
			if ( iSimilarWalls == 0 )
			{
				// Total the angles between the intersections
				float fAngleTotal = 0.0f;
				for ( int iDot = 0; iDot < 3; ++iDot )
				{
					fAngleTotal += acosf( fDot[ iDot ] );
				}

				// If it's in a triangle, it can't be fit
				if ( M_PI_F - 0.01f < fAngleTotal && fAngleTotal < M_PI_F + 0.01f )
				{
					// If any of the bumps are soft, give it another try
					if ( sFitData[ piIntersectionIndex[ 0 ] ].bSoftBump || sFitData[ piIntersectionIndex[ 1 ] ].bSoftBump || sFitData[ piIntersectionIndex[ 2 ] ].bSoftBump )
					{
						// Prepare data for recursion
						iIntersectionCount = 0;
						sFitData[ piIntersectionIndex[ 0 ] ].bCornerIntersection = false;
						sFitData[ piIntersectionIndex[ 1 ] ].bCornerIntersection = false;
						sFitData[ piIntersectionIndex[ 2 ] ].bCornerIntersection = false;

						return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
					}
					else
					{
						return false;
					}
				}
			}

			// If the intersections are all similar there's an easy way
			if ( iSimilarWalls == 3 )
			{
				// Get the closest intersection to the portal's center
				int iClosestIntersection = 0;
				float fClosestDistance = vOrigin.DistTo( sFitData[ piIntersectionIndex[ 0 ] ].ptIntersectionPoint );

				float fDistance = vOrigin.DistTo( sFitData[ piIntersectionIndex[ 1 ] ].ptIntersectionPoint );
				if ( fClosestDistance > fDistance )
				{
					iClosestIntersection = 1;
					fClosestDistance = fDistance;
				}

				fDistance = vOrigin.DistTo( sFitData[ piIntersectionIndex[ 2 ] ].ptIntersectionPoint );
				if ( fClosestDistance > fDistance )
				{
					iClosestIntersection = 2;
					fClosestDistance = fDistance;
				}

				// Find the largest amount that the portal needs to bump for the corner to pass the intersection
				float pfBumpDistance[ 3 ];

				for ( int iIntersection = 0; iIntersection < 3; ++iIntersection )
				{
					pfBumpDistance[ iIntersection ] = CalcDistanceToLine( pptCorner[ piIntersectionIndex[ iIntersection ] ], 
						sFitData[ piIntersectionIndex[ iClosestIntersection ] ].ptIntersectionPoint, 
						sFitData[ piIntersectionIndex[ iClosestIntersection ] ].ptIntersectionPoint + sFitData[ piIntersectionIndex[ iClosestIntersection ] ].vIntersectionDirection );
					pfBumpDistance[ iIntersection ] += PORTAL_BUMP_FORGIVENESS;
				}

				int iLargestBump = ( ( pfBumpDistance[ 0 ] > pfBumpDistance[ 1 ] ) ? ( 0 ) : ( 1 ) );

				iLargestBump = ( ( pfBumpDistance[ iLargestBump ] > pfBumpDistance[ 2 ] ) ? ( iLargestBump ) : ( 2 ) );

				// Bump the portal
				vOrigin += sFitData[ piIntersectionIndex[ iClosestIntersection ] ].vBumpDirection * pfBumpDistance[ iLargestBump ];

				// Prepare data for recursion
				int iStillIntersecting = 0;

				for ( int iIntersection = 0; iIntersection < 3; ++iIntersection )
				{
					// Invalidate corners that were closer to the intersection line
					if ( pfBumpDistance[ iIntersection ] != pfBumpDistance[ iLargestBump ] )
					{
						sFitData[ piIntersectionIndex[ iIntersection ] ].bCornerIntersection = false;
						--iIntersectionCount;
					}
					else
					{
						sFitData[ piIntersectionIndex[ iIntersection ] ] = sFitData[ piIntersectionIndex[ iClosestIntersection ] ];
						piIntersectionIndex[ iStillIntersecting ] = piIntersectionIndex[ iIntersection ];
						++iStillIntersecting;
					}
				}

				return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
			}

			// Get info for which corners are diagonal from each other
			float fLongestDist = 0.0f;
			int iLongestDist = 0;

			for ( int iIntersection = 0; iIntersection < 3; ++iIntersection )
			{
				float fDist = pptCorner[ piIntersectionIndex[ iIntersection ] ].DistTo( pptCorner[ piIntersectionIndex[ ( iIntersection + 1 ) % 3 ] ] );

				if ( fLongestDist < fDist )
				{
					fLongestDist = fDist;
					iLongestDist = iIntersection;
				}
			}

			int iIndex1, iIndex2, iIndex3;

			switch ( iLongestDist )
			{
			case 0:
				iIndex1 = 0;
				iIndex2 = 1;
				iIndex3 = 2;
				break;

			case 1:
				iIndex1 = 1;
				iIndex2 = 2;
				iIndex3 = 0;
				break;

			default:
				iIndex1 = 2;
				iIndex2 = 0;
				iIndex3 = 1;
				break;
			}

			// If corner is 90 degrees there my be an easy way
			float fCornerDot = sFitData[ piIntersectionIndex[ iIndex1 ] ].vIntersectionDirection.Dot( sFitData[ piIntersectionIndex[ iIndex2 ] ].vIntersectionDirection );

			if ( fCornerDot < 0.0001f && fCornerDot > -0.0001f )
			{
				// Check if portal is aligned perfectly with intersection normals
				float fPortalDot = sFitData[ piIntersectionIndex[ iIndex1 ] ].vIntersectionDirection.Dot( vRight );

				if ( ( fPortalDot < 0.0001f && fPortalDot > -0.0001f ) || fPortalDot > 0.9999f || fPortalDot < -0.9999f )
				{
					float fBump1 = CalcDistanceToLine( pptCorner[ piIntersectionIndex[ iIndex1 ] ], 
						sFitData[ piIntersectionIndex[ iIndex1 ] ].ptIntersectionPoint, 
						sFitData[ piIntersectionIndex[ iIndex1 ] ].ptIntersectionPoint + sFitData[ piIntersectionIndex[ iIndex1 ] ].vIntersectionDirection );

					fBump1 += PORTAL_BUMP_FORGIVENESS;

					float fBump2 = CalcDistanceToLine( pptCorner[ piIntersectionIndex[ iIndex2 ] ], 
						sFitData[ piIntersectionIndex[ iIndex2 ] ].ptIntersectionPoint, 
						sFitData[ piIntersectionIndex[ iIndex2 ] ].ptIntersectionPoint + sFitData[ piIntersectionIndex[ iIndex2 ] ].vIntersectionDirection );

					fBump2 += PORTAL_BUMP_FORGIVENESS;

					// Bump portal
					vOrigin += sFitData[ piIntersectionIndex[ iIndex1 ] ].vBumpDirection * fBump1;
					vOrigin += sFitData[ piIntersectionIndex[ iIndex2 ] ].vBumpDirection * fBump2;

					// Prepare recursion data
					iIntersectionCount = 0;
					sFitData[ piIntersectionIndex[ iIndex1 ] ].bCornerIntersection = false;
					sFitData[ piIntersectionIndex[ iIndex2 ] ].bCornerIntersection = false;
					sFitData[ piIntersectionIndex[ iIndex3 ] ].bCornerIntersection = false;

					return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
				}
			}

			vOrigin += FindBumpVectorInCorner( pptCorner[ piIntersectionIndex[ iIndex1 ] ], pptCorner[ piIntersectionIndex[ iIndex2 ] ], 
				sFitData[ piIntersectionIndex[ iIndex1 ] ].ptIntersectionPoint, sFitData[ piIntersectionIndex[ iIndex2 ] ].ptIntersectionPoint,
				sFitData[ piIntersectionIndex[ iIndex1 ] ].vIntersectionDirection, sFitData[ piIntersectionIndex[ iIndex2 ] ].vIntersectionDirection,
				sFitData[ piIntersectionIndex[ iIndex1 ] ].vBumpDirection, sFitData[ piIntersectionIndex[ iIndex2 ] ].vBumpDirection );

			// Prepare data for recursion
			iIntersectionCount = 0;
			sFitData[ piIntersectionIndex[ iIndex3 ] ].bCornerIntersection = false;

			return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );				
		}
		break;

	default:
		{	
			if ( sFitData[ piIntersectionIndex[ 0 ] ].bSoftBump || sFitData[ piIntersectionIndex[ 1 ] ].bSoftBump || sFitData[ piIntersectionIndex[ 2 ] ].bSoftBump || sFitData[ piIntersectionIndex[ 3 ] ].bSoftBump )
			{
				// Prepare data for recursion
				iIntersectionCount = 0;
				sFitData[ piIntersectionIndex[ 0 ] ].bCornerIntersection = false;
				sFitData[ piIntersectionIndex[ 1 ] ].bCornerIntersection = false;
				sFitData[ piIntersectionIndex[ 2 ] ].bCornerIntersection = false;
				sFitData[ piIntersectionIndex[ 3 ] ].bCornerIntersection = false;

				return FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, pTraceFilterPortalShot, iRecursions + 1, sFitData, piIntersectionIndex, &iIntersectionCount );
			}
			else
			{
				// All corners intersect with no soft bumps, so it can't be fit
				return false;
			}
		}
		break;
	}

	return true;
}

void FitPortalAroundOtherPortals( const CProp_Portal *pIgnorePortal, Vector &vOrigin, const Vector &vForward, const Vector &vRight, const Vector &vUp )
{
	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 != pIgnorePortal && pTempPortal->m_bActivated )
			{
				Vector vOtherOrigin = pTempPortal->GetAbsOrigin();
				QAngle qOtherAngles = pTempPortal->GetAbsAngles();

				Vector vLinkedForward;
				AngleVectors( qOtherAngles, &vLinkedForward, NULL, NULL );

				// If they're not on the same face then don't worry about overlap
				if ( vForward.Dot( vLinkedForward ) < 0.95f )
					continue;

				Vector vDiff = vOrigin - pTempPortal->GetLocalOrigin();

				Vector vDiffProjRight = vDiff.Dot( vRight ) * vRight;
				Vector vDiffProjUp = vDiff.Dot( vUp ) * vUp;

				float fProjRightLength = VectorNormalize( vDiffProjRight );
				float fProjUpLength = VectorNormalize( vDiffProjUp );

				if ( fProjRightLength < 1.0f )
				{
					vDiffProjRight = vRight;
				}

				if ( fProjUpLength < PORTAL_HALF_HEIGHT && fProjRightLength < PORTAL_HALF_WIDTH )
				{
					vOrigin += vDiffProjRight * ( PORTAL_HALF_WIDTH - fProjRightLength + 1.0f );
				}
			}
		}
	}
}

bool IsPortalIntersectingNoPortalVolume( const Vector &vOrigin, const QAngle &qAngles, const Vector &vForward )
{
	// Walk the no portal volume list, check each with box-box intersection
	for ( CFuncNoPortalVolume *pNoPortalEnt = GetNoPortalVolumeList(); pNoPortalEnt != NULL; pNoPortalEnt = pNoPortalEnt->m_pNext )
	{
		// Skip inactive no portal zones
		if ( !pNoPortalEnt->IsActive() )
		{
			continue;
		}

		Vector vMin;
		Vector vMax;
		pNoPortalEnt->GetCollideable()->WorldSpaceSurroundingBounds( &vMin, &vMax );

		Vector vBoxCenter = ( vMin + vMax ) * 0.5f;
		Vector vBoxExtents = ( vMax - vMin ) * 0.5f;

		// Take bump forgiveness into account on non major axies
		vBoxExtents += Vector( ( ( vForward.x > 0.5f || vForward.x < -0.5f ) ? ( 0.0f ) : ( -PORTAL_BUMP_FORGIVENESS ) ),
							   ( ( vForward.y > 0.5f || vForward.y < -0.5f ) ? ( 0.0f ) : ( -PORTAL_BUMP_FORGIVENESS ) ),
							   ( ( vForward.z > 0.5f || vForward.z < -0.5f ) ? ( 0.0f ) : ( -PORTAL_BUMP_FORGIVENESS ) ) );

		if ( UTIL_IsBoxIntersectingPortal( vBoxCenter, vBoxExtents, vOrigin, qAngles ) )
		{
			if ( sv_portal_placement_debug.GetBool() )
			{
				NDebugOverlay::Box( Vector( 0.0f, 0.0f, 0.0f ), vMin, vMax, 0, 255, 0, 128, 0.5f );
				UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );

				DevMsg( "Portal placed in no portal volume.\n" );
			}

			return true; 
		}
	}

	// Passed the list, so we didn't hit any func_noportal_volumes
	return false;
}

bool IsPortalOverlappingOtherPortals( const CProp_Portal *pIgnorePortal, const Vector &vOrigin, const QAngle &qAngles, bool bFizzle /*= false*/ )
{
	bool bOverlappedOtherPortal = false;

	Vector vForward;
	AngleVectors( qAngles, &vForward, NULL, NULL );

	Vector vPortalOBBMin = CProp_Portal_Shared::vLocalMins + Vector( 1.0f, 1.0f, 1.0f );
	Vector vPortalOBBMax = CProp_Portal_Shared::vLocalMaxs - Vector( 1.0f, 1.0f, 1.0f );

	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 != pIgnorePortal && pTempPortal->m_bActivated )
			{
				Vector vOtherOrigin = pTempPortal->GetAbsOrigin();
				QAngle qOtherAngles = pTempPortal->GetAbsAngles();

				Vector vLinkedForward;
				AngleVectors( qOtherAngles, &vLinkedForward, NULL, NULL );

				// If they're not on the same face then don't worry about overlap
				if ( vForward.Dot( vLinkedForward ) < 0.95f )
					continue;

				if ( IsOBBIntersectingOBB( vOrigin, qAngles, vPortalOBBMin, vPortalOBBMax, 
										   vOtherOrigin, qOtherAngles, vPortalOBBMin, vPortalOBBMax, 0.0f ) )
				{
					if ( sv_portal_placement_debug.GetBool() )
					{
						UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
						UTIL_Portal_NDebugOverlay( pTempPortal, 255, 0, 0, 128, false, 0.5f );

						DevMsg( "Portal overlapped another portal.\n" );
					}

					if ( bFizzle )
					{
						pTempPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
						pTempPortal->Fizzle();
						bOverlappedOtherPortal = true;
					}
					else
					{
						return true;
					}
				}
			}
		}
	}

	return bOverlappedOtherPortal;
}

bool IsPortalOnValidSurface( const Vector &vOrigin, const Vector &vForward, const Vector &vRight, const Vector &vUp, ITraceFilter *traceFilterPortalShot )
{
	trace_t tr;

	// Check if corners are on a no portal material
	for ( int iCorner = 0; iCorner < 5; ++iCorner )
	{
		Vector ptCorner = vOrigin;

		if ( iCorner < 4 )
		{
			if ( iCorner / 2 == 0 )
				ptCorner += vUp * ( PORTAL_HALF_HEIGHT - PORTAL_BUMP_FORGIVENESS * 1.1f ); //top
			else
				ptCorner += vUp * -( PORTAL_HALF_HEIGHT - PORTAL_BUMP_FORGIVENESS * 1.1f ); //bottom

			if ( iCorner % 2 == 0 )
				ptCorner += vRight * -( PORTAL_HALF_WIDTH - PORTAL_BUMP_FORGIVENESS * 1.1f ); //left
			else
				ptCorner += vRight * ( PORTAL_HALF_WIDTH - PORTAL_BUMP_FORGIVENESS * 1.1f ); //right
		}

		Ray_t ray;
		ray.Init( ptCorner + vForward, ptCorner - vForward );
		enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, traceFilterPortalShot, &tr );

		if ( tr.startsolid )
		{
			// Portal center/corner in solid
			if ( sv_portal_placement_debug.GetBool() )
			{
				DevMsg( "Portal center or corner placed inside solid.\n" );
			}

			return false;
		}

		if ( tr.fraction == 1.0f )
		{
			// Check if there's a portal bumper to act as a surface
			TraceBumpingEntities( ptCorner + vForward, ptCorner - vForward, tr );

			if ( tr.fraction == 1.0f )
			{
				// No surface behind the portal
				if ( sv_portal_placement_debug.GetBool() )
				{
					DevMsg( "Portal corner has no surface behind it.\n" );
				}

				return false;
			}
		}

		if ( tr.m_pEnt && FClassnameIs( tr.m_pEnt, "func_door" ) )
		{
			if ( sv_portal_placement_debug.GetBool() )
			{
				DevMsg( "Portal placed on func_door.\n" );
			}

			return false;
		}

		if ( IsPassThroughMaterial( tr.surface ) )
		{
			if ( sv_portal_placement_debug.GetBool() )
			{
				DevMsg( "Portal placed on a pass through material.\n" );
			}

			return false;
		}

		if ( IsNoPortalMaterial( tr.surface ) )
		{
			if ( sv_portal_placement_debug.GetBool() )
			{
				DevMsg( "Portal placed on a no portal material.\n" );
			}

			return false;
		}
	}

	return true;
}

float VerifyPortalPlacement( const CProp_Portal *pIgnorePortal, Vector &vOrigin, QAngle &qAngles, int iPlacedBy, bool bTest /*= false*/ )
{
	Vector vOriginalOrigin = vOrigin;

	Vector vForward, vRight, vUp;
	AngleVectors( qAngles, &vForward, &vRight, &vUp );

	VectorNormalize( vForward );
	VectorNormalize( vRight );
	VectorNormalize( vUp );

	trace_t tr;
	CTraceFilterSimpleClassnameList baseFilter( pIgnorePortal, COLLISION_GROUP_NONE );
	UTIL_Portal_Trace_Filter( &baseFilter );
	baseFilter.AddClassnameToIgnore( "prop_portal" );
	CTraceFilterTranslateClones traceFilterPortalShot( &baseFilter );

	// Check if center is on a surface
	Ray_t ray;
	ray.Init( vOrigin + vForward, vOrigin - vForward );
	enginetrace->TraceRay( ray, MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );

	if ( tr.fraction == 1.0f )
	{
		if ( sv_portal_placement_debug.GetBool() )
		{
			UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
			DevMsg( "Portal center has no surface behind it.\n" );
		}

		return PORTAL_ANALOG_SUCCESS_INVALID_SURFACE;
	}

	// Check if the surface is moving
	Vector vVelocityCheck;
	AngularImpulse vAngularImpulseCheck;

	IPhysicsObject *pPhysicsObject = tr.m_pEnt->VPhysicsGetObject();

	if ( pPhysicsObject )
	{
		pPhysicsObject->GetVelocity( &vVelocityCheck, &vAngularImpulseCheck );
	}
	else
	{
		tr.m_pEnt->GetVelocity( &vVelocityCheck, &vAngularImpulseCheck );
	}

	if ( vVelocityCheck != vec3_origin || vAngularImpulseCheck != vec3_origin )
	{
		if ( sv_portal_placement_debug.GetBool() )
		{
			DevMsg( "Portal was on moving surface.\n" );
		}

		return PORTAL_ANALOG_SUCCESS_INVALID_SURFACE;
	}

	// Check for invalid materials
	if ( IsPassThroughMaterial( tr.surface ) )
	{
		if ( sv_portal_placement_debug.GetBool() )
		{
			UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
			DevMsg( "Portal placed on a pass through material.\n" );
		}

		return PORTAL_ANALOG_SUCCESS_PASSTHROUGH_SURFACE;
	}

	if ( IsNoPortalMaterial( tr.surface ) )
	{
		if ( sv_portal_placement_debug.GetBool() )
		{
			UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
			DevMsg( "Portal placed on a no portal material.\n" );
		}

		return PORTAL_ANALOG_SUCCESS_INVALID_SURFACE;
	}

	// Get pointer to liked portal if it might be in the way
	g_bBumpedByLinkedPortal = false;

	if ( iPlacedBy == PORTAL_PLACED_BY_PLAYER && !sv_portal_placement_never_bump.GetBool() )
	{
		// Bump away from linked portal so it can be fit next to it
		FitPortalAroundOtherPortals( pIgnorePortal, vOrigin, vForward, vRight, vUp );
	}

	float fBumpDistance = 0.0f;

	if ( !sv_portal_placement_never_bump.GetBool() )
	{
		// Fit onto surface and auto bump
		g_FuncBumpingEntityList.RemoveAll();

		Vector vTopEdge = vUp * ( PORTAL_HALF_HEIGHT - PORTAL_BUMP_FORGIVENESS );
		Vector vBottomEdge = -vTopEdge;
		Vector vRightEdge = vRight * ( PORTAL_HALF_WIDTH - PORTAL_BUMP_FORGIVENESS );
		Vector vLeftEdge = -vRightEdge;

		if ( !FitPortalOnSurface( pIgnorePortal, vOrigin, vForward, vRight, vTopEdge, vBottomEdge, vRightEdge, vLeftEdge, iPlacedBy, &traceFilterPortalShot ) )
		{
			if ( g_bBumpedByLinkedPortal )
			{
				return PORTAL_ANALOG_SUCCESS_OVERLAP_LINKED;
			}

			if ( sv_portal_placement_debug.GetBool() )
			{
				UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
				DevMsg( "Portal was unable to fit on surface.\n" );
			}

			return PORTAL_ANALOG_SUCCESS_CANT_FIT;
		}

		// Check if it's moved too far from it's original location
		fBumpDistance = vOrigin.DistToSqr( vOriginalOrigin );

		if ( fBumpDistance > MAXIMUM_BUMP_DISTANCE )
		{
			if ( sv_portal_placement_debug.GetBool() )
			{
				UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
				DevMsg( "Portal adjusted too far from it's original location.\n" );
			}

			return PORTAL_ANALOG_SUCCESS_CANT_FIT;
		}

		//if we're less than a unit from floor, we're going to bump to match it exactly and help game movement code run smoothly
		if( vUp.z > 0.7f )
		{
			Vector vSmallForward = vForward * 0.05f;
			trace_t FloorTrace;
			UTIL_TraceLine( vOrigin + vSmallForward, vOrigin + vSmallForward - (vUp * (PORTAL_HALF_HEIGHT + 1.5f)), MASK_SOLID_BRUSHONLY, &traceFilterPortalShot, &FloorTrace );
			if( FloorTrace.fraction < 1.0f )
			{
				//we hit floor in that 1 extra unit, now doublecheck to make sure we didn't hit something else
				trace_t FloorTrace_Verify;
				UTIL_TraceLine( vOrigin + vSmallForward, vOrigin + vSmallForward - (vUp * (PORTAL_HALF_HEIGHT - 0.1f)), MASK_SOLID_BRUSHONLY, &traceFilterPortalShot, &FloorTrace_Verify );
				if( FloorTrace_Verify.fraction == 1.0f )
				{
					//if we're in here, we're definitely in a floor matching configuration, bump down to match the floor better
					vOrigin = FloorTrace.endpos + (vUp * PORTAL_HALF_HEIGHT) - vSmallForward;// - vUp * PORTAL_WALL_MIN_THICKNESS;
				}
			}
		}
	}

	// Fail if it's in a no portal volume
	if ( IsPortalIntersectingNoPortalVolume( vOrigin, qAngles, vForward ) )
	{
		return PORTAL_ANALOG_SUCCESS_INVALID_VOLUME;
	}

	// Fail if it's overlapping the linked portal
	if ( bTest && IsPortalOverlappingOtherPortals( pIgnorePortal, vOrigin, qAngles ) )
	{
		return PORTAL_ANALOG_SUCCESS_OVERLAP_LINKED;
	}

	// Fail if it's on a flagged surface material
	if ( !IsPortalOnValidSurface( vOrigin, vForward, vRight, vUp, &traceFilterPortalShot ) )
	{
		if ( sv_portal_placement_debug.GetBool() )
		{
			UTIL_Portal_NDebugOverlay( vOrigin, qAngles, 0, 0, 255, 128, false, 0.5f );
		}
		return PORTAL_ANALOG_SUCCESS_INVALID_SURFACE;
	}

	float fAnalogSuccessMultiplier = 1.0f - ( fBumpDistance / MAXIMUM_BUMP_DISTANCE );
	fAnalogSuccessMultiplier *= fAnalogSuccessMultiplier;
	fAnalogSuccessMultiplier *= fAnalogSuccessMultiplier;

	return fAnalogSuccessMultiplier * ( PORTAL_ANALOG_SUCCESS_NO_BUMP - PORTAL_ANALOG_SUCCESS_BUMPED ) + PORTAL_ANALOG_SUCCESS_BUMPED;
}