initial
This commit is contained in:
@@ -0,0 +1,622 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "baseprojectedentity_shared.h"
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
# include "baseprojector.h"
|
||||
# include "info_placement_helper.h"
|
||||
# include "portal_gamestats.h"
|
||||
# include "weapon_portalgun_shared.h"
|
||||
#else
|
||||
typedef C_BaseProjectedEntity CBaseProjectedEntity;
|
||||
#include "prediction.h"
|
||||
#include "c_baseprojector.h"
|
||||
#endif
|
||||
|
||||
// offset away from the projector, so the trace doesn't start in solid
|
||||
#define PROJECTEDENTITY_TRACE_OFFSET 25.f
|
||||
|
||||
|
||||
void UTil_ProjectedEntity_Trace_Filter( CTraceFilterSimpleClassnameList *traceFilter )
|
||||
{
|
||||
traceFilter->AddClassnameToIgnore( "prop_physics" );
|
||||
traceFilter->AddClassnameToIgnore( "func_physbox" );
|
||||
traceFilter->AddClassnameToIgnore( "simple_physics_brush" );
|
||||
/*traceFilter->AddClassnameToIgnore( "prop_weighted_cube" );
|
||||
traceFilter->AddClassnameToIgnore( "npc_portal_turret_floor" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_energy_ball" );
|
||||
traceFilter->AddClassnameToIgnore( "npc_security_camera" );
|
||||
traceFilter->AddClassnameToIgnore( "simple_physics_prop" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_ragdoll" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_glados_core" );
|
||||
traceFilter->AddClassnameToIgnore( "player" );
|
||||
traceFilter->AddClassnameToIgnore( "projected_wall_entity" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_paint_bomb" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_exploding_futbol" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_wall_projector" );
|
||||
traceFilter->AddClassnameToIgnore( "projected_wall_entity" );
|
||||
traceFilter->AddClassnameToIgnore( "projected_tractor_beam_entity" );
|
||||
traceFilter->AddClassnameToIgnore( "trigger_tractorbeam" );
|
||||
traceFilter->AddClassnameToIgnore( "physicsshadowclone" );
|
||||
traceFilter->AddClassnameToIgnore( "prop_floor_button" );*/
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool CBaseProjectedEntity::IsHittingPortal( Vector* pOutOrigin, QAngle* pOutAngles, CPortal_Base2D** pOutPortal )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
QAngle qAngles = GetLocalAngles();
|
||||
#else
|
||||
QAngle qAngles = GetNetworkAngles();
|
||||
#endif
|
||||
|
||||
// Get current orientation
|
||||
Vector vForward, vecRight, vecUp;
|
||||
AngleVectors( qAngles, &vForward );
|
||||
|
||||
Vector mins, maxs;
|
||||
GetProjectionExtents( mins, maxs );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
Vector vStart = GetLocalOrigin();
|
||||
#else
|
||||
Vector vStart = GetNetworkOrigin();
|
||||
#endif
|
||||
|
||||
Ray_t ray;
|
||||
// Projected ents keep themselves off the walls slightly, so move this up double that ammount to make
|
||||
// sure we hit any potential portal.
|
||||
Vector rayPos = vStart + PROJECTEDENTITY_TRACE_OFFSET * vForward;// GetEndPoint() - 5.f * vForward; // back up the start pos of the ray a bit to make sure that we miss anything at the endpoint
|
||||
ray.Init( rayPos, rayPos + ( vForward * MAX_TRACE_LENGTH ), mins, maxs );
|
||||
|
||||
float flPortalTraceFraction = 1.0f;
|
||||
|
||||
trace_t worldTrace;
|
||||
CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
|
||||
UTil_ProjectedEntity_Trace_Filter( &traceFilter );
|
||||
UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &worldTrace );
|
||||
CPortal_Base2D* pHitPortal = UTIL_Portal_FirstAlongRay( ray, flPortalTraceFraction );
|
||||
|
||||
if ( pOutOrigin )
|
||||
{
|
||||
*pOutOrigin = worldTrace.endpos;
|
||||
}
|
||||
|
||||
if ( pOutAngles )
|
||||
{
|
||||
*pOutAngles = qAngles;
|
||||
}
|
||||
|
||||
// trace hit world brush before hitting portal
|
||||
// we need the threshold because the difference of the two fractions is very small in a valid case
|
||||
float flTraceThreshold = 0.0001f;
|
||||
if ( flPortalTraceFraction - worldTrace.fraction > flTraceThreshold )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pOutPortal )
|
||||
{
|
||||
*pOutPortal = pHitPortal;
|
||||
}
|
||||
|
||||
if ( !pHitPortal )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only care about portals that are currently linked (we don't project through anything else)
|
||||
if ( pHitPortal->IsActivedAndLinked() == false )
|
||||
return false;
|
||||
|
||||
float flIntersectionFraction = UTIL_IntersectRayWithPortal( ray, pHitPortal );
|
||||
Vector vHitPoint = ray.m_Start + ray.m_Delta*flIntersectionFraction;
|
||||
|
||||
// Wall hit a portal, reorient and project on the other side
|
||||
VMatrix matToLinked = pHitPortal->MatrixThisToLinked();
|
||||
|
||||
Vector vNewWallOrigin;
|
||||
UTIL_Portal_PointTransform( matToLinked, vHitPoint, vNewWallOrigin );
|
||||
|
||||
QAngle vNewAngles;
|
||||
UTIL_Portal_AngleTransform( matToLinked, qAngles, vNewAngles );
|
||||
|
||||
Vector vNewForward;
|
||||
AngleVectors( vNewAngles, &vNewForward, NULL, NULL );
|
||||
|
||||
// Move far enough in front of the portal not to be co-planar
|
||||
// (will cause traces to start solid and have z fighting issues on the renderable)
|
||||
vNewWallOrigin += vNewForward * PROJECTION_END_POINT_EPSILON;
|
||||
|
||||
if ( pOutAngles )
|
||||
{
|
||||
*pOutAngles = vNewAngles;
|
||||
}
|
||||
|
||||
if ( pOutOrigin )
|
||||
{
|
||||
*pOutOrigin = vNewWallOrigin;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
ConVar cl_predict_projected_entities( "cl_predict_projected_entities", "1" );
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::RecursiveProjection( bool bShouldSpawn, CBaseProjector *pParentProjector, CPortal_Base2D *pExitPortal, const Vector &vProjectOrigin, const QAngle &qProjectAngles, int iRemainingProjections, bool bDisablePlacementHelper )
|
||||
{
|
||||
#if defined( CLIENT_DLL )
|
||||
if( !prediction->InPrediction() || !GetPredictable() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
AddEffects( EF_NOINTERP );
|
||||
|
||||
#if 0
|
||||
Vector vFlooredPosition; //HACKHACK: the inputs vary just ever so slightly from client/server. Hopefully flooring them will keep them in sync
|
||||
vFlooredPosition.x = floor( vProjectOrigin.x * 512.0f ) / 512.0f;
|
||||
vFlooredPosition.y = floor( vProjectOrigin.y * 512.0f ) / 512.0f;
|
||||
vFlooredPosition.z = floor( vProjectOrigin.z * 512.0f ) / 512.0f;
|
||||
#else
|
||||
Vector vFlooredPosition = vProjectOrigin;
|
||||
#endif
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
OnPreProjected();
|
||||
#endif
|
||||
|
||||
Vector vOldOrigin = GetAbsOrigin();
|
||||
|
||||
QAngle qModAngles; //SendProxy_Angles will perform this operation on the angles, making them differ by either extremely small values, or 360 degrees (-90 == 270 angularly, but not from a precision standpoint)
|
||||
qModAngles.x = anglemod( qProjectAngles.x );
|
||||
qModAngles.y = anglemod( qProjectAngles.y );
|
||||
qModAngles.z = anglemod( qProjectAngles.z );
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
Assert( bShouldSpawn == false );
|
||||
SetNetworkOrigin( vFlooredPosition );
|
||||
SetNetworkAngles( qModAngles );
|
||||
#else
|
||||
SetOwnerEntity( pParentProjector );
|
||||
SetLocalOrigin( vFlooredPosition );
|
||||
SetLocalAngles( qModAngles );
|
||||
#endif
|
||||
|
||||
|
||||
//EASY_DIFFPRINT( this, "CBaseProjectedEntity::RecursiveProjection() %f %f %f\n", XYZ( vFlooredPosition ) );
|
||||
|
||||
m_iMaxRemainingRecursions = iRemainingProjections;
|
||||
#if defined( GAME_DLL )
|
||||
if( bShouldSpawn )
|
||||
{
|
||||
DispatchSpawn( this );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
FindProjectedEndpoints();
|
||||
}
|
||||
|
||||
if( pExitPortal )
|
||||
{
|
||||
SetSourcePortal( pExitPortal );
|
||||
#if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
|
||||
CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( pExitPortal->m_hPlacedBy.Get() );
|
||||
if ( pPortalGun != NULL )
|
||||
{
|
||||
Vector vecForward, vecRight, vecUp;
|
||||
AngleVectors( qModAngles, &vecForward, &vecRight, &vecUp );
|
||||
g_PortalGameStats.Event_TractorBeam_Project( pExitPortal->m_ptOrigin, vecForward , ToPortalPlayer( pPortalGun->GetOwner() ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
OnProjected();
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
if( cl_predict_projected_entities.GetBool() == false )
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If this hits a portal, reorient through it.
|
||||
// We create a new ent to do this.
|
||||
if ( iRemainingProjections > 1 )
|
||||
{
|
||||
// If there is a portal within a small distance of our end point, reorient
|
||||
CPortal_Base2D* pHitPortal = NULL;
|
||||
Vector vNewProjectedEntityOrigin;
|
||||
QAngle qNewProjectedEntityAngles;
|
||||
bool bIsHittingPortal = IsHittingPortal( &vNewProjectedEntityOrigin, &qNewProjectedEntityAngles, &pHitPortal );
|
||||
SetHitPortal( pHitPortal );
|
||||
if ( bIsHittingPortal && pHitPortal && pHitPortal->IsActivedAndLinked() )
|
||||
{
|
||||
CPortal_Base2D *pNewExitPortal = pHitPortal->m_hLinkedPortal.Get();
|
||||
bool bCreateNew = (m_hChildSegment.Get() == NULL);
|
||||
#if defined( GAME_DLL ) //TODO: Set dormant on the client
|
||||
if( bCreateNew )
|
||||
{
|
||||
m_hChildSegment = CreateNewProjectedEntity();
|
||||
}
|
||||
#else
|
||||
if( !bCreateNew )
|
||||
#endif
|
||||
{
|
||||
m_hChildSegment.Get()->RecursiveProjection( bCreateNew, pParentProjector, pNewExitPortal, vNewProjectedEntityOrigin, qNewProjectedEntityAngles, iRemainingProjections - 1, bDisablePlacementHelper );
|
||||
}
|
||||
}
|
||||
// FIXME: Bring this back for DLC2
|
||||
//else if ( engine->HasPaintmap() )
|
||||
//{
|
||||
// //TestForReflectPaint();
|
||||
//}
|
||||
else if( m_hChildSegment.Get() != NULL )
|
||||
{
|
||||
#if defined( GAME_DLL ) //TODO: Set dormant on the client
|
||||
UTIL_Remove( m_hChildSegment.Get() );
|
||||
m_hChildSegment = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
|
||||
if( !bDisablePlacementHelper )
|
||||
{
|
||||
m_bCreatePlacementHelper = true;
|
||||
bool bCreatePlacement = m_hPlacementHelper.Get() == NULL;
|
||||
if( bCreatePlacement )
|
||||
{
|
||||
m_hPlacementHelper = (CInfoPlacementHelper *) CreateEntityByName( "info_placement_helper" );
|
||||
}
|
||||
|
||||
PlacePlacementHelper( m_hPlacementHelper );
|
||||
|
||||
if( bCreatePlacement )
|
||||
{
|
||||
DispatchSpawn( m_hPlacementHelper );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bCreatePlacementHelper = false;
|
||||
}
|
||||
|
||||
PhysicsTouchTriggers( NULL );
|
||||
#endif
|
||||
|
||||
// Projected entities should probably reflect in water because they are noticeable
|
||||
AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::TestForProjectionChanges( void )
|
||||
{
|
||||
#if defined( CLIENT_DLL )
|
||||
if( cl_predict_projected_entities.GetBool() == false )
|
||||
return;
|
||||
#endif
|
||||
Vector vNewPosition;
|
||||
QAngle qNewAngles;
|
||||
CPortal_Base2D* pHitPortal = NULL;
|
||||
bool bIsHittingPortal = IsHittingPortal( &vNewPosition, &qNewAngles, &pHitPortal );
|
||||
|
||||
CBaseProjectedEntity *pChild = m_hChildSegment.Get();
|
||||
|
||||
//if( pChild )
|
||||
//{
|
||||
// EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::TestForProjectionChanges() %i %s %i", entindex(), bIsHittingPortal ? "true" : "false", pHitPortal ? pHitPortal->entindex() : -1 );
|
||||
//}
|
||||
|
||||
// Lost the portal we were hitting: Fizzle all children
|
||||
if ( !bIsHittingPortal || (pHitPortal && !pHitPortal->IsActivedAndLinked()) )
|
||||
{
|
||||
SetHitPortal( NULL );
|
||||
#if defined( GAME_DLL ) //TODO: Set dormant on the client
|
||||
float flDistSqr = GetEndPoint().DistToSqr( vNewPosition );
|
||||
if ( flDistSqr > 0.1f )
|
||||
{
|
||||
FindProjectedEndpoints();
|
||||
OnProjected();
|
||||
}
|
||||
#endif
|
||||
|
||||
// FIXME: Bring this back for DLC2
|
||||
// check for reflect paint
|
||||
/*if ( engine->HasPaintmap() )
|
||||
{
|
||||
TestForReflectPaint();
|
||||
}*/
|
||||
#if defined( GAME_DLL )
|
||||
/*else*/ if( pChild )
|
||||
{
|
||||
UTIL_Remove( pChild );
|
||||
m_hChildSegment = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined( GAME_DLL )
|
||||
else if( pHitPortal->IsActivedAndLinked() && (DidRedirectionPortalMove( pHitPortal ) || ((pChild == NULL) && (m_iMaxRemainingRecursions > 0))) )
|
||||
#else
|
||||
else if( pHitPortal->IsActivedAndLinked() && DidRedirectionPortalMove( pHitPortal ) && (pChild != NULL) )
|
||||
#endif
|
||||
{
|
||||
#if defined( CLIENT_DLL )
|
||||
if( GetPredictable() )
|
||||
#endif
|
||||
{
|
||||
Vector vPrevStart = GetStartPoint();
|
||||
Vector vPrevEnd = GetEndPoint();
|
||||
FindProjectedEndpoints();
|
||||
if( (vPrevStart != GetStartPoint()) || (vPrevEnd != GetEndPoint()) )
|
||||
{
|
||||
OnProjected();
|
||||
}
|
||||
|
||||
SetHitPortal( pHitPortal );
|
||||
}
|
||||
|
||||
//reproject child portal
|
||||
bool bCreateNew = (pChild == NULL);
|
||||
#if defined( GAME_DLL )
|
||||
if( bCreateNew )
|
||||
{
|
||||
m_hChildSegment = pChild = CreateNewProjectedEntity();
|
||||
}
|
||||
#else
|
||||
if( !bCreateNew && pChild->GetPredictable() )
|
||||
#endif
|
||||
{
|
||||
pChild->RecursiveProjection( bCreateNew, (CBaseProjector *)GetOwnerEntity(), pHitPortal->m_hLinkedPortal.Get(), vNewPosition, qNewAngles, m_iMaxRemainingRecursions - 1, m_bCreatePlacementHelper );
|
||||
}
|
||||
}
|
||||
#if defined( GAME_DLL ) //server propogates down the chain. Client evaluates each entity separately since it's not guaranteed to know about them all
|
||||
else if( pChild )
|
||||
{
|
||||
pChild->TestForProjectionChanges();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Test for perturbation of the portal this projected entity is redirecting through
|
||||
// if true, this entity's owning projector will rebuild all projected ents after this one
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool CBaseProjectedEntity::DidRedirectionPortalMove( CPortal_Base2D* pPortal )
|
||||
{
|
||||
if ( !pPortal )
|
||||
return true;
|
||||
|
||||
// remote portal must exist to project through
|
||||
if ( pPortal->IsActivedAndLinked() == false )
|
||||
return true;
|
||||
|
||||
if ( pPortal != m_hHitPortal.Get() )
|
||||
return true;
|
||||
|
||||
CBaseProjectedEntity *pChild = m_hChildSegment;
|
||||
if( !pChild )
|
||||
return true;
|
||||
|
||||
// close portal moved
|
||||
if ( VectorsAreEqual( pChild->m_vecSourcePortalRemoteCenter, pPortal->m_ptOrigin ) == false )
|
||||
{
|
||||
//EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::DidRedirectionPortalMove() hit portal moved" );
|
||||
return true;
|
||||
}
|
||||
// close portal rotated
|
||||
if ( QAnglesAreEqual( pChild->m_vecSourcePortalRemoteAngle, pPortal->m_qAbsAngle ) == false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//EASY_DIFFPRINT( pChild, "%f %f %f %f %f %f", XYZ( (Vector)pChild->m_vecSourcePortalCenter ), XYZ( pPortal->m_hLinkedPortal->m_ptOrigin ) );
|
||||
|
||||
// remote portal moved
|
||||
if ( VectorsAreEqual( pChild->m_vecSourcePortalCenter, pPortal->m_hLinkedPortal->m_ptOrigin ) == false )
|
||||
{
|
||||
//EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::DidRedirectionPortalMove() remote portal moved" );
|
||||
return true;
|
||||
}
|
||||
// remote portal rotated
|
||||
if ( QAnglesAreEqual( pChild->m_vecSourcePortalAngle, pPortal->m_hLinkedPortal->m_qAbsAngle ) == false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Project from origin to solid in the direction of our forward
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::FindProjectedEndpoints( void )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
QAngle qAngles = GetLocalAngles();
|
||||
#else
|
||||
QAngle qAngles = GetNetworkAngles();
|
||||
#endif
|
||||
|
||||
// Get current orientation
|
||||
Vector vecForward, vecRight, vecUp;
|
||||
AngleVectors( qAngles, &vecForward, &vecRight, &vecUp );
|
||||
|
||||
Vector mins, maxs;
|
||||
GetProjectionExtents( mins, maxs );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
Vector vStart = GetLocalOrigin();
|
||||
#else
|
||||
Vector vStart = GetNetworkOrigin();
|
||||
#endif
|
||||
|
||||
Vector vRayPos = vStart + PROJECTEDENTITY_TRACE_OFFSET * vecForward;
|
||||
|
||||
Ray_t ray;
|
||||
ray.Init( vRayPos, vRayPos + vecForward*PROJECTOR_MAX_LENGTH, mins, maxs );
|
||||
|
||||
trace_t tr;
|
||||
CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
|
||||
UTil_ProjectedEntity_Trace_Filter( &traceFilter );
|
||||
UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
|
||||
|
||||
// Should up the max trace dist if this hits
|
||||
Assert ( tr.DidHit() );
|
||||
|
||||
m_vecStartPoint = vStart;
|
||||
m_vecEndPoint = tr.endpos + tr.plane.normal * (PROJECTION_END_POINT_EPSILON); // Move a tiny bit off the hit surface so there's no physical overlap
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::SetHitPortal( CPortal_Base2D* pPortal )
|
||||
{
|
||||
m_hHitPortal = pPortal;
|
||||
if ( pPortal )
|
||||
{
|
||||
Assert( pPortal->IsActivedAndLinked() );
|
||||
if ( pPortal->IsActivedAndLinked() )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
// Listen for this portal to move
|
||||
//pPortal->AddPortalEventListener( this );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CPortal_Base2D* CBaseProjectedEntity::GetHitPortal( void )
|
||||
{
|
||||
return m_hHitPortal.Get();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::SetSourcePortal( CPortal_Base2D* pPortal )
|
||||
{
|
||||
Assert( pPortal && pPortal->IsActivedAndLinked() );
|
||||
m_hSourcePortal.Set( pPortal );
|
||||
#if defined( CLIENT_DLL )
|
||||
SetPredictionEligible( pPortal != NULL );
|
||||
#endif
|
||||
if( pPortal )
|
||||
{
|
||||
m_vecSourcePortalCenter = pPortal->m_ptOrigin;
|
||||
m_vecSourcePortalRemoteCenter = pPortal->m_hLinkedPortal->m_ptOrigin;
|
||||
|
||||
m_vecSourcePortalAngle = pPortal->m_qAbsAngle;
|
||||
m_vecSourcePortalRemoteAngle = pPortal->m_hLinkedPortal->m_qAbsAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecSourcePortalCenter = vec3_origin;
|
||||
m_vecSourcePortalRemoteCenter = vec3_origin;
|
||||
|
||||
m_vecSourcePortalAngle = vec3_angle;
|
||||
m_vecSourcePortalRemoteAngle = vec3_angle;
|
||||
}
|
||||
|
||||
if( pPortal && pPortal->GetSimulatingPlayer() )
|
||||
{
|
||||
SetPlayerSimulated( pPortal->GetSimulatingPlayer() );
|
||||
}
|
||||
else
|
||||
{
|
||||
UnsetPlayerSimulated();
|
||||
}
|
||||
}
|
||||
|
||||
CPortal_Base2D* CBaseProjectedEntity::GetSourcePortal( void )
|
||||
{
|
||||
return m_hSourcePortal.Get();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Specify the extents to use for the projection trace
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void CBaseProjectedEntity::GetProjectionExtents( Vector &outMins, Vector &outMaxs )
|
||||
{
|
||||
outMins = outMaxs = vec3_origin;
|
||||
}
|
||||
|
||||
|
||||
void CBaseProjectedEntity::OnProjected( void )
|
||||
{
|
||||
AddEffects( EF_NOINTERP );
|
||||
#if defined( CLIENT_DLL )
|
||||
SetNetworkOrigin( GetStartPoint() );
|
||||
PreDataChanged.vStartPoint = GetStartPoint();
|
||||
PreDataChanged.vEndPoint = GetEndPoint();
|
||||
PreDataChanged.qAngles = GetNetworkAngles();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CBaseProjectedEntity::TestForReflectPaint( void )
|
||||
{
|
||||
Ray_t ray;
|
||||
// make ray twice longer than the projected length, so the trace will actually hit something
|
||||
ray.Init( GetStartPoint(), GetStartPoint() + 2.f * ( GetEndPoint() - GetStartPoint() ) );
|
||||
CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
|
||||
UTil_ProjectedEntity_Trace_Filter( &traceFilter );
|
||||
|
||||
trace_t tr;
|
||||
UTIL_ClearTrace( tr );
|
||||
UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
|
||||
|
||||
Vector vDir, vNewProjectedEntityOrigin;
|
||||
if ( UTIL_Paint_Reflect( tr, vNewProjectedEntityOrigin, vDir ) )
|
||||
{
|
||||
// rotate psuedo up vector
|
||||
Vector vOldDir, vOldUp;
|
||||
GetVectors( &vOldDir, NULL, &vOldUp );
|
||||
Vector vRotAxis = CrossProduct( vOldDir, vDir );
|
||||
float flAngleBetween = RAD2DEG( acos( clamp( DotProduct( vOldDir, vDir ), -1.f, 1.f ) ) );
|
||||
matrix3x4_t matRotation;
|
||||
MatrixBuildRotationAboutAxis( vRotAxis, flAngleBetween, matRotation );
|
||||
Vector vNewUp;
|
||||
VectorRotate( vOldUp, matRotation, vNewUp );
|
||||
|
||||
QAngle qNewProjectedEntityAngles;
|
||||
VectorAngles( vDir, vNewUp, qNewProjectedEntityAngles );
|
||||
|
||||
//reproject child portal
|
||||
bool bCreateNew = (m_hChildSegment.Get() == NULL);
|
||||
#if defined( GAME_DLL )
|
||||
if( bCreateNew )
|
||||
{
|
||||
m_hChildSegment = CreateNewProjectedEntity();
|
||||
}
|
||||
#else
|
||||
if( !bCreateNew )
|
||||
#endif
|
||||
{
|
||||
m_hChildSegment.Get()->RecursiveProjection( bCreateNew, (CBaseProjector *)GetOwnerEntity(), NULL, vNewProjectedEntityOrigin, qNewProjectedEntityAngles, m_iMaxRemainingRecursions - 1, m_bCreatePlacementHelper );
|
||||
}
|
||||
}
|
||||
#if defined( GAME_DLL )
|
||||
else if ( m_hChildSegment.Get() != NULL )
|
||||
{
|
||||
UTIL_Remove( m_hChildSegment.Get() );
|
||||
m_hChildSegment = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef BASEPROJECTEDENTITY_SHARED_H
|
||||
#define BASEPROJECTEDENTITY_SHARED_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
#include "baseprojectedentity.h"
|
||||
#else
|
||||
#include "c_baseprojectedentity.h"
|
||||
#endif
|
||||
|
||||
// Create these a small amount in front of portals they redirect through
|
||||
// and in front of solid objects they project into to prevent z fighting and stuck-in-solid problems.
|
||||
#define PROJECTION_END_POINT_EPSILON 0.1f
|
||||
|
||||
#define PROJECTOR_MAX_LENGTH 4096
|
||||
|
||||
#endif // BASEPROJECTEDENTITY_SHARED_H
|
||||
@@ -0,0 +1,69 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,79 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,21 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#ifndef IIEXTPROPPORTALLOCATORHINC
|
||||
#define IIEXTPROPPORTALLOCATORHINC
|
||||
|
||||
#define IEXTPROPPORTALLOCATOR_INTERFACE_NAME "PORTAL_SERVER_DLL_PROPPORTAL_LOCATOR"
|
||||
|
||||
abstract_class IPortalServerDllPropPortalLocator
|
||||
{
|
||||
public:
|
||||
struct PortalInfo_t
|
||||
{
|
||||
int iLinkageGroupId;
|
||||
int nPortal;
|
||||
Vector vecOrigin;
|
||||
QAngle vecAngle;
|
||||
};
|
||||
virtual void LocateAllPortals( CUtlVector<PortalInfo_t> &arrPortals ) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,299 @@
|
||||
//========= Copyright © Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//==========================================================================//
|
||||
#ifndef PAINT_BLOBS_SHARED_H
|
||||
#define PAINT_BLOBS_SHARED_H
|
||||
|
||||
#include "paint_color_manager.h"
|
||||
#include "trigger_tractorbeam_shared.h"
|
||||
|
||||
enum BlobTraceResult
|
||||
{
|
||||
BLOB_TRACE_HIT_NOTHING = 0,
|
||||
BLOB_TRACE_HIT_PORTAL,
|
||||
BLOB_TRACE_HIT_WORLD,
|
||||
BLOB_TRACE_HIT_PAINT_CLEANSER,
|
||||
BLOB_TRACE_HIT_SOMETHING,
|
||||
BLOB_TRACE_HIT_PLAYER,
|
||||
BLOB_TRACE_HIT_TRACTORBEAM,
|
||||
BLOB_TRACE_HIT_PROP_PORTAL // this flag is for creating ghost blobs to make smooth blobs rendering when blobs go through portal
|
||||
};
|
||||
|
||||
struct BlobCollisionRecord
|
||||
{
|
||||
trace_t trace;
|
||||
Vector targetEndPos;
|
||||
BlobTraceResult traceResultType;
|
||||
};
|
||||
|
||||
enum PaintBlobMoveState
|
||||
{
|
||||
PAINT_BLOB_AIR_MOVE = 0,
|
||||
PAINT_BLOB_STREAK_MOVE,
|
||||
PAINT_BLOB_TRACTOR_BEAM_MOVE
|
||||
};
|
||||
|
||||
|
||||
class CBasePaintBlob
|
||||
{
|
||||
public:
|
||||
CBasePaintBlob( void );
|
||||
~CBasePaintBlob( void );
|
||||
|
||||
void Init( const Vector &vecOrigin, const Vector &vecVelocity, int paintType, float flMaxStreakTime, float flStreakSpeedDampenRate, CBaseEntity* pOwner, bool bSilent, bool bDrawOnly );
|
||||
|
||||
bool IsStreaking( void ) const;
|
||||
|
||||
void SetTractorBeam( CTrigger_TractorBeam* pBeam );
|
||||
|
||||
const Vector& GetTempEndPosition( void ) const;
|
||||
void SetTempEndPosition( const Vector &vecTempEndPosition );
|
||||
|
||||
const Vector& GetTempEndVelocity( void ) const;
|
||||
void SetTempEndVelocity( const Vector &vecTempEndVelocity );
|
||||
|
||||
const Vector& GetPosition( void ) const;
|
||||
void SetPosition( const Vector &vecPosition );
|
||||
|
||||
const Vector& GetPrevPosition() const;
|
||||
void SetPrevPosition( const Vector& vPrevPosition );
|
||||
|
||||
const Vector& GetVelocity( void ) const;
|
||||
void SetVelocity( const Vector &vecVelocity );
|
||||
|
||||
const Vector& GetStreakDir() const;
|
||||
|
||||
PaintPowerType GetPaintPowerType( void ) const;
|
||||
|
||||
PaintBlobMoveState GetMoveState( void ) const;
|
||||
void SetMoveState( PaintBlobMoveState moveState );
|
||||
|
||||
float GetAccumulatedTime() const { return m_flAccumulatedTime; }
|
||||
void SetAccumulatedTime( float flAccumulatedTime ) { m_flAccumulatedTime = flAccumulatedTime; }
|
||||
|
||||
// kill this if we don't need fixed time step
|
||||
float GetLastUpdateTime() const { return m_flLastUpdateTime; }
|
||||
void SetLastUpdateTime( float flLastUpdateTime ) { m_flLastUpdateTime = flLastUpdateTime; }
|
||||
|
||||
float GetVortexDirection() const;
|
||||
|
||||
bool ShouldDeleteThis() const;
|
||||
void SetDeletionFlag( bool bDelete );
|
||||
|
||||
float GetLifeTime() const;
|
||||
void UpdateLifeTime( float flUpdateTime );
|
||||
|
||||
void UpdateBlobCollision( float flDeltaTime, const Vector& vecEndPos, Vector& vecEndVelocity );
|
||||
void UpdateBlobPostCollision( float flDeltaTime );
|
||||
|
||||
const Vector& GetContactNormal() const;
|
||||
|
||||
float GetStreakTime() const { return m_flStreakTimer; }
|
||||
float GetStreakSpeedDampenRate() const { return m_flStreakSpeedDampenRate; }
|
||||
|
||||
void SetRadiusScale( float flRadiusScale );
|
||||
float GetRadiusScale( void ) const;
|
||||
|
||||
bool ShouldPlayEffect() const;
|
||||
|
||||
bool IsSilent() const { return m_bSilent; }
|
||||
|
||||
bool IsGhosting() const { return ( m_hPortal != NULL ); }
|
||||
void GetGhostMatrix( VMatrix& matGhostTransform );
|
||||
void ResetGhostState() { m_hPortal = NULL; }
|
||||
|
||||
CTrigger_TractorBeam* GetCurrentBeam() const;
|
||||
|
||||
bool PaintBlobCheckShouldStreak( const trace_t &trace );
|
||||
|
||||
void PlayEffect( const Vector& vPosition, const Vector& vNormal );
|
||||
|
||||
bool PaintBlobStreakPaint( const Vector &vecBlobStartPos );
|
||||
|
||||
virtual void PaintBlobPaint( const trace_t &tr ) = 0;
|
||||
|
||||
void SetShouldPlaySound( bool shouldPlaySound );
|
||||
bool ShouldPlaySound() const;
|
||||
|
||||
void SetBlobTeleportedThisFrame( bool bTeleported ) { m_bTeleportedThisFrame = bTeleported; }
|
||||
bool HasBlobTeleportedThisFrame() const { return m_bTeleportedThisFrame; }
|
||||
int GetTeleportationCount() const { return m_nTeleportationCount; }
|
||||
|
||||
float m_flDestVortexRadius;
|
||||
float m_flCurrentVortexRadius;
|
||||
float m_flCurrentVortexSpeed;
|
||||
float m_flVortexDirection; // -1.f or 1.f
|
||||
|
||||
protected:
|
||||
void PaintBlobMoveThroughPortal( float flDeltaTime, CPortal_Base2D *pInPortal, const Vector &vecStartPos, const Vector &vecTransformedEndPos );
|
||||
|
||||
BlobTraceResult BlobHitSolid( CBaseEntity* pHitEntity );
|
||||
int CheckCollision( BlobCollisionRecord *pCollisions, int maxCollisions, const Vector &vecEndPos );
|
||||
void CheckCollisionAgainstWorldAndStaticProps( BlobCollisionRecord& solidHitRecord, float& flHitFraction );
|
||||
int CheckCollisionThroughPortal( BlobCollisionRecord *pCollisions, int maxCollisions, const Vector &vecEndPos );
|
||||
void ResolveCollision( bool& bDeleted, const BlobCollisionRecord& collision, Vector& targetVelocity, float deltaTime );
|
||||
|
||||
void DecayVortexSpeed( float flDeltaTime );
|
||||
|
||||
Vector m_vecTempEndPosition;
|
||||
Vector m_vecTempEndVelocity;
|
||||
|
||||
Vector m_vecPosition;
|
||||
Vector m_vecPrevPosition;
|
||||
Vector m_vecVelocity;
|
||||
|
||||
// this is used for playing paint effect on client and painting surface on server
|
||||
Vector m_vContactNormal;
|
||||
|
||||
PaintPowerType m_paintType;
|
||||
EHANDLE m_hOwner;
|
||||
PaintBlobMoveState m_MoveState;
|
||||
|
||||
//Timers for the blob
|
||||
float m_flLifeTime;
|
||||
|
||||
//Streaking
|
||||
Vector m_vecStreakDir;
|
||||
bool m_bStreakDirChanged;
|
||||
float m_flStreakTimer;
|
||||
float m_flStreakSpeedDampenRate;
|
||||
|
||||
bool m_bDeleteFlag;
|
||||
|
||||
float m_flAccumulatedTime;
|
||||
float m_flLastUpdateTime;
|
||||
|
||||
//The radius scale of the blob for the isosurface rendering
|
||||
// this is shared so the listen server doesn't have to create blobs on the client dll
|
||||
float m_flRadiusScale;
|
||||
|
||||
bool m_bShouldPlayEffect;
|
||||
|
||||
// blob needs to know if hitting beam is the same as the current beam, so it doesn't need to init beam data again
|
||||
EntityBeamHistory_t m_beamHistory;
|
||||
bool m_bInTractorBeam;
|
||||
|
||||
// HACK: remove this when awesome paint box/sphere feature is done (Bank)
|
||||
bool m_bSilent;
|
||||
|
||||
// portal handle (for ghosting)
|
||||
EHANDLE m_hPortal;
|
||||
|
||||
// optimize trace
|
||||
bool CheckCollisionBoxAgainstWorldAndStaticProps();
|
||||
Vector m_vCollisionBoxCenter;
|
||||
bool m_bCollisionBoxHitSolid;
|
||||
bool m_bShouldPlaySound;
|
||||
|
||||
// if this flag is true, blob won't do effect/sound/paint
|
||||
bool m_bDrawOnly;
|
||||
|
||||
// teleportation counter
|
||||
bool m_bTeleportedThisFrame;
|
||||
int m_nTeleportationCount;
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
class CPaintBlob;
|
||||
#else
|
||||
class C_PaintBlob;
|
||||
typedef C_PaintBlob CPaintBlob;
|
||||
#endif
|
||||
|
||||
typedef CUtlVector<CPaintBlob*> PaintBlobVector_t;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Listen Server Shared Data
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct BlobTeleportationHistory_t
|
||||
{
|
||||
BlobTeleportationHistory_t() : m_vEnterPosition( vec3_origin ), m_vExitPosition( vec3_origin ), m_flTeleportTime( 0.f )
|
||||
{
|
||||
}
|
||||
|
||||
BlobTeleportationHistory_t( const VMatrix& matSourceToLinked, const VMatrix& matLinkedToSource, const Vector& vEnter, const Vector& vExit, float flTeleportTime )
|
||||
{
|
||||
m_matSourceToLinked = matSourceToLinked;
|
||||
m_matLinkedToSource = matLinkedToSource;
|
||||
|
||||
m_vEnterPosition = vEnter;
|
||||
m_vExitPosition = vExit;
|
||||
m_flTeleportTime = flTeleportTime;
|
||||
}
|
||||
|
||||
VMatrix m_matSourceToLinked;
|
||||
VMatrix m_matLinkedToSource;
|
||||
|
||||
Vector m_vEnterPosition;
|
||||
Vector m_vExitPosition;
|
||||
float m_flTeleportTime;
|
||||
};
|
||||
|
||||
typedef CUtlVector< BlobTeleportationHistory_t > BlobTeleportationHistoryVector_t;
|
||||
|
||||
struct BlobData_t
|
||||
{
|
||||
BlobData_t()
|
||||
{
|
||||
m_blobID = -1;
|
||||
m_vPosition = vec3_origin;
|
||||
m_flScale = 0.f;
|
||||
m_bTeleportedThisFrame = false;
|
||||
m_bGhosting = false;
|
||||
MatrixSetIdentity( m_matGhostTransform );
|
||||
}
|
||||
|
||||
BlobData_t( const BlobData_t& blobData )
|
||||
{
|
||||
m_blobID = blobData.m_blobID;
|
||||
m_vPosition = blobData.m_vPosition;
|
||||
m_flScale = blobData.m_flScale;
|
||||
m_bTeleportedThisFrame = blobData.m_bTeleportedThisFrame;
|
||||
m_bGhosting = blobData.m_bGhosting;
|
||||
m_matGhostTransform = blobData.m_matGhostTransform;
|
||||
}
|
||||
|
||||
~BlobData_t()
|
||||
{
|
||||
m_teleportationHistory.Purge();
|
||||
}
|
||||
|
||||
int m_blobID;
|
||||
Vector m_vPosition;
|
||||
float m_flScale;
|
||||
|
||||
// teleportation data
|
||||
bool m_bTeleportedThisFrame;
|
||||
BlobTeleportationHistoryVector_t m_teleportationHistory;
|
||||
|
||||
// ghosting data
|
||||
bool m_bGhosting;
|
||||
VMatrix m_matGhostTransform;
|
||||
};
|
||||
|
||||
typedef CUtlVector< BlobData_t > BlobDataVector_t;
|
||||
|
||||
struct BlobInterpolationData_t
|
||||
{
|
||||
BlobInterpolationData_t()
|
||||
{
|
||||
m_flUpdateTime = 0.f;
|
||||
}
|
||||
|
||||
~BlobInterpolationData_t()
|
||||
{
|
||||
m_blobData.Purge();
|
||||
}
|
||||
|
||||
float m_flUpdateTime;
|
||||
BlobDataVector_t m_blobData;
|
||||
};
|
||||
|
||||
typedef CUtlVector< BlobInterpolationData_t > BlobInterpolationDataVector_t;
|
||||
|
||||
void PaintBlobUpdate( const PaintBlobVector_t& blobList );
|
||||
|
||||
#endif //PAINT_BLOBS_SHARED_H
|
||||
@@ -0,0 +1,218 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include "paint_cleanser_manager.h"
|
||||
#include "igameevents.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_trigger_paint_cleanser.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
|
||||
ConVar paint_cleanser_visibility_poll_frequency( "paint_cleanser_visibility_poll_rate", "0.5", FCVAR_CHEAT );
|
||||
ConVar paint_cleanser_visibility_checks_debug( "paint_cleanser_visibility_checks_debug", "0", FCVAR_DEVELOPMENTONLY );
|
||||
ConVar paint_cleanser_visibility_range( "paint_cleanser_visibility_range", "1000.0f", FCVAR_CHEAT );
|
||||
ConVar paint_cleanser_visibility_look_angle( "paint_cleanser_visibility_look_angle", "60.0f", FCVAR_CHEAT );
|
||||
#else
|
||||
#include "trigger_paint_cleanser.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
CPaintCleanserManager PaintCleanserManager( "PaintCleanserManager" );
|
||||
|
||||
CPaintCleanserManager::CPaintCleanserManager( char const *name )
|
||||
#ifdef CLIENT_DLL
|
||||
: CAutoGameSystemPerFrame( name ),
|
||||
m_flNextPollTime( 0.0f )
|
||||
#else
|
||||
: CAutoGameSystem( name )
|
||||
#endif
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
memset( m_ppVisibleCleanser, 0, sizeof(m_ppVisibleCleanser) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CPaintCleanserManager::~CPaintCleanserManager( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::LevelInitPreEntity()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
m_flNextPollTime = gpGlobals->curtime + paint_cleanser_visibility_poll_frequency.GetFloat();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::LevelShutdownPreEntity()
|
||||
{
|
||||
m_PaintCleansers.RemoveAll();
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::AddPaintCleanser( C_TriggerPaintCleanser *pCleanser )
|
||||
{
|
||||
m_PaintCleansers.AddToTail( pCleanser );
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::RemovePaintCleanser( C_TriggerPaintCleanser *pCleanser )
|
||||
{
|
||||
m_PaintCleansers.FindAndRemove( pCleanser );
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::GetPaintCleansers( PaintCleanserVector_t& paintCleansers )
|
||||
{
|
||||
paintCleansers = m_PaintCleansers;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void CPaintCleanserManager::Update( float frametime )
|
||||
{
|
||||
//Check if it is time to update the cleanser visibility
|
||||
if( gpGlobals->curtime < m_flNextPollTime )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Set the time for the next check
|
||||
m_flNextPollTime = gpGlobals->curtime + paint_cleanser_visibility_poll_frequency.GetFloat();
|
||||
|
||||
UpdatePaintCleanserVisibility();
|
||||
}
|
||||
|
||||
|
||||
void CPaintCleanserManager::UpdatePaintCleanserVisibility( void )
|
||||
{
|
||||
FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
|
||||
{
|
||||
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer( hh );
|
||||
if( !pPlayer )
|
||||
continue;
|
||||
|
||||
Vector vecPlayerForward;
|
||||
AngleVectors( pPlayer->EyeAngles(), &vecPlayerForward );
|
||||
VectorNormalize( vecPlayerForward );
|
||||
Vector vecPlayerEyePos = pPlayer->EyePosition();
|
||||
|
||||
//Max distance to check for visibility
|
||||
float flVisibilityRange = paint_cleanser_visibility_range.GetFloat() * paint_cleanser_visibility_range.GetFloat();
|
||||
|
||||
bool bDebugging = paint_cleanser_visibility_checks_debug.GetBool();
|
||||
|
||||
float flClosestCleanserDist = FLT_MAX;
|
||||
C_TriggerPaintCleanser *pClosestCleanser = NULL;
|
||||
|
||||
//If there is a cleanser visible before the check
|
||||
bool bCleanserWasVisible = m_ppVisibleCleanser[hh] ? true : false;
|
||||
|
||||
for( int i = 0; i < m_PaintCleansers.Count(); ++i )
|
||||
{
|
||||
Vector vecCleanserPos = m_PaintCleansers[i]->WorldSpaceCenter();
|
||||
|
||||
//Don't test the cleanser if the player is too far from it
|
||||
float flDistSqr = vecPlayerEyePos.DistToSqr( vecCleanserPos );
|
||||
if( flDistSqr > flVisibilityRange )
|
||||
{
|
||||
if( m_ppVisibleCleanser[hh] == m_PaintCleansers[i] )
|
||||
{
|
||||
m_ppVisibleCleanser[hh] = NULL;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Don't test the cleanser if the player isn't looking in its direction
|
||||
Vector vecPlayerCleanserDir = vecCleanserPos - vecPlayerEyePos;
|
||||
VectorNormalize( vecPlayerCleanserDir );
|
||||
float flPlayerCleanserAngle = RAD2DEG( acos( DotProduct( vecPlayerForward, vecPlayerCleanserDir ) ) );
|
||||
if( flPlayerCleanserAngle >= paint_cleanser_visibility_look_angle.GetFloat() )
|
||||
{
|
||||
if( m_ppVisibleCleanser[hh] == m_PaintCleansers[i] )
|
||||
{
|
||||
m_ppVisibleCleanser[hh] = NULL;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Trace from the player's eye to the paint cleanser
|
||||
Ray_t playerCleanserRay;
|
||||
playerCleanserRay.Init( vecPlayerEyePos, vecCleanserPos );
|
||||
trace_t tr;
|
||||
int mask = MASK_BLOCKLOS_AND_NPCS & ~CONTENTS_BLOCKLOS;
|
||||
UTIL_TraceRay( playerCleanserRay, mask, pPlayer, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
//If there is nothing between the player and the cleanser
|
||||
if( tr.fraction == 1.0f || tr.m_pEnt == m_PaintCleansers[i] )
|
||||
{
|
||||
if( bDebugging )
|
||||
{
|
||||
NDebugOverlay::Line( pPlayer->WorldSpaceCenter(), vecCleanserPos, 0, 255, 255, false, paint_cleanser_visibility_poll_frequency.GetFloat() );
|
||||
}
|
||||
|
||||
Color debugColor( 255, 0, 0 );
|
||||
|
||||
//If this is the closest visible paint cleanser
|
||||
if( flDistSqr < flClosestCleanserDist )
|
||||
{
|
||||
flClosestCleanserDist = flDistSqr;
|
||||
pClosestCleanser = m_PaintCleansers[i];
|
||||
|
||||
debugColor = Color( 0, 255, 0 );
|
||||
}
|
||||
|
||||
if( bDebugging )
|
||||
{
|
||||
NDebugOverlay::Cross3D( vecCleanserPos, 16, debugColor.r(), debugColor.g(), debugColor.b(), false, paint_cleanser_visibility_poll_frequency.GetFloat() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( bDebugging )
|
||||
{
|
||||
NDebugOverlay::Line( pPlayer->WorldSpaceCenter(), vecCleanserPos, 0, 0, 255, false, paint_cleanser_visibility_poll_frequency.GetFloat() );
|
||||
}
|
||||
}
|
||||
} //For all the paint cleansers
|
||||
|
||||
//If the player can see a different cleanser
|
||||
if( pClosestCleanser && pClosestCleanser != m_ppVisibleCleanser[hh] )
|
||||
{
|
||||
m_ppVisibleCleanser[hh] = pClosestCleanser;
|
||||
|
||||
if( pPlayer->Weapon_OwnsThisType( "weapon_paintgun" ) )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "paint_cleanser_visible" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "userid", pPlayer->GetUserID() );
|
||||
event->SetInt( "subject", m_ppVisibleCleanser[hh]->entindex() );
|
||||
|
||||
gameeventmanager->FireEventClientSide( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
//If there is no visible cleanser
|
||||
else if( !m_ppVisibleCleanser[hh] && bCleanserWasVisible )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "paint_cleanser_not_visible" );
|
||||
if( event )
|
||||
{
|
||||
event->SetInt( "userid", pPlayer->GetUserID() );
|
||||
|
||||
gameeventmanager->FireEventClientSide( event );
|
||||
}
|
||||
}
|
||||
|
||||
} //for each pPlayer
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_CLEANSER_MANAGER_H
|
||||
#define PAINT_CLEANSER_MANAGER_H
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
class C_TriggerPaintCleanser;
|
||||
#else
|
||||
class CTriggerPaintCleanser;
|
||||
#endif
|
||||
|
||||
//The paint cleanser on client and server
|
||||
#ifdef CLIENT_DLL
|
||||
typedef CUtlVector<C_TriggerPaintCleanser*> PaintCleanserVector_t;
|
||||
#else
|
||||
typedef CUtlVector<CTriggerPaintCleanser*> PaintCleanserVector_t;
|
||||
typedef CTriggerPaintCleanser C_TriggerPaintCleanser;
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
class CPaintCleanserManager : public CAutoGameSystemPerFrame
|
||||
#else
|
||||
class CPaintCleanserManager : public CAutoGameSystem
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CPaintCleanserManager( char const *name );
|
||||
~CPaintCleanserManager();
|
||||
|
||||
//CAutoGameSystem members
|
||||
virtual char const *Name() { return "PaintCleanserManager"; }
|
||||
virtual void LevelInitPreEntity();
|
||||
virtual void LevelShutdownPreEntity();
|
||||
|
||||
void AddPaintCleanser( C_TriggerPaintCleanser *pCleanser );
|
||||
void RemovePaintCleanser( C_TriggerPaintCleanser *pCleanser );
|
||||
|
||||
void GetPaintCleansers( PaintCleanserVector_t& paintCleansers );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//CAutoGameSystemPerFrame members
|
||||
virtual void Update( float frametime );
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void UpdatePaintCleanserVisibility( void );
|
||||
|
||||
C_TriggerPaintCleanser *m_ppVisibleCleanser[ MAX_SPLITSCREEN_PLAYERS ];
|
||||
float m_flNextPollTime;
|
||||
#endif
|
||||
|
||||
PaintCleanserVector_t m_PaintCleansers;
|
||||
};
|
||||
|
||||
extern CPaintCleanserManager PaintCleanserManager;
|
||||
|
||||
#endif //PAINT_CLEANSER_MANAGER_H
|
||||
@@ -0,0 +1,116 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the paint color manager class.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "paint_color_manager.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Paint color ConVars
|
||||
ConVar speed_paint_color( "speed_paint_color", "255 106 0 255", FCVAR_REPLICATED, "Color for speed paint" );
|
||||
ConVar bounce_paint_color( "bounce_paint_color", "0 165 255 255", FCVAR_REPLICATED, "Color for bounce paint" );
|
||||
// FIXME: Bring this back for DLC2
|
||||
//ConVar reflect_paint_color( "reflect_paint_color", "0 255 0 255", FCVAR_REPLICATED, "Color for reflect paint" );
|
||||
ConVar portal_paint_color( "portal_paint_color", "128 128 128 255", FCVAR_REPLICATED, "Color for portal paint");
|
||||
ConVar erase_color( "erase_color", "0 0 0 0", FCVAR_REPLICATED, "Color for erase" );
|
||||
ConVar erase_visual_color( "erase_visual_color", "255 255 255 255", FCVAR_REPLICATED, "Color for erase that is rendered" );
|
||||
|
||||
ConVar paint_color_max_diff( "paint_color_max_diff", "32", FCVAR_REPLICATED, "The maximum difference between two colors for matching." );
|
||||
|
||||
int ComputeColorDiff( const Color& colorA, const Color& colorB )
|
||||
{
|
||||
return abs( colorA.r() - colorB.r() ) +
|
||||
abs( colorA.g() - colorB.g() ) +
|
||||
abs( colorA.b() - colorB.b() );
|
||||
}
|
||||
|
||||
|
||||
PaintPowerType MapColorToPower( const Color& color )
|
||||
{
|
||||
int result = NO_POWER;
|
||||
|
||||
int minDiff = ComputeColorDiff( color, MapPowerToColor(NO_POWER) );
|
||||
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
|
||||
{
|
||||
const int diff = ComputeColorDiff( color, MapPowerToColor( i ) );
|
||||
if( diff < minDiff )
|
||||
{
|
||||
minDiff = diff;
|
||||
result = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the closest matching power, if the diff is small enough. Otherwise, return NO_POWER.
|
||||
return minDiff <= paint_color_max_diff.GetInt() ? static_cast<PaintPowerType>( result ) : NO_POWER;
|
||||
}
|
||||
|
||||
|
||||
PaintPowerType MapColorToPower( const color24& color )
|
||||
{
|
||||
return MapColorToPower( Color( color.r, color.g, color.b ) );
|
||||
}
|
||||
|
||||
|
||||
PaintPowerType MapColorToPower( const CUtlVector<BYTE>& colors )
|
||||
{
|
||||
// Find the hit count of each power for the colors in the array
|
||||
unsigned powerCount[PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER] = { 0 };
|
||||
PaintPowerType power;
|
||||
for ( int i = 0; i < colors.Count(); ++i )
|
||||
{
|
||||
power = static_cast< PaintPowerType >( colors[i] );
|
||||
AssertMsg( power >= 0 && power < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER, "Invalid power type! This may cause out-of-bounds indexing!" );
|
||||
++powerCount[ power ];
|
||||
}
|
||||
|
||||
// Find the power with the highest hit count, excluding no power
|
||||
power = NO_POWER;
|
||||
unsigned highestCount = 0;
|
||||
for ( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
|
||||
{
|
||||
if ( powerCount[i] > highestCount )
|
||||
{
|
||||
power = static_cast< PaintPowerType >( i );
|
||||
highestCount = powerCount[i];
|
||||
}
|
||||
}
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
|
||||
Color MapPowerToColor( int paintPowerType )
|
||||
{
|
||||
AssertMsg( paintPowerType < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER, "Index out of bounds." );
|
||||
paintPowerType = MIN( paintPowerType, static_cast<int>( NO_POWER ) );
|
||||
|
||||
switch ( paintPowerType )
|
||||
{
|
||||
case BOUNCE_POWER:
|
||||
return bounce_paint_color.GetColor();
|
||||
case SPEED_POWER:
|
||||
return speed_paint_color.GetColor();
|
||||
case REFLECT_POWER:
|
||||
return speed_paint_color.GetColor();// FIXME: Bring this back for DLC2 reflect_paint_color.GetColor();
|
||||
case PORTAL_POWER:
|
||||
return portal_paint_color.GetColor();
|
||||
default:
|
||||
return erase_color.GetColor();
|
||||
}
|
||||
}
|
||||
|
||||
Color MapPowerToVisualColor( int paintPowerType )
|
||||
{
|
||||
if( paintPowerType == NO_POWER )
|
||||
{
|
||||
return erase_visual_color.GetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
return MapPowerToColor( paintPowerType );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the paint color manager class.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_COLOR_MANAGER_H
|
||||
#define PAINT_COLOR_MANAGER_H
|
||||
|
||||
// src/public/
|
||||
#include "game/shared/portal2/paint_enum.h"
|
||||
|
||||
PaintPowerType MapColorToPower( const color24& color );
|
||||
PaintPowerType MapColorToPower( const Color& color );
|
||||
PaintPowerType MapColorToPower( const CUtlVector<BYTE>& colors );
|
||||
Color MapPowerToColor( int paintPowerType );
|
||||
Color MapPowerToVisualColor( int paintPowerType );
|
||||
|
||||
#endif // ifndef PAINT_COLOR_MANAGER_H
|
||||
@@ -0,0 +1,222 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the PaintPowerInfo structure for storing information about
|
||||
// the paint powers used.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
#include "paint_power_info.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//BEGIN_SEND_TABLE_NOBASE( PaintPowerInfo_t, DT_PaintPowerInfo_t )
|
||||
// SendPropEHandle( SENDINFO( m_HandleToOther ) ),
|
||||
// SendPropVector( SENDINFO( m_SurfaceNormal ) ),
|
||||
//END_SEND_TABLE()
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
BEGIN_PREDICTION_DATA_NO_BASE( PaintPowerInfo_t )
|
||||
|
||||
DEFINE_PRED_FIELD( m_HandleToOther, FIELD_EHANDLE, 0 ),
|
||||
DEFINE_PRED_FIELD( m_SurfaceNormal, FIELD_VECTOR, 0 ),
|
||||
DEFINE_PRED_FIELD( m_ContactPoint, FIELD_VECTOR, 0 ),
|
||||
DEFINE_PRED_FIELD( m_State, FIELD_INTEGER, 0 ),
|
||||
DEFINE_PRED_FIELD( m_PaintPowerType, FIELD_INTEGER, 0 ),
|
||||
DEFINE_PRED_FIELD( m_IsOnThinSurface, FIELD_BOOLEAN, 0 )
|
||||
|
||||
END_PREDICTION_DATA()
|
||||
#endif
|
||||
|
||||
IMPLEMENT_NULL_SIMPLE_DATADESC( PaintPowerInfo_t );
|
||||
|
||||
const Vector DEFAULT_PAINT_SURFACE_NORMAL = Vector(0.0f, 0.0f, 1.0f); // Flat ground
|
||||
|
||||
PaintPowerInfo_t::PaintPowerInfo_t()
|
||||
: m_SurfaceNormal( DEFAULT_PAINT_SURFACE_NORMAL ),
|
||||
m_ContactPoint( 0.0f, 0.0f, 0.0f ),
|
||||
m_PaintPowerType( NO_POWER ),
|
||||
m_HandleToOther( 0 ),
|
||||
m_State( INACTIVE_PAINT_POWER ),
|
||||
m_IsOnThinSurface( false )
|
||||
{}
|
||||
|
||||
|
||||
PaintPowerInfo_t::PaintPowerInfo_t( const Vector &normal,
|
||||
const Vector &contactPt,
|
||||
CBaseEntity* pOther,
|
||||
PaintPowerType power,
|
||||
bool isOnThinSurface )
|
||||
: m_SurfaceNormal( normal ),
|
||||
m_ContactPoint( contactPt ),
|
||||
m_PaintPowerType( power ),
|
||||
m_State( INACTIVE_PAINT_POWER ),
|
||||
m_IsOnThinSurface( isOnThinSurface )
|
||||
{
|
||||
|
||||
m_HandleToOther.Set( pOther );
|
||||
}
|
||||
|
||||
#define PAINT_POWER_CPP_INLINE
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool AreSamePower( const PaintPowerInfo_t& powerA, const PaintPowerInfo_t& powerB )
|
||||
{
|
||||
return powerA.m_PaintPowerType == powerB.m_PaintPowerType &&
|
||||
powerA.m_HandleToOther == powerB.m_HandleToOther &&
|
||||
AlmostEqual( DotProduct( powerA.m_SurfaceNormal, powerB.m_SurfaceNormal ), 1.f );
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool AreDifferentPowers( const PaintPowerInfo_t& powerA, const PaintPowerInfo_t& powerB )
|
||||
{
|
||||
return !AreSamePower( powerA, powerB );
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsSpeedPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_PaintPowerType == SPEED_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsBouncePower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_PaintPowerType == BOUNCE_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsReflectPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_PaintPowerType == REFLECT_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsPortalPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_PaintPowerType == PORTAL_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsNoPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_PaintPowerType == NO_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsActivatingPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_State == ACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsActivePower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_State == ACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsDeactivatingPower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_State == DEACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
PAINT_POWER_CPP_INLINE bool IsInactivePower( const PaintPowerInfo_t& power )
|
||||
{
|
||||
return power.m_State == INACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
char const *const PowerTypeToString( const PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return PowerTypeToString( powerInfo.m_PaintPowerType );
|
||||
}
|
||||
|
||||
|
||||
char const *const PowerTypeToString( PaintPowerType type )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case BOUNCE_POWER:
|
||||
return "Bounce";
|
||||
|
||||
case SPEED_POWER:
|
||||
return "Speed";
|
||||
|
||||
case REFLECT_POWER:
|
||||
return "Speed";// FIXME: Bring this back for DLC2 "Reflect";
|
||||
|
||||
case PORTAL_POWER:
|
||||
return "Portal";
|
||||
|
||||
case NO_POWER:
|
||||
return "No"; // Yes
|
||||
|
||||
default:
|
||||
return "Invalid power or hasn't been added to PowerTypeToString() in paint_power_info.cpp";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char const *const PowerStateToString( const PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return PowerStateToString( powerInfo.m_State );
|
||||
}
|
||||
|
||||
|
||||
char const *const PowerStateToString( PaintPowerState state )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
case ACTIVATING_PAINT_POWER:
|
||||
return "Activating";
|
||||
|
||||
case ACTIVE_PAINT_POWER:
|
||||
return "Active";
|
||||
|
||||
case DEACTIVATING_PAINT_POWER:
|
||||
return "Deactivating";
|
||||
|
||||
case INACTIVE_PAINT_POWER:
|
||||
return "Inactive";
|
||||
|
||||
default:
|
||||
return "Invalid state or hasn't been added to PowerStateToString() in paint_power_info.cpp";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintPowerInfoDebugMsg( const PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
DevMsg( "Contact Point:\t(%f, %f, %f)\n", XYZ(powerInfo.m_ContactPoint) );
|
||||
DevMsg( "Surface Normal:\t(%f, %f, %f)\n", XYZ(powerInfo.m_SurfaceNormal) );
|
||||
DevMsg( "Paint Power: %s Power\n", PowerTypeToString(powerInfo.m_PaintPowerType) );
|
||||
|
||||
// Non-const because CBaseEntity::GetClassname() is non-const because someone is lame.
|
||||
// Rebuilding every project in main to fix a const-correctness mistake is incredibly annoying.
|
||||
CBaseEntity* pOther;
|
||||
pOther = powerInfo.m_HandleToOther.Get() != NULL ? EntityFromEntityHandle( powerInfo.m_HandleToOther.Get() ) : NULL;
|
||||
DevMsg( "Other Class: %s\n", pOther != NULL ? pOther->GetClassname() : "Null" );
|
||||
DevMsg( "State: %s\n", PowerStateToString( powerInfo.m_State ) );
|
||||
}
|
||||
|
||||
|
||||
void DrawPaintPowerContactInfo( const PaintPowerInfo_t& powerInfo, const Color& color, float duration, bool noDepthTest )
|
||||
{
|
||||
NDebugOverlay::Sphere( powerInfo.m_ContactPoint, 5.0f, color.r(), color.g(), color.b(), noDepthTest, duration );
|
||||
NDebugOverlay::Line( powerInfo.m_ContactPoint, powerInfo.m_ContactPoint + 20.0f * powerInfo.m_SurfaceNormal, color.r(), color.g(), color.b(), noDepthTest, duration );
|
||||
}
|
||||
|
||||
|
||||
int DescendingPaintPriorityCompare( const PaintPowerInfo_t* a, const PaintPowerInfo_t* b )
|
||||
{
|
||||
return a->m_PaintPowerType - b->m_PaintPowerType;
|
||||
}
|
||||
|
||||
int AscendingPaintPriorityCompare( const PaintPowerInfo_t* a, const PaintPowerInfo_t* b )
|
||||
{
|
||||
return b->m_PaintPowerType - a->m_PaintPowerType;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the PaintPowerInfo structure for storing information about
|
||||
// the paint powers used.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PAINT_POWER_INFO_H
|
||||
#define PAINT_POWER_INFO_H
|
||||
|
||||
#include "paint_color_manager.h"
|
||||
|
||||
enum PaintPowerState
|
||||
{
|
||||
ACTIVATING_PAINT_POWER,
|
||||
ACTIVE_PAINT_POWER,
|
||||
DEACTIVATING_PAINT_POWER,
|
||||
INACTIVE_PAINT_POWER
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// struct PaintPowerInfo
|
||||
// Purpose: Holds the necessary information for using a paint power.
|
||||
// Note: This will change quite a bit once the paint tech is implemented.
|
||||
//=============================================================================
|
||||
struct PaintPowerInfo_t
|
||||
{
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
DECLARE_PREDICTABLE();
|
||||
DECLARE_CLASS_NOBASE( PaintPowerInfo_t );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
Vector m_SurfaceNormal; // Normal to the surface the paint is on
|
||||
Vector m_ContactPoint; // Contact point on the surface
|
||||
PaintPowerType m_PaintPowerType; // Paint power at this point on the surface
|
||||
CBaseHandle m_HandleToOther; // Handle to the other entity
|
||||
PaintPowerState m_State; // Current state of the power
|
||||
bool m_IsOnThinSurface; // The power is on a thin surface
|
||||
|
||||
PaintPowerInfo_t();
|
||||
PaintPowerInfo_t( const Vector& normal,
|
||||
const Vector& contactPt,
|
||||
CBaseEntity* pOther,
|
||||
PaintPowerType power = NO_POWER,
|
||||
bool isOnThinSurface = false );
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// Helper Functions and Functors
|
||||
//=============================================================================
|
||||
extern bool AreSamePower( const PaintPowerInfo_t& powerA, const PaintPowerInfo_t& powerB );
|
||||
extern bool AreDifferentPowers( const PaintPowerInfo_t& powerA, const PaintPowerInfo_t& powerB );
|
||||
extern bool IsSpeedPower( const PaintPowerInfo_t& power );
|
||||
extern bool IsBouncePower( const PaintPowerInfo_t& power );
|
||||
extern bool IsStickPower( const PaintPowerInfo_t& power );
|
||||
extern bool IsNoPower( const PaintPowerInfo_t& power );
|
||||
extern bool IsActivatingPower( const PaintPowerInfo_t& power );
|
||||
extern bool IsActivePower( const PaintPowerInfo_t& power );
|
||||
extern bool IsDeactivatingPower( const PaintPowerInfo_t& power );
|
||||
extern bool IsInactivePower( const PaintPowerInfo_t& power );
|
||||
char const *const PowerTypeToString( const PaintPowerInfo_t& powerInfo );
|
||||
char const *const PowerTypeToString( PaintPowerType type );
|
||||
char const *const PowerStateToString( const PaintPowerInfo_t& powerInfo );
|
||||
char const *const PowerStateToString( PaintPowerState state );
|
||||
void PrintPowerInfoDebugMsg( const PaintPowerInfo_t& powerInfo );
|
||||
void DrawPaintPowerContactInfo( const PaintPowerInfo_t& powerInfo, const Color& color, float duration, bool noDepthTest );
|
||||
|
||||
int DescendingPaintPriorityCompare( const PaintPowerInfo_t* a, const PaintPowerInfo_t* b );
|
||||
int AscendingPaintPriorityCompare( const PaintPowerInfo_t* a, const PaintPowerInfo_t* b );
|
||||
|
||||
#endif // ifndef PAINT_POWER_INFO_H
|
||||
@@ -0,0 +1,660 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the base class for all paint power users.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_POWER_USER_H
|
||||
#define PAINT_POWER_USER_H
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include "gamestringpool.h"
|
||||
#include "paint_power_user_interface.h"
|
||||
#include "portal_player_shared.h"
|
||||
#include "paint_power_info.h"
|
||||
|
||||
extern ConVar sv_enable_paint_power_user_debug;
|
||||
|
||||
//#define PAINT_POWER_USER_DEBUG
|
||||
|
||||
char const* const PAINT_POWER_USER_DATA_CLASS_NAME = "PaintPowerUser";
|
||||
const int MAX_PAINT_SURFACE_CONTEXT_LENGTH = 32;
|
||||
|
||||
template< typename T >
|
||||
inline T* GetBegin( CUtlVector<T>& v )
|
||||
{
|
||||
return v.Base();
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
inline T* GetEnd( CUtlVector<T>& v )
|
||||
{
|
||||
return v.Base() + v.Count();
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
inline const T* GetConstBegin( const CUtlVector<T>& v )
|
||||
{
|
||||
return v.Base();
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
inline const T* GetConstEnd( const CUtlVector<T>& v )
|
||||
{
|
||||
return v.Base() + v.Count();
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
inline const std::pair< T*, T* > GetRange( CUtlVector< T >& v )
|
||||
{
|
||||
return std::pair< T*, T* >( v.Base(), v.Base() + v.Count() );
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
inline const std::pair< const T*, const T* > GetConstRange( const CUtlVector< T >& v )
|
||||
{
|
||||
return std::pair< const T*, const T* >( v.Base(), v.Base() + v.Count() );
|
||||
}
|
||||
|
||||
|
||||
template< typename IteratorType >
|
||||
inline int GetCountFromRange( const std::pair< IteratorType, IteratorType >& range )
|
||||
{
|
||||
return range.second - range.first;
|
||||
}
|
||||
|
||||
|
||||
template< typename IteratorType >
|
||||
inline bool IsEmptyRange( const std::pair< IteratorType, IteratorType >& range )
|
||||
{
|
||||
return range.first == range.second;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// class PaintPowerUser
|
||||
// Purpose: Base class for entities which use paint powers.
|
||||
//=============================================================================
|
||||
template< typename BaseEntityType >
|
||||
class PaintPowerUser : public BaseEntityType, public IPaintPowerUser
|
||||
{
|
||||
DECLARE_CLASS( PaintPowerUser< BaseEntityType >, BaseEntityType );
|
||||
|
||||
#if defined( CLIENT_DLL ) && !defined( NO_ENTITY_PREDICTION )
|
||||
DECLARE_PREDICTABLE();
|
||||
static const datamap_t PredMapInit();
|
||||
#endif
|
||||
|
||||
public:
|
||||
//-------------------------------------------------------------------------
|
||||
// Constructor/Virtual Destructor
|
||||
//-------------------------------------------------------------------------
|
||||
PaintPowerUser();
|
||||
virtual ~PaintPowerUser();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Public Accessors
|
||||
//-------------------------------------------------------------------------
|
||||
virtual const PaintPowerConstRange GetPaintPowers() const;
|
||||
virtual const PaintPowerInfo_t& GetPaintPower( unsigned powerType ) const;
|
||||
virtual const PaintPowerInfo_t* FindHighestPriorityActivePaintPower() const;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paint Power Effects
|
||||
//-------------------------------------------------------------------------
|
||||
virtual void AddSurfacePaintPowerInfo( const PaintPowerInfo_t& contact, char const* context /*= 0*/ );
|
||||
virtual void UpdatePaintPowers();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Protected Types
|
||||
//-------------------------------------------------------------------------
|
||||
typedef CUtlVector< PaintPowerInfo_t > PaintPowerInfoVector;
|
||||
|
||||
virtual void ChooseActivePaintPowers( PaintPowerInfoVector& activePowers ) = 0; // Default provided
|
||||
void ClearSurfacePaintPowerInfo();
|
||||
|
||||
protected:
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paint Power Effects
|
||||
//-------------------------------------------------------------------------
|
||||
typedef int (*PaintPowerInfoCompare)( const PaintPowerInfo_t*, const PaintPowerInfo_t* );
|
||||
void PrioritySortSurfacePaintPowerInfo( PaintPowerInfoCompare comp );
|
||||
|
||||
PaintPowerState ActivatePaintPower( PaintPowerInfo_t& power );
|
||||
PaintPowerState UsePaintPower( PaintPowerInfo_t& power );
|
||||
PaintPowerState DeactivatePaintPower( PaintPowerInfo_t& power );
|
||||
|
||||
void MapSurfacesToPowers();
|
||||
bool SurfaceInfoContainsPower( const PaintPowerInfo_t& power, char const* context = 0 ) const;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Protected Accessors
|
||||
//-------------------------------------------------------------------------
|
||||
const PaintPowerConstRange GetSurfacePaintPowerInfo( char const* context = 0 ) const;
|
||||
bool HasAnySurfacePaintPowerInfo() const;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Protected Mutators
|
||||
//-------------------------------------------------------------------------
|
||||
void ForceSetPaintPower( const PaintPowerInfo_t& powerInfo );
|
||||
void ForcePaintPowerToState( PaintPowerType type, PaintPowerState newState );
|
||||
|
||||
private:
|
||||
//-------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//-------------------------------------------------------------------------
|
||||
struct ContextSurfacePaintPowerInfo_t
|
||||
{
|
||||
PaintPowerInfoVector paintPowerInfo;
|
||||
string_t context;
|
||||
};
|
||||
|
||||
typedef CUtlVector< ContextSurfacePaintPowerInfo_t > ContextPaintPowerInfoArray;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//-------------------------------------------------------------------------
|
||||
PaintPowerInfo_t m_PaintPowers[PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER]; // Current powers and their states
|
||||
PaintPowerInfoVector m_SurfacePaintPowerInfo; // Array of paint power info from surfaces touched
|
||||
ContextPaintPowerInfoArray m_ContextSurfacePaintPowerInfo; // Array of paint power info from surfaces under some context
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Private Accessors
|
||||
//-------------------------------------------------------------------------
|
||||
const PaintPowerRange GetNonConstSurfacePaintPowerInfo( char const* context = 0 );
|
||||
int FindContextSurfacePaintPowerInfo( char const* context ) const;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paint Power Effects
|
||||
//-------------------------------------------------------------------------
|
||||
virtual PaintPowerState ActivateSpeedPower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
virtual PaintPowerState UseSpeedPower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
virtual PaintPowerState DeactivateSpeedPower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
|
||||
virtual PaintPowerState ActivateBouncePower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
virtual PaintPowerState UseBouncePower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
virtual PaintPowerState DeactivateBouncePower( PaintPowerInfo_t& powerInfo ) = 0;
|
||||
|
||||
virtual PaintPowerState ActivateNoPower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState UseNoPower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState DeactivateNoPower( PaintPowerInfo_t& powerInfo );
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// PaintPowerUser Implementation
|
||||
//=============================================================================
|
||||
|
||||
//#define DEFINE_PRED_TYPEDESCRIPTION( name, fieldtype ) \
|
||||
// { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, NULL, NULL, NULL, &fieldtype::m_PredMap }
|
||||
|
||||
// OMFG HACK: A macro to individually add embedded types from arrays because the current macros don't handle arrays of embedded types properly
|
||||
// OMFG TODO: Write a generic macro to work with templatized classes.
|
||||
#define DEFINE_EMBEDDED_ARRAY_ELEMENT( elementType, arrayName, arrayIndex ) \
|
||||
{ FIELD_EMBEDDED, #arrayName"["#arrayIndex"]", offsetof(classNameTypedef, arrayName[arrayIndex]), 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, NULL, NULL, NULL, &elementType::m_PredMap }
|
||||
|
||||
|
||||
// OMFG HACK: Define the prediction table. The current macros don't work with templatized classes.
|
||||
// OMFG TODO: Write a generic macro to work with templatized classes.
|
||||
#if defined( CLIENT_DLL ) && !defined( NO_ENTITY_PREDICTION )
|
||||
|
||||
template< typename BaseEntityType >
|
||||
datamap_t PaintPowerUser<BaseEntityType>::m_PredMap = PaintPowerUser<BaseEntityType>::PredMapInit();
|
||||
|
||||
// Note: This type description table is unused but declared as part of DECLARE_PREDICTABLE. Initializing it here to get rid of warnings.
|
||||
template< typename BaseEntityType >
|
||||
typedescription_t PaintPowerUser<BaseEntityType>::m_PredDesc[1] = { { FIELD_VOID,0,0,0,0,0,0,0,0 } };
|
||||
|
||||
template< typename BaseEntityType >
|
||||
datamap_t *PaintPowerUser<BaseEntityType>::GetPredDescMap( void )
|
||||
{
|
||||
return &m_PredMap;
|
||||
}
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const datamap_t PaintPowerUser<BaseEntityType>::PredMapInit()
|
||||
{
|
||||
typedef PaintPowerUser<BaseEntityType> classNameTypedef;
|
||||
static typedescription_t predDesc[] =
|
||||
{
|
||||
//{ FIELD_VOID,0,0,0,0,0,0,0,0 },
|
||||
//DEFINE_EMBEDDED_AUTO_ARRAY( m_PaintPowers ) // This only predicts the first element of the array right now
|
||||
DEFINE_EMBEDDED_ARRAY_ELEMENT( PaintPowerInfo_t, m_PaintPowers, BOUNCE_POWER ),
|
||||
DEFINE_EMBEDDED_ARRAY_ELEMENT( PaintPowerInfo_t, m_PaintPowers, SPEED_POWER ),
|
||||
DEFINE_EMBEDDED_ARRAY_ELEMENT( PaintPowerInfo_t, m_PaintPowers, NO_POWER )
|
||||
};
|
||||
|
||||
datamap_t predMap = { predDesc, ARRAYSIZE( predDesc ), PAINT_POWER_USER_DATA_CLASS_NAME, &PaintPowerUser<BaseEntityType>::BaseClass::m_PredMap };
|
||||
|
||||
return predMap;
|
||||
}
|
||||
#endif // if defined( CLIENT_DLL ) && !defined( NO_ENTITY_PREDICTION )
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerUser<BaseEntityType>::PaintPowerUser()
|
||||
{
|
||||
for( unsigned i = 0; i < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER; ++i )
|
||||
{
|
||||
m_PaintPowers[i] = PaintPowerInfo_t();
|
||||
m_PaintPowers[i].m_State = INACTIVE_PAINT_POWER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerUser<BaseEntityType>::~PaintPowerUser()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const PaintPowerConstRange PaintPowerUser<BaseEntityType>::GetPaintPowers() const
|
||||
{
|
||||
return PaintPowerConstRange( m_PaintPowers, m_PaintPowers + ARRAYSIZE(m_PaintPowers) );
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const PaintPowerInfo_t& PaintPowerUser<BaseEntityType>::GetPaintPower( unsigned powerType ) const
|
||||
{
|
||||
AssertMsg( powerType < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER, "Out of bounds." );
|
||||
return m_PaintPowers[ powerType < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER ? powerType : NO_POWER ];
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const PaintPowerInfo_t* PaintPowerUser<BaseEntityType>::FindHighestPriorityActivePaintPower() const
|
||||
{
|
||||
const PaintPowerInfo_t* pPower = 0;
|
||||
for( unsigned i = 0; i < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER; ++i )
|
||||
{
|
||||
if( m_PaintPowers[i].m_State == ACTIVE_PAINT_POWER )
|
||||
{
|
||||
pPower = m_PaintPowers + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pPower;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::AddSurfacePaintPowerInfo( const PaintPowerInfo_t& contact, char const* context )
|
||||
{
|
||||
if ( !engine->HasPaintmap() )
|
||||
{
|
||||
Warning( "MEMORY LEAK: adding surface paint powers in a level with no paintmaps.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no context, add it to the default list
|
||||
if( context == NULL)
|
||||
{
|
||||
m_SurfacePaintPowerInfo.AddToTail( contact );
|
||||
}
|
||||
// There is a context, so add it to the appropriate list
|
||||
else
|
||||
{
|
||||
// Search for context
|
||||
int index = FindContextSurfacePaintPowerInfo( context );
|
||||
|
||||
// If not found, create it first
|
||||
if( index == m_ContextSurfacePaintPowerInfo.InvalidIndex() )
|
||||
{
|
||||
index = m_ContextSurfacePaintPowerInfo.Count();
|
||||
m_ContextSurfacePaintPowerInfo.EnsureCount( index + 1 );
|
||||
m_ContextSurfacePaintPowerInfo[index].context = AllocPooledString( context );
|
||||
}
|
||||
|
||||
// Add the new surface info
|
||||
m_ContextSurfacePaintPowerInfo[index].paintPowerInfo.AddToTail( contact );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::ChooseActivePaintPowers( PaintPowerInfoVector& activePowers )
|
||||
{
|
||||
// Figure out colors/powers
|
||||
MapSurfacesToPowers();
|
||||
|
||||
// If there is a contact with any surface
|
||||
if( m_SurfacePaintPowerInfo.Count() != 0 )
|
||||
{
|
||||
// Sort the surfaces by priority
|
||||
PrioritySortSurfacePaintPowerInfo( &DescendingPaintPriorityCompare );
|
||||
|
||||
// The active power is the one with the highest priority
|
||||
activePowers.AddToTail( m_SurfacePaintPowerInfo.Head() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::UpdatePaintPowers()
|
||||
{
|
||||
// Only update if there's paint in the map
|
||||
if( engine->HasPaintmap() )
|
||||
{
|
||||
// Update which powers are active
|
||||
PaintPowerInfoVector activePowers;
|
||||
ChooseActivePaintPowers( activePowers );
|
||||
|
||||
// Cache the powers
|
||||
PaintPowerInfo_t cachedPowers[PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER];
|
||||
//V_memcpy( cachedPowers, m_PaintPowers, sizeof(PaintPowerInfo_t) * PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER );
|
||||
std::copy( m_PaintPowers, m_PaintPowers + PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER, cachedPowers );
|
||||
|
||||
// Set all powers in a state other than inactive to deactivating
|
||||
for( unsigned i = 0; i < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER; ++i )
|
||||
{
|
||||
PaintPowerInfo_t& power = m_PaintPowers[i];
|
||||
power.m_State = IsInactivePower( power ) ? INACTIVE_PAINT_POWER : DEACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
// For each new active power
|
||||
PaintPowerConstRange activeRange = GetConstRange( activePowers );
|
||||
for( PaintPowerConstIter i = activeRange.first; i != activeRange.second; ++i )
|
||||
{
|
||||
// Set it as the current one
|
||||
const PaintPowerInfo_t& newPower = *i;
|
||||
const unsigned index = newPower.m_PaintPowerType;
|
||||
m_PaintPowers[index] = newPower;
|
||||
|
||||
// If the old power was active and roughly the same, keep it active. Otherwise, activate it.
|
||||
const PaintPowerInfo_t& oldPower = cachedPowers[index];
|
||||
const bool stayActive = IsActivePower( oldPower ) && AreSamePower( oldPower, newPower );
|
||||
m_PaintPowers[index].m_State = stayActive ? ACTIVE_PAINT_POWER : ACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
// Clear the surface information
|
||||
// NOTE: Calling this after Activating/Using/Deactivating paint powers makes sticky boxes not very sticky
|
||||
// and that's why I moved it back over here. -Brett
|
||||
ClearSurfacePaintPowerInfo();
|
||||
|
||||
// Update the state of each power
|
||||
for( unsigned i = 0; i < PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER; ++i )
|
||||
{
|
||||
switch( m_PaintPowers[i].m_State )
|
||||
{
|
||||
case ACTIVATING_PAINT_POWER:
|
||||
m_PaintPowers[i].m_State = ActivatePaintPower( m_PaintPowers[i] );
|
||||
#if defined CLIENT_DLL
|
||||
RANDOM_CEG_TEST_SECRET_PERIOD( 127, 1023 );
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ACTIVE_PAINT_POWER:
|
||||
m_PaintPowers[i].m_State = UsePaintPower( m_PaintPowers[i] );
|
||||
break;
|
||||
|
||||
case DEACTIVATING_PAINT_POWER:
|
||||
#if defined GAME_DLL
|
||||
RANDOM_CEG_TEST_SECRET_PERIOD( 937, 3821 );
|
||||
#endif
|
||||
m_PaintPowers[i].m_State = DeactivatePaintPower( m_PaintPowers[i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::ClearSurfacePaintPowerInfo()
|
||||
{
|
||||
m_SurfacePaintPowerInfo.RemoveAll();
|
||||
|
||||
const int count = m_ContextSurfacePaintPowerInfo.Count();
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
m_ContextSurfacePaintPowerInfo[i].paintPowerInfo.RemoveAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::PrioritySortSurfacePaintPowerInfo( PaintPowerInfoCompare comp )
|
||||
{
|
||||
// Sort the surfaces by priority
|
||||
m_SurfacePaintPowerInfo.Sort( comp );
|
||||
|
||||
const int count = m_ContextSurfacePaintPowerInfo.Count();
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
m_ContextSurfacePaintPowerInfo[i].paintPowerInfo.Sort( comp );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename PaintPowerIterator >
|
||||
void MapSurfacesToPowers( PaintPowerIterator begin, PaintPowerIterator end )
|
||||
{
|
||||
for( PaintPowerIterator i = begin; i != end; ++i )
|
||||
{
|
||||
MapSurfaceToPower(*i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::MapSurfacesToPowers()
|
||||
{
|
||||
PaintPowerRange range = GetNonConstSurfacePaintPowerInfo();
|
||||
::MapSurfacesToPowers( range.first, range.second );
|
||||
|
||||
const int count = m_ContextSurfacePaintPowerInfo.Count();
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
PaintPowerRange contextRange = GetRange( m_ContextSurfacePaintPowerInfo[i].paintPowerInfo );
|
||||
::MapSurfacesToPowers( contextRange.first, contextRange.second );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
bool PaintPowerUser<BaseEntityType>::SurfaceInfoContainsPower( const PaintPowerInfo_t& power, char const* context ) const
|
||||
{
|
||||
PaintPowerConstRange range = GetSurfacePaintPowerInfo( context );
|
||||
for( PaintPowerConstIter i = range.first; i != range.second; ++i )
|
||||
{
|
||||
if( AreSamePower(power, *i) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const PaintPowerRange PaintPowerUser<BaseEntityType>::GetNonConstSurfacePaintPowerInfo( char const* context )
|
||||
{
|
||||
if( !context )
|
||||
return GetRange( m_SurfacePaintPowerInfo );
|
||||
|
||||
const int i = FindContextSurfacePaintPowerInfo( context );
|
||||
#ifdef PAINT_POWER_USER_DEBUG
|
||||
if( i == m_ContextSurfacePaintPowerInfo.InvalidIndex() )
|
||||
Warning( "Trying to get a range that doesn't exist.\n" );
|
||||
#endif
|
||||
return i != m_ContextSurfacePaintPowerInfo.InvalidIndex() ? GetRange( m_ContextSurfacePaintPowerInfo[i].paintPowerInfo ) : PaintPowerRange( (PaintPowerIter)0, (PaintPowerIter)0 );
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
int PaintPowerUser<BaseEntityType>::FindContextSurfacePaintPowerInfo( char const* context ) const
|
||||
{
|
||||
AssertMsg( context, "Null pointers are bad, and you should feel bad." );
|
||||
int index = m_ContextSurfacePaintPowerInfo.InvalidIndex();
|
||||
const int count = m_ContextSurfacePaintPowerInfo.Count();
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
if( !V_strncmp( STRING( m_ContextSurfacePaintPowerInfo[i].context ), context, MAX_PAINT_SURFACE_CONTEXT_LENGTH ) )
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::ActivatePaintPower( PaintPowerInfo_t& power )
|
||||
{
|
||||
AssertMsg( power.m_State == ACTIVATING_PAINT_POWER, "Activating a paint power that's not trying to activate." );
|
||||
switch( power.m_PaintPowerType )
|
||||
{
|
||||
case BOUNCE_POWER:
|
||||
return ActivateBouncePower( power );
|
||||
|
||||
case SPEED_POWER:
|
||||
return ActivateSpeedPower( power );
|
||||
|
||||
case PORTAL_POWER:
|
||||
return ActivateNoPower( power );
|
||||
|
||||
case REFLECT_POWER:
|
||||
return ActivateNoPower( power );
|
||||
|
||||
case NO_POWER:
|
||||
return ActivateNoPower( power );
|
||||
|
||||
default:
|
||||
AssertMsg( false, "Invalid power or power hasn't been added to PaintPowerUser properly." );
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::UsePaintPower( PaintPowerInfo_t& power )
|
||||
{
|
||||
AssertMsg( power.m_State == ACTIVE_PAINT_POWER, "Using a power that hasn't been activated." );
|
||||
switch( power.m_PaintPowerType )
|
||||
{
|
||||
case BOUNCE_POWER:
|
||||
return UseBouncePower( power );
|
||||
|
||||
case SPEED_POWER:
|
||||
return UseSpeedPower( power );
|
||||
|
||||
case PORTAL_POWER:
|
||||
return UseNoPower( power );
|
||||
|
||||
case REFLECT_POWER:
|
||||
return UseNoPower( power );
|
||||
|
||||
case NO_POWER:
|
||||
return UseNoPower( power );
|
||||
|
||||
default:
|
||||
AssertMsg( false, "Invalid power or power hasn't been added to PaintPowerUser properly." );
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::DeactivatePaintPower( PaintPowerInfo_t& power )
|
||||
{
|
||||
AssertMsg( power.m_State == DEACTIVATING_PAINT_POWER, "Deactivating a power that's not trying to deactivate." );
|
||||
switch( power.m_PaintPowerType )
|
||||
{
|
||||
case BOUNCE_POWER:
|
||||
return DeactivateBouncePower( power );
|
||||
|
||||
case SPEED_POWER:
|
||||
return DeactivateSpeedPower( power );
|
||||
|
||||
case PORTAL_POWER:
|
||||
return DeactivateNoPower( power );
|
||||
|
||||
case REFLECT_POWER:
|
||||
return DeactivateNoPower( power );
|
||||
|
||||
case NO_POWER:
|
||||
return DeactivateNoPower( power );
|
||||
|
||||
default:
|
||||
AssertMsg( false, "Invalid power or power hasn't been added to PaintPowerUser properly." );
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const PaintPowerConstRange PaintPowerUser<BaseEntityType>::GetSurfacePaintPowerInfo( char const* context ) const
|
||||
{
|
||||
if( !context )
|
||||
return GetConstRange( m_SurfacePaintPowerInfo );
|
||||
|
||||
const int i = FindContextSurfacePaintPowerInfo( context );
|
||||
#ifdef PAINT_POWER_USER_DEBUG
|
||||
if( i == m_ContextSurfacePaintPowerInfo.InvalidIndex() )
|
||||
Warning( "Trying to get a range that doesn't exist.\n" );
|
||||
#endif
|
||||
return i != m_ContextSurfacePaintPowerInfo.InvalidIndex() ? GetConstRange( m_ContextSurfacePaintPowerInfo[i].paintPowerInfo ) : PaintPowerConstRange( (PaintPowerConstIter)0, (PaintPowerConstIter)0 );
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
bool PaintPowerUser<BaseEntityType>::HasAnySurfacePaintPowerInfo() const
|
||||
{
|
||||
bool hasSurfaceInfo = m_SurfacePaintPowerInfo.Count() != 0;
|
||||
const int count = m_ContextSurfacePaintPowerInfo.Count();
|
||||
for( int i = 0; i < count && !hasSurfaceInfo; ++i )
|
||||
{
|
||||
hasSurfaceInfo = m_ContextSurfacePaintPowerInfo[i].paintPowerInfo.Count() != 0;
|
||||
}
|
||||
|
||||
return hasSurfaceInfo;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::ForceSetPaintPower( const PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
m_PaintPowers[powerInfo.m_PaintPowerType] = powerInfo;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void PaintPowerUser<BaseEntityType>::ForcePaintPowerToState( PaintPowerType type, PaintPowerState newState )
|
||||
{
|
||||
// TODO: Implement some error checking for edge cases here.
|
||||
m_PaintPowers[type].m_State = newState;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::ActivateNoPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return ACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::UseNoPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return ACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerState PaintPowerUser<BaseEntityType>::DeactivateNoPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
#endif // ifndef PAINT_POWER_USER_H
|
||||
@@ -0,0 +1,108 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the interface class for all paint power users.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "paint_power_user_interface.h"
|
||||
#include "paintable_entity.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "world.h"
|
||||
#else
|
||||
#include "c_world.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sv_enable_paint_power_user_debug("sv_enable_paint_power_user_debug", "0", FCVAR_REPLICATED | FCVAR_CHEAT,"Enable debug spew for paint power users.");
|
||||
extern ConVar sv_debug_draw_contacts;
|
||||
|
||||
IPaintPowerUser::~IPaintPowerUser()
|
||||
{}
|
||||
|
||||
|
||||
void MapSurfaceToPower( PaintPowerInfo_t& info )
|
||||
{
|
||||
IHandleEntity* pEntityHandle = info.m_HandleToOther.Get();
|
||||
CBaseEntity* pEnt = pEntityHandle != NULL ? EntityFromEntityHandle( pEntityHandle ) : NULL;
|
||||
|
||||
// If the power hasn't been set to anything elsewhere, and the entity is valid
|
||||
if ( info.m_PaintPowerType == NO_POWER && pEnt != NULL )
|
||||
{
|
||||
// Treat portalsimulator_collisionentity as the world
|
||||
if( FClassnameIs( pEnt, "portalsimulator_collisionentity" ) )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
pEnt = GetWorldEntity();
|
||||
#else
|
||||
pEnt = GetClientWorldEntity();
|
||||
#endif
|
||||
}
|
||||
|
||||
// If this is a world entity
|
||||
if( pEnt->IsBSPModel() )
|
||||
{
|
||||
Vector vStart = info.m_ContactPoint + info.m_SurfaceNormal;
|
||||
Vector vEnd = vStart - 10.f * info.m_SurfaceNormal;
|
||||
Ray_t ray;
|
||||
ray.Init( vStart, vEnd );
|
||||
trace_t tr;
|
||||
UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
// Trace into the paint map to find the power if the surface is paintable
|
||||
if ( UTIL_IsPaintableSurface( tr.surface ) )
|
||||
{
|
||||
Vector contactPoint = info.m_ContactPoint + 0.1f * info.m_SurfaceNormal;
|
||||
info.m_PaintPowerType = UTIL_Paint_TracePower( pEnt, contactPoint, info.m_SurfaceNormal );
|
||||
}
|
||||
else
|
||||
{
|
||||
info.m_PaintPowerType = NO_POWER;
|
||||
}
|
||||
}
|
||||
else if( !pEnt->IsPlayer() )
|
||||
{
|
||||
// The dynamic_cast here shouldn't be any more of a performance concern than the
|
||||
// dynamic_cast that would be necessary when this PaintPowerInfo_t was added in the first place.
|
||||
const IPaintableEntity* pPaintableEnt = dynamic_cast< const IPaintableEntity* >( pEnt );
|
||||
if( pPaintableEnt )
|
||||
{
|
||||
info.m_PaintPowerType = pPaintableEnt->GetPaintPowerAtPoint( info.m_ContactPoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Use the render color of the owner if this entity is a bone follower
|
||||
if( FClassnameIs( pEnt, "phys_bone_follower" ) )
|
||||
{
|
||||
CBaseEntity *pBoneOwner = pEnt->GetOwnerEntity();
|
||||
|
||||
if( pBoneOwner )
|
||||
{
|
||||
info.m_PaintPowerType = MapColorToPower( pBoneOwner->GetRenderColor() );
|
||||
}
|
||||
}
|
||||
else //Use the render color of this entity
|
||||
{
|
||||
info.m_PaintPowerType = MapColorToPower( pEnt->GetRenderColor() );
|
||||
}
|
||||
|
||||
if( sv_enable_paint_power_user_debug.GetBool() )
|
||||
{
|
||||
Warning( "Non-world, non-player entity, %s, doesn't implement IPaintableEntity. See the global version of MapSurfacesToPowers() in paint_power_user_interface.cpp.\n", pEnt->GetClassname() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( sv_debug_draw_contacts.GetInt() == 2 )
|
||||
{
|
||||
Color color = MapPowerToVisualColor( info.m_PaintPowerType );
|
||||
NDebugOverlay::Sphere( info.m_ContactPoint, 5.0f, color.r(), color.g(), color.b(), true, 0 );
|
||||
NDebugOverlay::Line( info.m_ContactPoint, info.m_ContactPoint + 20.0f * info.m_SurfaceNormal, color.r(), color.g(), color.b(), true, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the interface class for all paint power users.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_POWER_USER_INTERFACE_H
|
||||
#define PAINT_POWER_USER_INTERFACE_H
|
||||
|
||||
#include <utility>
|
||||
#include "paint_color_manager.h"
|
||||
#include "paint_power_info.h"
|
||||
|
||||
typedef PaintPowerInfo_t* PaintPowerIter;
|
||||
typedef const PaintPowerInfo_t* PaintPowerConstIter;
|
||||
typedef std::pair< PaintPowerIter, PaintPowerIter > PaintPowerRange;
|
||||
typedef std::pair< PaintPowerConstIter, PaintPowerConstIter > PaintPowerConstRange;
|
||||
|
||||
abstract_class IPaintPowerUser
|
||||
{
|
||||
public:
|
||||
//-------------------------------------------------------------------------
|
||||
// Virtual Destructor
|
||||
//-------------------------------------------------------------------------
|
||||
virtual ~IPaintPowerUser();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Public Accessors
|
||||
//-------------------------------------------------------------------------
|
||||
virtual const PaintPowerConstRange GetPaintPowers() const = 0;
|
||||
virtual const PaintPowerInfo_t& GetPaintPower( unsigned powerType ) const = 0;
|
||||
virtual const PaintPowerInfo_t* FindHighestPriorityActivePaintPower() const = 0;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paint Power Effects
|
||||
//-------------------------------------------------------------------------
|
||||
virtual void AddSurfacePaintPowerInfo( const PaintPowerInfo_t& contact, char const* context = 0 ) = 0;
|
||||
virtual void UpdatePaintPowers() = 0;
|
||||
};
|
||||
|
||||
|
||||
void MapSurfaceToPower( PaintPowerInfo_t& info );
|
||||
|
||||
#endif // ifndef PAINT_POWER_USER_INTERFACE_H
|
||||
@@ -0,0 +1,201 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "shot_manipulator.h"
|
||||
#include "paint_sprayer_shared.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
#include "paint_stream_manager.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_paint_sprayer.h"
|
||||
#else
|
||||
#include "paint_sprayer.h"
|
||||
#endif
|
||||
|
||||
ConVar debug_paint_sprayer_cone("debug_paint_sprayer_cone", "0", FCVAR_REPLICATED | FCVAR_CHEAT);
|
||||
ConVar max_noisy_blobs_per_second("max_noisy_blobs_per_second", "5.0f", FCVAR_REPLICATED | FCVAR_CHEAT);
|
||||
|
||||
float const MAX_SPRAYER_SPREAD_ANGLE = 89.f;
|
||||
|
||||
void CPaintSprayer::SprayPaint( float flDeltaTime )
|
||||
{
|
||||
CPaintStream *pPaintStream = m_hPaintStream;
|
||||
if ( !pPaintStream )
|
||||
return;
|
||||
|
||||
//Don't spray silent blobs on the clients
|
||||
if( m_bSilent && gpGlobals->IsClient() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( flDeltaTime <= 0.0f )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector vecSprayDir;
|
||||
AngleVectors( GetAbsAngles(), &vecSprayDir );
|
||||
|
||||
m_flAccumulatedTime += flDeltaTime;
|
||||
|
||||
const float fireDeltaTime = 1.0f / m_flBlobsPerSecond;
|
||||
int blobsFired = 0;
|
||||
|
||||
while ( m_flAccumulatedTime > fireDeltaTime )
|
||||
{
|
||||
m_flAccumulatedTime -= fireDeltaTime;
|
||||
|
||||
if ( pPaintStream->GetBlobsCount() >= m_nMaxBlobCount )
|
||||
continue;
|
||||
|
||||
CPaintBlob *pBlob = FirePaintBlob( GetAbsOrigin(),
|
||||
GetAbsOrigin(),
|
||||
GetAbsVelocity(),
|
||||
vecSprayDir,
|
||||
m_PaintPowerType,
|
||||
m_flBlobSpreadRadius,
|
||||
m_flBlobSpreadAngle,
|
||||
m_flMinSpeed,
|
||||
m_flMaxSpeed,
|
||||
m_flStreakPercentage,
|
||||
m_flMinStreakTime,
|
||||
m_flMaxStreakTime,
|
||||
m_flMinStreakSpeedDampen,
|
||||
m_flMaxStreakSpeedDampen,
|
||||
m_bSilent,
|
||||
m_bDrawOnly,
|
||||
pPaintStream,
|
||||
m_nBlobRandomSeed );
|
||||
|
||||
if( pBlob )
|
||||
{
|
||||
m_flPercentageSinceLastNoisyBlob += m_flNoisyBlobPercentage;
|
||||
if( m_flPercentageSinceLastNoisyBlob >= 1.0f )
|
||||
{
|
||||
m_flPercentageSinceLastNoisyBlob = 0.0f;
|
||||
pBlob->SetShouldPlaySound( !m_bSilent );
|
||||
}
|
||||
}
|
||||
|
||||
pPaintStream->AddPaintBlob( pBlob );
|
||||
|
||||
++m_nBlobRandomSeed;
|
||||
++blobsFired;
|
||||
}
|
||||
|
||||
// Note: Assume that if the sprayer is dripping, it's not spawning more than
|
||||
// one blob per frame and also that, if it did, we wouldn't want more
|
||||
// than one drip sound playing from the same position in a frame or within
|
||||
// a fraction of a second in general. That takes this conditional out of
|
||||
// the loop above.
|
||||
#ifdef GAME_DLL
|
||||
if( !m_bSilent && blobsFired > 0 &&
|
||||
m_nAmbientSound == PAINT_SPRAYER_SOUND_DRIP )
|
||||
{
|
||||
CBroadcastRecipientFilter filter;
|
||||
CBaseEntity::EmitSound( filter, entindex(), "Paint.Drip", &GetAbsOrigin() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CPaintBlob* FirePaintBlob( const Vector& vecSourcePosition,
|
||||
const Vector& vecOldSourcePosition,
|
||||
const Vector& vecSourceVelocity,
|
||||
const Vector& vecSprayDir,
|
||||
int paintType,
|
||||
float flBlobSpreadRadius,
|
||||
float flBlobSpreadAngle,
|
||||
float flMinSpeed,
|
||||
float flMaxSpeed,
|
||||
float flBlobStreakPercent,
|
||||
float flMinStreakTime,
|
||||
float flMaxStreakTime,
|
||||
float flMinStreakSpeedDampen,
|
||||
float flMaxStreakSpeedDampen,
|
||||
bool bSilent,
|
||||
bool bDrawOnly,
|
||||
CBaseEntity *pOwner,
|
||||
int nRandomSeed /*= 0*/ )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
// if the client is listen server, don't create blobs twice, we don't want to double the work
|
||||
if ( engine->IsClientLocalToActiveServer() )
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
// set random seed
|
||||
RandomSeed( nRandomSeed );
|
||||
|
||||
// clamp spread angle
|
||||
flBlobSpreadAngle = clamp( flBlobSpreadAngle, 0.f, MAX_SPRAYER_SPREAD_ANGLE );
|
||||
|
||||
// random position inside the circle area
|
||||
Vector vecCircleRight, vecCircleUp;
|
||||
VectorVectors( vecSprayDir, vecCircleRight, vecCircleUp );
|
||||
vecCircleUp.NormalizeInPlace();
|
||||
VMatrix matRotate;
|
||||
MatrixBuildRotationAboutAxis( matRotate, vecSprayDir, RandomFloat( 0.f, 360.f ) );
|
||||
Vector vecBlobFirePos = vecSourcePosition + RandomFloat( 0.f, flBlobSpreadRadius ) * ( matRotate * vecCircleUp );
|
||||
|
||||
// compute cone origin
|
||||
float flDistanceToConeOrigin = 1.f;
|
||||
Vector vecConeOrigin;
|
||||
if ( AlmostEqual( flBlobSpreadAngle, 0.f ) )
|
||||
{
|
||||
vecConeOrigin = vecBlobFirePos - flDistanceToConeOrigin * vecSprayDir.Normalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
flDistanceToConeOrigin = flBlobSpreadRadius / tanf( DEG2RAD( flBlobSpreadAngle ) );
|
||||
vecConeOrigin = vecSourcePosition - flDistanceToConeOrigin * vecSprayDir.Normalized();
|
||||
}
|
||||
|
||||
// compute direction from random position
|
||||
Vector vecBlobFireDir = vecBlobFirePos - vecConeOrigin;
|
||||
if ( AlmostEqual( flBlobSpreadRadius, 0.f ) )
|
||||
{
|
||||
float flSpread = sin( DEG2RAD( flBlobSpreadAngle )/2.0f );
|
||||
Vector vecSpraySpread( flSpread, flSpread, flSpread );
|
||||
CShotManipulator shotManipulator( vecSprayDir );
|
||||
vecBlobFireDir = shotManipulator.ApplySpread( vecSpraySpread );
|
||||
}
|
||||
vecBlobFireDir.NormalizeInPlace();
|
||||
|
||||
if ( debug_paint_sprayer_cone.GetBool() )
|
||||
{
|
||||
QAngle debugAngle;
|
||||
VectorAngles( vecSprayDir, debugAngle );
|
||||
#ifdef CLIENT_DLL
|
||||
NDebugOverlay::Circle( vecSourcePosition, debugAngle, flBlobSpreadRadius, 255, 0, 0, 128, true, 0.1f );
|
||||
NDebugOverlay::Line( vecBlobFirePos, vecBlobFirePos + 20 * vecBlobFireDir, 255, 255, 0, true, 1.f );
|
||||
#else
|
||||
NDebugOverlay::Circle( vecSourcePosition, debugAngle, flBlobSpreadRadius, 0, 255, 0, 128, true, 0.1f );
|
||||
NDebugOverlay::Line( vecBlobFirePos, vecBlobFirePos + 20 * vecBlobFireDir, 0, 255, 255, true, 1.f );
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector vecBlobVelocity = vecBlobFireDir * RandomFloat( flMinSpeed, flMaxSpeed ) + vecSourceVelocity;
|
||||
|
||||
//The streaking values of the blob
|
||||
float flStreakTime = 0.0f;
|
||||
float flStreakSpeedDampen = 0.0f;
|
||||
bool bShouldStreak = ( RandomFloat( 0.0f, 1.0f ) * 100.0f ) <= flBlobStreakPercent;
|
||||
if( bShouldStreak )
|
||||
{
|
||||
flStreakTime = RandomFloat( flMinStreakTime, flMaxStreakTime );
|
||||
flStreakSpeedDampen = RandomFloat( flMinStreakSpeedDampen, flMaxStreakSpeedDampen );
|
||||
}
|
||||
|
||||
CPaintBlob *pBlob = PaintStreamManager.AllocatePaintBlob( bSilent );
|
||||
if ( pBlob )
|
||||
{
|
||||
pBlob->Init( vecBlobFirePos, vecBlobVelocity, paintType, flStreakTime, flStreakSpeedDampen, pOwner, bSilent, bDrawOnly );
|
||||
}
|
||||
|
||||
return pBlob;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_SPRAYER_SHARED_H
|
||||
#define PAINT_SPRAYER_SHARED_H
|
||||
|
||||
#include "paint_blobs_shared.h"
|
||||
|
||||
CPaintBlob* FirePaintBlob( const Vector& vecSourcePosition,
|
||||
const Vector& vecOldSourcePosition,
|
||||
const Vector& vecSourceVelocity,
|
||||
const Vector& vecSprayDir,
|
||||
int paintType,
|
||||
float flBlobSpreadRadius,
|
||||
float flBlobSpreadAngle,
|
||||
float flMinSpeed,
|
||||
float flMaxSpeed,
|
||||
float flBlobStreakPercent,
|
||||
float flMinStreakTime,
|
||||
float flMaxStreakTime,
|
||||
float flMinStreakSpeedDampen,
|
||||
float flMaxStreakSpeedDampen,
|
||||
bool bSilent,
|
||||
bool bDrawOnly,
|
||||
CBaseEntity *pOwner,
|
||||
int nRandomSeed = 0 );
|
||||
|
||||
enum BlobRenderMode_t
|
||||
{
|
||||
BLOB_RENDER_BLOBULATOR = 0,
|
||||
BLOB_RENDER_FAST_SPHERE,
|
||||
TOTAL_BLOB_RENDER_TYPE
|
||||
};
|
||||
|
||||
#endif //PAINT_SPRAYER_SHARED_H
|
||||
@@ -0,0 +1,563 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "paint_stream_manager.h"
|
||||
#include "paint_blobs_shared.h"
|
||||
#include "paint_sprayer_shared.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
#include "fmtstr.h"
|
||||
#include "vprof.h"
|
||||
#include "paint_stream_shared.h"
|
||||
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define VPROF_BUDGETGROUP_PAINTBLOB _T("Paintblob")
|
||||
|
||||
const char* const CPaintStreamManager::m_pPaintMaterialNames[PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER] =
|
||||
{
|
||||
"paintblobs/blob_surface_bounce",
|
||||
"paintblobs/blob_surface_stick", // FIXME: Bring this back for DLC2 "paintblobs/blob_surface_reflect",
|
||||
"paintblobs/blob_surface_speed",
|
||||
"paintblobs/blob_surface_portal",
|
||||
"paintblobs/blob_surface_erase"
|
||||
};
|
||||
|
||||
const char* const CPaintStreamManager::s_SoundEffectNames[PAINT_IMPACT_EFFECT_COUNT] =
|
||||
{
|
||||
"Paintblob.Impact",
|
||||
"Paintblob.ImpactDrip"
|
||||
};
|
||||
|
||||
//Paint particle impact effect convars
|
||||
ConVar paint_impact_particles_distance_threshold( "paint_impact_particles_distance_threshold", "20.0f", FCVAR_REPLICATED );
|
||||
ConVar paint_impact_particles_duration( "paint_impact_particles_duration", "0.2f", FCVAR_REPLICATED );
|
||||
ConVar paint_min_impact_particles( "paint_min_impact_particles", "20", FCVAR_REPLICATED );
|
||||
ConVar paint_max_impact_particles( "paint_max_impact_particles", "50", FCVAR_REPLICATED );
|
||||
|
||||
ConVar paint_impact_accumulate_sound_distance_threshold( "paint_impact_accumulate_sound_distance_threshold", "128.0f", FCVAR_REPLICATED );
|
||||
ConVar paint_impact_count_to_max_adjusted_volume( "paint_impact_count_to_max_adjusted_volume", "5", FCVAR_REPLICATED );
|
||||
ConVar paint_impact_count_to_min_adjusted_pitch_after_full_volume( "paint_impact_count_to_min_adjusted_pitch_after_full_volume", "5", FCVAR_REPLICATED );
|
||||
ConVar min_adjusted_pitch_percentage( "min_adjusted_pitch_percentage", "0.85", FCVAR_REPLICATED );
|
||||
|
||||
ConVar draw_paint_splat_particles( "draw_paint_splat_particles", "1", FCVAR_REPLICATED );
|
||||
|
||||
ConVar group_paint_impact_effects( "cl_group_paint_impact_effects", "1", FCVAR_REPLICATED );
|
||||
ConVar debug_paint_impact_effects( "debug_paint_impact_effects", "0", FCVAR_REPLICATED );
|
||||
|
||||
ConVar blobs_paused("blobs_paused", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
|
||||
CPaintStreamManager PaintStreamManager( "PaintStreamManager" );
|
||||
|
||||
CPaintStreamManager::CPaintStreamManager( char const *name )
|
||||
: CAutoGameSystemPerFrame( name )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CPaintStreamManager::~CPaintStreamManager( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::LevelInitPreEntity()
|
||||
{
|
||||
blobs_paused.SetValue( true );
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::LevelInitPostEntity()
|
||||
{
|
||||
blobs_paused.SetValue( false );
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::LevelShutdownPostEntity()
|
||||
{
|
||||
m_PaintImpactParticles.RemoveAll();
|
||||
|
||||
if ( m_pBlobPool )
|
||||
{
|
||||
m_pBlobPool->Clear();
|
||||
delete m_pBlobPool;
|
||||
m_pBlobPool = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::AllocatePaintBlobPool( int nMaxBlobs )
|
||||
{
|
||||
int nMaxCount = ( nMaxBlobs ) ? nMaxBlobs : 250;
|
||||
// pre-allocate pool of blobs
|
||||
if ( !m_pBlobPool )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
m_pBlobPool = new CClassMemoryPool< CPaintBlob >( nMaxCount, CUtlMemoryPool::GROW_NONE );
|
||||
#else
|
||||
if ( !engine->IsClientLocalToActiveServer() )
|
||||
{
|
||||
m_pBlobPool = new CClassMemoryPool< CPaintBlob >( nMaxCount, CUtlMemoryPool::GROW_NONE );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if ( ( m_pBlobPool->Size() / m_pBlobPool->BlockSize() ) != nMaxBlobs )
|
||||
{
|
||||
Assert( 0 );
|
||||
Warning( "CPaintStreamManager::AllocatePaintBlobPool is being called multiple times (for some reasons) with different pool sizes." );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::RemoveAllPaintBlobs( void )
|
||||
{
|
||||
for( int i = 0; i < IPaintStreamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CPaintStream *pStream = static_cast< CPaintStream* >( IPaintStreamAutoList::AutoList()[i] );
|
||||
if( pStream )
|
||||
{
|
||||
pStream->RemoveAllPaintBlobs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPaintBlob* CPaintStreamManager::AllocatePaintBlob( bool bSilent /*= false*/ )
|
||||
{
|
||||
// don't create when blob is paused
|
||||
if ( blobs_paused.GetBool() )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
if ( bSilent )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_pBlobPool->Alloc();
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::FreeBlob( CPaintBlob* pBlob )
|
||||
{
|
||||
m_pBlobPool->Free( pBlob );
|
||||
}
|
||||
|
||||
|
||||
const char *CPaintStreamManager::GetPaintMaterialName( int type )
|
||||
{
|
||||
return m_pPaintMaterialNames[ type ];
|
||||
}
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
void CPaintStreamManager::Update( float frametime )
|
||||
{
|
||||
PaintStreamUpdate();
|
||||
|
||||
//Update the particle and sound impact effects
|
||||
UpdatePaintImpactEffects( frametime, m_PaintImpactParticles );
|
||||
|
||||
//Display a list of all the paint impact effects currently playing
|
||||
if( debug_paint_impact_effects.GetBool() )
|
||||
{
|
||||
int line = 6;
|
||||
float lineHeight = 0.015;
|
||||
|
||||
float startX = 0.01f;
|
||||
float startY = 0.0f;
|
||||
|
||||
CFmtStr msg;
|
||||
|
||||
msg.sprintf( "Paint blob impact particles: %d", m_PaintImpactParticles.Count() );
|
||||
NDebugOverlay::ScreenText( startX, startY + (line * lineHeight), msg, 0, 255, 255, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void CPaintStreamManager::PreClientUpdate( void )
|
||||
{
|
||||
PaintStreamUpdate();
|
||||
|
||||
//engine->Con_NPrintf( 0, "num blobs = %d", GetBlobCount() );
|
||||
|
||||
//Update the particle and sound impact effects
|
||||
UpdatePaintImpactEffects( gpGlobals->frametime, m_PaintImpactParticles );
|
||||
|
||||
//Display a list of all the paint impact effects currently playing
|
||||
if( debug_paint_impact_effects.GetBool() )
|
||||
{
|
||||
int line = 6;
|
||||
float lineHeight = 0.015;
|
||||
|
||||
float startX = 0.01f;
|
||||
float startY = 0.0f;
|
||||
|
||||
CFmtStr msg;
|
||||
|
||||
msg.sprintf( "Paint blob impact particles: %d", m_PaintImpactParticles.Count() );
|
||||
NDebugOverlay::ScreenText( startX, startY + (line * lineHeight), msg, 0, 255, 255, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void CPaintStreamManager::PaintStreamUpdate()
|
||||
{
|
||||
VPROF_BUDGET( "CPaintStreamManager::PaintStreamUpdate", VPROF_BUDGETGROUP_PAINTBLOB );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// if the client is local to server, only update render bounds
|
||||
// let the server do all the work.
|
||||
if ( engine->IsClientLocalToActiveServer() )
|
||||
{
|
||||
for ( int i = 0; i < IPaintStreamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CPaintStream *pStream = static_cast< CPaintStream* >( IPaintStreamAutoList::AutoList()[i] );
|
||||
if ( pStream )
|
||||
{
|
||||
pStream->UpdateRenderBoundsAndOriginWorldspace();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// remove dead blobs from beam list before we set dead blobs to NULL
|
||||
CTrigger_TractorBeam_Shared::RemoveDeadBlobsFromBeams();
|
||||
|
||||
// we want to update blob collision for all blobs at once
|
||||
PaintBlobVector_t allBlobs;
|
||||
|
||||
// preupdate (delete blobs from streams)
|
||||
for ( int i = 0; i < IPaintStreamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CPaintStream *pStream = static_cast< CPaintStream* >( IPaintStreamAutoList::AutoList()[i] );
|
||||
if ( pStream )
|
||||
{
|
||||
pStream->PreUpdateBlobs();
|
||||
|
||||
int numCurrentBlobs = allBlobs.Count();
|
||||
int numNewBlobs = pStream->GetBlobList().Count();
|
||||
allBlobs.EnsureCount( allBlobs.Count() + numNewBlobs );
|
||||
V_memcpy( allBlobs.Base() + numCurrentBlobs, pStream->GetBlobList().Base(), numNewBlobs * sizeof( CBasePaintBlob* ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// copy blobs from all stream and update all of them
|
||||
if ( blobs_paused.GetBool() )
|
||||
{
|
||||
for ( int i=0; i<allBlobs.Count(); ++i )
|
||||
{
|
||||
allBlobs[i]->SetLastUpdateTime( gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// update all blobs
|
||||
PaintBlobUpdate( allBlobs );
|
||||
}
|
||||
|
||||
// post update
|
||||
for ( int i = 0; i < IPaintStreamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CPaintStream *pStream = static_cast< CPaintStream* >( IPaintStreamAutoList::AutoList()[i] );
|
||||
if ( pStream )
|
||||
{
|
||||
pStream->PostUpdateBlobs();
|
||||
}
|
||||
}
|
||||
|
||||
// remove blobs that change beams to correct blob list in beam
|
||||
CTrigger_TractorBeam_Shared::RemoveBlobsFromPreviousBeams();
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::UpdatePaintImpactEffects( float flDeltaTime, PaintBlobImpactEffectVector_t &paintImpactEffects )
|
||||
{
|
||||
//Update the paint blob impact effects
|
||||
for( int i = 0; i < paintImpactEffects.Count(); ++i )
|
||||
{
|
||||
PaintBlobImpactEffect_t *pEffect = &paintImpactEffects[i];
|
||||
|
||||
if ( pEffect )
|
||||
{
|
||||
//Decrement the timer of the effect
|
||||
pEffect->flTime -= flDeltaTime;
|
||||
|
||||
//Remove the effect if it has finished playing
|
||||
if( pEffect->flTime <= 0.0f )
|
||||
{
|
||||
paintImpactEffects.FastRemove( i-- );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPaintStreamManager::CreatePaintImpactParticles( const Vector &vecPosition, const Vector &vecNormal, int paintType )
|
||||
{
|
||||
//Check if the impact particle effect should be played
|
||||
if( ShouldPlayImpactEffect( vecPosition,
|
||||
m_PaintImpactParticles,
|
||||
paint_min_impact_particles.GetInt(),
|
||||
paint_max_impact_particles.GetInt(),
|
||||
paint_impact_particles_distance_threshold.GetFloat() * paint_impact_particles_distance_threshold.GetFloat() ) )
|
||||
{
|
||||
PlayPaintImpactParticles( vecPosition, vecNormal, paintType );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CPaintStreamManager::ShouldPlayImpactEffect( const Vector& vecPosition, PaintBlobImpactEffectVector_t &paintImpactEffects, int minEffects, int maxEffects, float flDistanceThresholdSqr )
|
||||
{
|
||||
if( !group_paint_impact_effects.GetBool() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int iImpactEffectCount = paintImpactEffects.Count();
|
||||
|
||||
//If we are below the min threshold then play the paint impact effect
|
||||
if( iImpactEffectCount < minEffects )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//Don't play any paint impact effect if we are above the max
|
||||
else if( iImpactEffectCount >= maxEffects )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int iEffectIndex = 0;
|
||||
|
||||
//Don't play the effect if it's too close to another paint impact effect
|
||||
for ( iEffectIndex = 0; iEffectIndex < iImpactEffectCount; ++iEffectIndex )
|
||||
{
|
||||
PaintBlobImpactEffect_t *pEffect = &paintImpactEffects[iEffectIndex];
|
||||
|
||||
if ( pEffect )
|
||||
{
|
||||
//Check if this effect is too close to a effect already playing
|
||||
if ( vecPosition.DistToSqr( pEffect->vecPosition ) < ( flDistanceThresholdSqr ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//OK to play the effect
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct SplatParticlesForPaint_t
|
||||
{
|
||||
int nPaintType;
|
||||
const char *lpszParticleSystemName;
|
||||
};
|
||||
|
||||
SplatParticlesForPaint_t paintSplatCallbacks[] =
|
||||
{
|
||||
{ BOUNCE_POWER, "paint_splat_bounce_01" },
|
||||
{ REFLECT_POWER,"paint_splat_stick_01" }, // FIXME: Bring this back for DLC2 { REFLECT_POWER,"paint_splat_reflect_01" },
|
||||
{ SPEED_POWER, "paint_splat_speed_01" },
|
||||
{ PORTAL_POWER, "paint_splat_erase_01" },
|
||||
{ NO_POWER, "paint_splat_erase_01" },
|
||||
};
|
||||
|
||||
void PaintSplatEffect( const Vector& vecPosition, const Vector& vecNormal, int paintType )
|
||||
{
|
||||
Assert( paintType >= 0 && paintType < ARRAYSIZE( paintSplatCallbacks ) );
|
||||
Assert( paintSplatCallbacks[paintType].nPaintType == paintType );
|
||||
|
||||
QAngle angle;
|
||||
VectorAngles( -vecNormal, angle );
|
||||
|
||||
CBasePlayer *pPlayer = NULL;
|
||||
#ifdef GAME_DLL
|
||||
if ( !engine->IsDedicatedServer() )
|
||||
{
|
||||
pPlayer = UTIL_GetLocalPlayerOrListenServerHost();
|
||||
}
|
||||
#else
|
||||
pPlayer = GetSplitScreenViewPlayer();
|
||||
#endif
|
||||
|
||||
if ( pPlayer )
|
||||
{
|
||||
CSingleUserRecipientFilter filter( pPlayer );
|
||||
DispatchParticleEffect( paintSplatCallbacks[paintType].lpszParticleSystemName, vecPosition, angle, NULL, -1, &filter );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPaintStreamManager::PlayPaintImpactParticles( const Vector &vecPosition, const Vector &vecNormal, int paintType )
|
||||
{
|
||||
//Play the particle effect for the impact
|
||||
if( draw_paint_splat_particles.GetBool() )
|
||||
{
|
||||
PaintSplatEffect( vecPosition, vecNormal, paintType );
|
||||
}
|
||||
|
||||
//Add the effect to the list
|
||||
int iEffectIndex = m_PaintImpactParticles.AddToTail();
|
||||
m_PaintImpactParticles[iEffectIndex].vecPosition = vecPosition;
|
||||
m_PaintImpactParticles[iEffectIndex].flTime = paint_impact_particles_duration.GetFloat();
|
||||
}
|
||||
|
||||
|
||||
float CPaintStreamManager::PlayPaintImpactSound( const Vector &vecPosition, PaintImpactEffect impactEffect )
|
||||
{
|
||||
//Emit the sound for the impact
|
||||
#ifdef GAME_DLL
|
||||
CBasePlayer *pRecipient = UTIL_GetLocalPlayerOrListenServerHost();
|
||||
if ( pRecipient == NULL )
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
CSingleUserRecipientFilter filter( pRecipient );
|
||||
#else
|
||||
CLocalPlayerFilter filter;
|
||||
#endif
|
||||
|
||||
const char* soundName = GetPaintSoundEffectName( impactEffect );
|
||||
CBaseEntity::EmitSound( filter, 0, soundName, &vecPosition );
|
||||
return CBaseEntity::GetSoundDuration( soundName, NULL );
|
||||
}
|
||||
|
||||
typedef CUtlVectorFixedGrowable< Vector, 16 > AccumulatedSoundPositionVector;
|
||||
struct AccumulatedImpactSound
|
||||
{
|
||||
CSoundParameters soundParams;
|
||||
AccumulatedSoundPositionVector positions;
|
||||
float volumeIncreasePerImpact;
|
||||
int pitchDecreasePerFullVolumeImpact;
|
||||
int minAdjustedPitch;
|
||||
|
||||
void Initialize( const Vector& center, const char* soundName );
|
||||
};
|
||||
|
||||
void AccumulatedImpactSound::Initialize( const Vector& center, const char* soundName )
|
||||
{
|
||||
positions.AddToTail( center );
|
||||
|
||||
if( CBaseEntity::GetParametersForSound( soundName, soundParams, NULL ) )
|
||||
{
|
||||
const int impactsToMinPitch = paint_impact_count_to_min_adjusted_pitch_after_full_volume.GetInt();
|
||||
minAdjustedPitch = min_adjusted_pitch_percentage.GetFloat() * soundParams.pitch + 0.5f;
|
||||
const int deltaToMinPitch = soundParams.pitch - minAdjustedPitch;
|
||||
pitchDecreasePerFullVolumeImpact = static_cast<float>(deltaToMinPitch) / impactsToMinPitch + 0.5f;
|
||||
const int impactsToFullVolume = paint_impact_count_to_max_adjusted_volume.GetInt();
|
||||
const float deltaToFullVolume = VOL_NORM - soundParams.volume;
|
||||
volumeIncreasePerImpact = deltaToFullVolume / impactsToFullVolume;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(!"GetParametersForSound() failed.");
|
||||
}
|
||||
}
|
||||
|
||||
typedef CUtlVectorFixedGrowable< AccumulatedImpactSound, 32 > AccumulatedImpactSoundVector;
|
||||
|
||||
void CPaintStreamManager::PlayMultiplePaintImpactSounds( TimeStampVector& channelTimeStamps, int maxChannels, const PaintImpactPositionVector& positions, PaintImpactEffect impactEffect )
|
||||
{
|
||||
const char* soundName = GetPaintSoundEffectName( impactEffect );
|
||||
|
||||
Assert( positions.Count() > 0 && soundName != NULL );
|
||||
|
||||
const int maxChannelsToAdd = imax( maxChannels - channelTimeStamps.Count(), 0 );
|
||||
if( positions.Count() > 0 && soundName != NULL && maxChannelsToAdd > 0 )
|
||||
{
|
||||
AccumulatedImpactSoundVector accumulatedSounds;
|
||||
accumulatedSounds.AddToTail();
|
||||
accumulatedSounds.Tail().Initialize( positions.Head(), soundName );
|
||||
|
||||
const float maxRadiusSq = Sqr( paint_impact_accumulate_sound_distance_threshold.GetFloat() );
|
||||
|
||||
// For each position
|
||||
for( int positionIndex = 1; positionIndex < positions.Count(); ++positionIndex )
|
||||
{
|
||||
// Check if the position is close enough to the center of an accumulated impact sound
|
||||
// Note: "Center" is just the first position in the list.
|
||||
const Vector& soundPosition = positions[positionIndex];
|
||||
bool positionAccumulated = false;
|
||||
for( int accumSoundIndex = 0; accumSoundIndex < accumulatedSounds.Count(); ++accumSoundIndex )
|
||||
{
|
||||
AccumulatedImpactSound& sound = accumulatedSounds[accumSoundIndex];
|
||||
const Vector& center = sound.positions.Head();
|
||||
if( (center - soundPosition).LengthSqr() < maxRadiusSq )
|
||||
{
|
||||
sound.positions.AddToTail( soundPosition );
|
||||
int& pitch = sound.soundParams.pitch;
|
||||
float& volume = sound.soundParams.volume;
|
||||
const int adjustedPitch = pitch - isel( VOL_NORM - volume, sound.pitchDecreasePerFullVolumeImpact, 0 );
|
||||
pitch = imax( adjustedPitch, sound.minAdjustedPitch );
|
||||
volume = fpmin( volume + sound.volumeIncreasePerImpact, VOL_NORM );
|
||||
positionAccumulated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !positionAccumulated && accumulatedSounds.Count() < maxChannelsToAdd )
|
||||
{
|
||||
accumulatedSounds.AddToTail();
|
||||
accumulatedSounds.Tail().Initialize( soundPosition, soundName );
|
||||
}
|
||||
}
|
||||
|
||||
// Play each accumulated sound
|
||||
for( int accumSoundIndex = 0; accumSoundIndex < accumulatedSounds.Count(); ++accumSoundIndex )
|
||||
{
|
||||
AccumulatedImpactSound& sound = accumulatedSounds[accumSoundIndex];
|
||||
|
||||
// Find the average position
|
||||
const AccumulatedSoundPositionVector& soundPositions = sound.positions;
|
||||
Vector averagedCenter = std::accumulate( soundPositions.Base(), soundPositions.Base() + soundPositions.Count(), vec3_origin );
|
||||
averagedCenter /= soundPositions.Count();
|
||||
|
||||
// Emit the sound
|
||||
EmitSound_t emitParams( sound.soundParams );
|
||||
emitParams.m_pOrigin = &averagedCenter;
|
||||
const float duration = PlayPaintImpactSound( emitParams );
|
||||
|
||||
// Update the number of used channels
|
||||
channelTimeStamps.AddToTail( gpGlobals->curtime + duration );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* CPaintStreamManager::GetPaintSoundEffectName( unsigned int impactEffect )
|
||||
{
|
||||
return impactEffect < PAINT_IMPACT_EFFECT_COUNT ? s_SoundEffectNames[impactEffect] : NULL;
|
||||
}
|
||||
|
||||
float CPaintStreamManager::PlayPaintImpactSound( const EmitSound_t& emitParams )
|
||||
{
|
||||
//Emit the sound for the impact
|
||||
#ifdef GAME_DLL
|
||||
CBasePlayer *pRecipient = UTIL_GetLocalPlayerOrListenServerHost();
|
||||
if ( pRecipient == NULL )
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
CSingleUserRecipientFilter filter( pRecipient );
|
||||
#else
|
||||
CLocalPlayerFilter filter;
|
||||
#endif
|
||||
|
||||
CBaseEntity::EmitSound( filter, 0, emitParams );
|
||||
return CBaseEntity::GetSoundDuration( emitParams.m_pSoundName, NULL ); // This will generate a "should use game_sounds.txt" warning, but the sound name comes from game_sounds.txt. The warning is benign.
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_STREAM_MANAGER_H
|
||||
#define PAINT_STREAM_MANAGER_H
|
||||
|
||||
#include "paint_color_manager.h"
|
||||
#include "mempool.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_baseentity.h"
|
||||
#include "c_paintblob.h"
|
||||
#else
|
||||
#include "baseentity.h"
|
||||
#include "cpaintblob.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
class C_PaintStream;
|
||||
#else
|
||||
class CPaintStream;
|
||||
#endif
|
||||
|
||||
class CBasePaintBlob;
|
||||
|
||||
//The blob impact effects. (Only on client)
|
||||
struct PaintBlobImpactEffect_t
|
||||
{
|
||||
Vector vecPosition;
|
||||
float flTime;
|
||||
};
|
||||
|
||||
enum PaintImpactEffect
|
||||
{
|
||||
PAINT_STREAM_IMPACT_EFFECT,
|
||||
PAINT_DRIP_IMPACT_EFFECT,
|
||||
|
||||
PAINT_IMPACT_EFFECT_COUNT
|
||||
};
|
||||
|
||||
|
||||
typedef CUtlVector<PaintBlobImpactEffect_t> PaintBlobImpactEffectVector_t;
|
||||
typedef float TimeStamp;
|
||||
typedef CUtlVector<TimeStamp> TimeStampVector;
|
||||
typedef CUtlVectorFixedGrowable<Vector, 32> PaintImpactPositionVector;
|
||||
|
||||
class CPaintStreamManager : public CAutoGameSystemPerFrame
|
||||
{
|
||||
public:
|
||||
CPaintStreamManager( char const *name );
|
||||
~CPaintStreamManager();
|
||||
|
||||
//CAutoGameSystem members
|
||||
virtual char const *Name() { return "PaintStreamManager"; }
|
||||
virtual void LevelInitPreEntity();
|
||||
virtual void LevelInitPostEntity();
|
||||
virtual void LevelShutdownPostEntity();
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual void Update( float frametime );
|
||||
#else
|
||||
virtual void PreClientUpdate( void );
|
||||
#endif
|
||||
|
||||
void RemoveAllPaintBlobs( void );
|
||||
|
||||
const char *GetPaintMaterialName( int type );
|
||||
|
||||
void CreatePaintImpactParticles( const Vector &vecPosition, const Vector &vecNormal, int paintType );
|
||||
float PlayPaintImpactSound( const Vector &vecPosition, PaintImpactEffect impactEffect );
|
||||
void PlayMultiplePaintImpactSounds( TimeStampVector& channelTimeStamps, int maxChannels, const PaintImpactPositionVector& positions, PaintImpactEffect impactEffect );
|
||||
|
||||
void QueuePaintImpactEffect( const Vector &vecPosition, const Vector &vecNormal, int paintType, PaintImpactEffect impactEffect );
|
||||
|
||||
void AllocatePaintBlobPool( int nMaxBlobs );
|
||||
CPaintBlob* AllocatePaintBlob( bool bSilent = false );
|
||||
void FreeBlob( CPaintBlob* pBlob );
|
||||
|
||||
private:
|
||||
|
||||
void PaintStreamUpdate();
|
||||
|
||||
void UpdatePaintImpactEffects( float flDeltaTime, PaintBlobImpactEffectVector_t &paintImpactEffects );
|
||||
bool ShouldPlayImpactEffect( const Vector& vecPosition, PaintBlobImpactEffectVector_t &paintImpactEffects, int minEffects, int maxEffects, float flDistanceThreshold );
|
||||
void PlayPaintImpactParticles( const Vector &vecPosition, const Vector &vecNormal, int paintType );
|
||||
PaintBlobImpactEffectVector_t m_PaintImpactParticles;
|
||||
|
||||
static const char* const m_pPaintMaterialNames[PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER];
|
||||
|
||||
float PlayPaintImpactSound( const EmitSound_t& emitParams );
|
||||
static const char* GetPaintSoundEffectName( unsigned int impactEffect );
|
||||
static const char* const s_SoundEffectNames[PAINT_IMPACT_EFFECT_COUNT];
|
||||
CClassMemoryPool< CPaintBlob > *m_pBlobPool;
|
||||
};
|
||||
|
||||
extern CPaintStreamManager PaintStreamManager;
|
||||
|
||||
#endif //PAINT_STREAM_MANAGER_H
|
||||
@@ -0,0 +1,232 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include "paint_stream_shared.h"
|
||||
#include "paint_stream_manager.h"
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
#include "paint_power_user.h"
|
||||
#include "vstdlib/jobthread.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#include "collisionutils.h"
|
||||
|
||||
|
||||
#include "c_paint_stream.h"
|
||||
#include "c_paintblob.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "paint_stream.h"
|
||||
#include "cpaintblob.h"
|
||||
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define VPROF_BUDGETGROUP_PAINTBLOB _T("Paintblob")
|
||||
|
||||
|
||||
ConVar max_sound_channels_per_paint_stream("max_sound_channels_per_paint_stream", "7", FCVAR_REPLICATED | FCVAR_CHEAT);
|
||||
|
||||
|
||||
IMPLEMENT_SHAREDCLASS_DT( CPaintStream )
|
||||
SharedProp( m_sharedBlobData )
|
||||
SharedProp( m_sharedBlobDataMutex )
|
||||
END_SHARED_TABLE()
|
||||
|
||||
|
||||
void CPaintStream::UpdateOnRemove()
|
||||
{
|
||||
RemoveAllPaintBlobs();
|
||||
|
||||
BaseClass::UpdateOnRemove();
|
||||
}
|
||||
|
||||
|
||||
void CPaintStream::RemoveAllPaintBlobs( void )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
RemoveFromLeafSystem();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
unsigned int CPaintStream::GetBlobsCount() const
|
||||
{
|
||||
return m_blobs.Count();
|
||||
}
|
||||
|
||||
|
||||
CPaintBlob* CPaintStream::GetBlob( int id )
|
||||
{
|
||||
return m_blobs[id];
|
||||
}
|
||||
|
||||
|
||||
struct ShouldNotDeleteBlob_t : std::unary_function< CPaintBlob*, bool >
|
||||
{
|
||||
inline bool operator()( const CPaintBlob* pBlob ) const
|
||||
{
|
||||
return !pBlob->ShouldDeleteThis();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void CPaintStream::RemoveDeadBlobs()
|
||||
{
|
||||
CPaintBlob** begin = GetBegin( m_blobs );
|
||||
CPaintBlob** end = GetEnd( m_blobs );
|
||||
CPaintBlob** middle = std::partition( begin, end, ShouldNotDeleteBlob_t() );
|
||||
for( CPaintBlob** i = middle; i != end; ++i )
|
||||
{
|
||||
PaintStreamManager.FreeBlob( *i );
|
||||
}
|
||||
|
||||
int numRemoved = end - middle;
|
||||
m_blobs.RemoveMultipleFromTail( numRemoved );
|
||||
}
|
||||
|
||||
|
||||
struct TimeElapsed : public std::unary_function<TimeStamp, bool>
|
||||
{
|
||||
TimeStamp m_CurrentTime;
|
||||
|
||||
TimeElapsed( TimeStamp currentTime ) : m_CurrentTime( currentTime ) {}
|
||||
|
||||
inline bool operator()( TimeStamp time ) const
|
||||
{
|
||||
return time <= m_CurrentTime;
|
||||
}
|
||||
};
|
||||
|
||||
void CPaintStream::QueuePaintEffect()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
// if not listen server, don't do anything
|
||||
if ( engine->IsDedicatedServer() )
|
||||
return;
|
||||
#else
|
||||
// if we are listen server, don't do anything on client
|
||||
if ( engine->IsClientLocalToActiveServer() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Update how many channels are in use
|
||||
TimeElapsed timeElapsedPred( gpGlobals->curtime );
|
||||
TimeStamp* begin = m_UsedChannelTimestamps.Base();
|
||||
TimeStamp* end = begin + m_UsedChannelTimestamps.Count();
|
||||
TimeStamp* newEnd = std::remove_if( begin, end, timeElapsedPred );
|
||||
m_UsedChannelTimestamps.RemoveMultipleFromTail( end - newEnd );
|
||||
|
||||
// Try to queue up effects for each impact that occurred
|
||||
PaintImpactEffect const impactEffect = (m_nRenderMode == BLOB_RENDER_BLOBULATOR) ? PAINT_STREAM_IMPACT_EFFECT : PAINT_DRIP_IMPACT_EFFECT;
|
||||
int const maxChannels = max_sound_channels_per_paint_stream.GetInt();
|
||||
|
||||
CUtlVectorFixedGrowable<Vector, 32> soundPositions;
|
||||
|
||||
for ( int i = 0; i < m_blobs.Count(); ++i )
|
||||
{
|
||||
CPaintBlob *pBlob = m_blobs[i];
|
||||
if ( pBlob->ShouldPlayEffect() && !pBlob->IsSilent() )
|
||||
{
|
||||
PaintStreamManager.CreatePaintImpactParticles( pBlob->GetPosition(), pBlob->GetContactNormal(), m_nPaintType );
|
||||
if( pBlob->ShouldPlaySound() )
|
||||
soundPositions.AddToTail( pBlob->GetPosition() );
|
||||
}
|
||||
}
|
||||
|
||||
if( soundPositions.Count() != 0 )
|
||||
PaintStreamManager.PlayMultiplePaintImpactSounds( m_UsedChannelTimestamps, maxChannels, soundPositions, impactEffect );
|
||||
}
|
||||
|
||||
|
||||
void CPaintStream::PreUpdateBlobs()
|
||||
{
|
||||
RemoveDeadBlobs();
|
||||
DebugDrawBlobs();
|
||||
}
|
||||
|
||||
|
||||
void CPaintStream::PostUpdateBlobs()
|
||||
{
|
||||
RemoveTeleportedThisFrameBlobs();
|
||||
|
||||
UpdateRenderBoundsAndOriginWorldspace();
|
||||
QueuePaintEffect();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
AddPaintToDatabase();
|
||||
UpdateBlobSharedData();
|
||||
#endif
|
||||
|
||||
// reset blobs teleported this frame flag
|
||||
ResetBlobsTeleportedThisFrame();
|
||||
}
|
||||
|
||||
|
||||
const Vector& CPaintStream::WorldAlignMins() const
|
||||
{
|
||||
return m_vCachedWorldMins;
|
||||
}
|
||||
|
||||
|
||||
const Vector& CPaintStream::WorldAlignMaxs() const
|
||||
{
|
||||
return m_vCachedWorldMaxs;
|
||||
}
|
||||
|
||||
|
||||
struct TeleportedThisFrameBlob_t : std::unary_function< CPaintBlob*, bool >
|
||||
{
|
||||
TeleportedThisFrameBlob_t( int nMaxTeleportationCount ) : m_nMaxTeleportationCount( nMaxTeleportationCount )
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator()( const CPaintBlob* pBlob ) const
|
||||
{
|
||||
return pBlob->HasBlobTeleportedThisFrame() && ( pBlob->GetTeleportationCount() >= m_nMaxTeleportationCount );
|
||||
}
|
||||
|
||||
int m_nMaxTeleportationCount;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
// Purpose: Remove blobs that have teleported this frame if the stream reaches max blob count
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
void CPaintStream::RemoveTeleportedThisFrameBlobs()
|
||||
{
|
||||
if ( m_nRenderMode == BLOB_RENDER_FAST_SPHERE )
|
||||
{
|
||||
Assert( m_blobs.Count() <= m_nMaxBlobCount );
|
||||
if ( m_blobs.Count() == m_nMaxBlobCount )
|
||||
{
|
||||
CPaintBlob** begin = GetBegin( m_blobs );
|
||||
CPaintBlob** end = GetEnd( m_blobs );
|
||||
CPaintBlob** middle = std::partition( begin, end, TeleportedThisFrameBlob_t( 4 ) );
|
||||
for( CPaintBlob** i = begin; i != middle; ++i )
|
||||
{
|
||||
PaintStreamManager.FreeBlob( *i );
|
||||
}
|
||||
|
||||
int numRemoved = middle - begin;
|
||||
m_blobs.RemoveMultipleFromHead( numRemoved );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPaintStream::ResetBlobsTeleportedThisFrame()
|
||||
{
|
||||
for ( int i=0; i<m_blobs.Count(); ++i )
|
||||
{
|
||||
m_blobs[i]->SetBlobTeleportedThisFrame( false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PAINT_STREAM_SHARED_H
|
||||
#define PAINT_STREAM_SHARED_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_paint_stream.h"
|
||||
#else
|
||||
#include "paint_stream.h"
|
||||
#endif
|
||||
|
||||
#define PAINTBLOB_MAX_RADIUS 1.5f
|
||||
|
||||
|
||||
#endif //PAINT_STREAM_SHARED_H
|
||||
@@ -0,0 +1,122 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PAINTABLE_ENTITY_BASE_H
|
||||
#define PAINTABLE_ENTITY_BASE_H
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
#include "paint_database.h"
|
||||
#endif // ifndef CLIENT_DLL
|
||||
|
||||
#include "paint_color_manager.h"
|
||||
|
||||
abstract_class IPaintableEntity
|
||||
{
|
||||
public:
|
||||
virtual ~IPaintableEntity() {}
|
||||
virtual PaintPowerType GetPaintPowerAtPoint( const Vector& contact ) const = 0;
|
||||
virtual void Paint( PaintPowerType type, const Vector& worldContactPt ) = 0;
|
||||
virtual void CleansePaint() = 0;
|
||||
virtual PaintPowerType GetPaintedPower() const = 0;
|
||||
};
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
class CPaintableEntity : public BaseEntityType, public IPaintableEntity
|
||||
{
|
||||
DECLARE_CLASS( CPaintableEntity, BaseEntityType );
|
||||
DECLARE_DATADESC();
|
||||
static const datamap_t DataMapInit();
|
||||
|
||||
public:
|
||||
CPaintableEntity();
|
||||
virtual ~CPaintableEntity();
|
||||
virtual PaintPowerType GetPaintPowerAtPoint( const Vector& worldContactPt ) const;
|
||||
virtual void Paint( PaintPowerType type, const Vector& worldContactPt );
|
||||
virtual void CleansePaint();
|
||||
virtual PaintPowerType GetPaintedPower() const;
|
||||
|
||||
private:
|
||||
PaintPowerType m_iPaintPower;
|
||||
};
|
||||
|
||||
// OMFG HACK: Define the data description table. The current macros don't work with templatized classes.
|
||||
// OMFG TODO: Write a generic macro to work with templatized classes.
|
||||
template< typename BaseEntityType >
|
||||
datamap_t CPaintableEntity<BaseEntityType>::m_DataMap = CPaintableEntity<BaseEntityType>::DataMapInit();
|
||||
|
||||
template< typename BaseEntityType >
|
||||
datamap_t* CPaintableEntity<BaseEntityType>::GetDataDescMap()
|
||||
{
|
||||
return &m_DataMap;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
datamap_t* CPaintableEntity<BaseEntityType>::GetBaseMap()
|
||||
{
|
||||
datamap_t *pResult;
|
||||
DataMapAccess((BaseClass *)NULL, &pResult);
|
||||
return pResult;
|
||||
}
|
||||
|
||||
const char PAINTABLE_ENTITY_USER_DATA_CLASS_NAME[] = "PaintableEntity";
|
||||
|
||||
template< typename BaseEntityType >
|
||||
const datamap_t CPaintableEntity<BaseEntityType>::DataMapInit()
|
||||
{
|
||||
typedef CPaintableEntity<BaseEntityType> classNameTypedef;
|
||||
static CDatadescGeneratedNameHolder nameHolder(PAINTABLE_ENTITY_USER_DATA_CLASS_NAME);
|
||||
static typedescription_t dataDesc[] =
|
||||
{
|
||||
DEFINE_FIELD( m_iPaintPower, FIELD_INTEGER )
|
||||
};
|
||||
|
||||
datamap_t dataMap = { dataDesc, SIZE_OF_ARRAY(dataDesc), PAINTABLE_ENTITY_USER_DATA_CLASS_NAME, CPaintableEntity<BaseEntityType>::GetBaseMap() };
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
template< typename BaseEntityType >
|
||||
CPaintableEntity< BaseEntityType >::CPaintableEntity()
|
||||
: m_iPaintPower( NO_POWER )
|
||||
{
|
||||
}
|
||||
|
||||
template< typename BaseEntityType >
|
||||
CPaintableEntity< BaseEntityType >::~CPaintableEntity()
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
//PaintDatabase.RemovePaintedEntity( this );
|
||||
#endif // ifndef CLIENT_DLL
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerType CPaintableEntity< BaseEntityType >::GetPaintPowerAtPoint( const Vector& worldContactPt ) const
|
||||
{
|
||||
return m_iPaintPower;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void CPaintableEntity< BaseEntityType >::Paint( PaintPowerType type, const Vector& worldContactPt )
|
||||
{
|
||||
m_iPaintPower = type;
|
||||
}
|
||||
|
||||
|
||||
template< typename BaseEntityType >
|
||||
void CPaintableEntity< BaseEntityType >::CleansePaint()
|
||||
{
|
||||
this->Paint( NO_POWER, vec3_origin );
|
||||
}
|
||||
|
||||
template< typename BaseEntityType >
|
||||
PaintPowerType CPaintableEntity< BaseEntityType >::GetPaintedPower() const
|
||||
{
|
||||
return m_iPaintPower;
|
||||
}
|
||||
|
||||
|
||||
#endif //PAINTABLE_ENTITY_BASE_H
|
||||
@@ -0,0 +1,33 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the base class for prop paint power users that the player can pick up.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PLAYER_PICKUP_PAINT_POWER_USER_H
|
||||
#define PLAYER_PICKUP_PAINT_POWER_USER_H
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
#include "player_pickup.h"
|
||||
#endif
|
||||
|
||||
#include "prop_paint_power_user.h"
|
||||
|
||||
template< typename BasePlayerPickupType >
|
||||
class PlayerPickupPaintPowerUser : public PropPaintPowerUser< BasePlayerPickupType >
|
||||
{
|
||||
DECLARE_CLASS( PlayerPickupPaintPowerUser< BasePlayerPickupType >, PropPaintPowerUser< BasePlayerPickupType > );
|
||||
|
||||
public:
|
||||
//-------------------------------------------------------------------------
|
||||
// IPlayerPickupVphysics Overrides
|
||||
//-------------------------------------------------------------------------
|
||||
virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
|
||||
};
|
||||
|
||||
template< typename BasePlayerPickupType >
|
||||
void PlayerPickupPaintPowerUser<BasePlayerPickupType>::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
|
||||
{
|
||||
BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
|
||||
}
|
||||
|
||||
#endif // ifndef PLAYER_PICKUP_PAINT_POWER_USER_H
|
||||
@@ -0,0 +1,995 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "portal_base2d_shared.h"
|
||||
#include "portal_shareddefs.h"
|
||||
#include "mathlib/polyhedron.h"
|
||||
#include "tier1/callqueue.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
#include "collisionutils.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#include "c_portal_player.h"
|
||||
#else
|
||||
#include "portal_player.h"
|
||||
#include "portal_physics_collisionevent.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_basedoor.h"
|
||||
#include "prediction.h"
|
||||
#else
|
||||
#include "env_debughistory.h"
|
||||
#include "portal/weapon_physcannon.h"
|
||||
#include "physics_bone_follower.h"
|
||||
#include "projectedwallentity.h"
|
||||
#include "portal_physics_collisionevent.h"
|
||||
#endif
|
||||
|
||||
CUtlVector<CPortal_Base2D *> CPortal_Base2D_Shared::AllPortals;
|
||||
|
||||
void MobilePortalsUpdatedCallback( IConVar *var, const char *pOldValue, float flOldValue );
|
||||
ConVar sv_allow_mobile_portals( "sv_allow_mobile_portals", "0", FCVAR_REPLICATED, "", MobilePortalsUpdatedCallback );
|
||||
|
||||
void MobilePortalsUpdatedCallback( IConVar *var, const char *pOldValue, float flOldValue )
|
||||
{
|
||||
if ( sv_allow_mobile_portals.GetFloat() == flOldValue )
|
||||
return;
|
||||
|
||||
static ConVarRef sv_cheats ( "sv_cheats" );
|
||||
bool bCheatsAllowed = (sv_cheats.IsValid() && sv_cheats.GetBool() );
|
||||
|
||||
if ( !bCheatsAllowed )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( V_stricmp( engine->GetLevelNameShort(), "sp_a2_bts5" ) == 0 )
|
||||
#else
|
||||
if ( V_stricmp( gpGlobals->mapname.ToCStr(), "sp_a2_bts5" ) == 0 )
|
||||
#endif
|
||||
{
|
||||
bCheatsAllowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bCheatsAllowed )
|
||||
{
|
||||
var->SetValue( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
ConVar sv_allow_mobile_portal_teleportation( "sv_allow_mobile_portal_teleportation", "1", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
ConVar sv_portal_unified_velocity( "sv_portal_unified_velocity", "1", FCVAR_CHEAT | FCVAR_REPLICATED, "An attempt at removing patchwork velocity tranformation in portals, moving to a unified approach." );
|
||||
ConVar sv_bowie_maneuver_threshold( "sv_bowie_maneuver_threshold", "375.0f", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
ConVar sv_futbol_floor_exit_angle( "sv_futbol_floor_exit_angle", "85", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
extern ConVar sv_portal_debug_touch;
|
||||
|
||||
extern CCallQueue *GetPortalCallQueue();
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
extern CPortal_CollisionEvent g_Collisions;
|
||||
ConVar sv_portal_teleportation_resets_collision_events( "sv_portal_teleportation_resets_collision_events", "1", FCVAR_CHEAT );
|
||||
#endif
|
||||
|
||||
void CPortal_Base2D_Shared::UpdatePortalTransformationMatrix( const matrix3x4_t &localToWorld, const matrix3x4_t &remoteToWorld, VMatrix *pMatrix )
|
||||
{
|
||||
VMatrix matPortal1ToWorldInv, matRotation;
|
||||
|
||||
//inverse of this
|
||||
MatrixInverseTR( VMatrix( localToWorld ), matPortal1ToWorldInv );
|
||||
|
||||
//180 degree rotation about up
|
||||
matRotation.Identity();
|
||||
matRotation.m[0][0] = -1.0f;
|
||||
matRotation.m[1][1] = -1.0f;
|
||||
|
||||
VMatrix vTest = matRotation * matPortal1ToWorldInv;
|
||||
|
||||
//final
|
||||
VMatrix matPortal2ToWorld( remoteToWorld );
|
||||
*pMatrix = matPortal2ToWorld * matRotation * matPortal1ToWorldInv;
|
||||
}
|
||||
|
||||
static char *g_pszPortalNonTeleportable[] =
|
||||
{
|
||||
"func_door",
|
||||
"func_door_rotating",
|
||||
"prop_door_rotating",
|
||||
"func_movelinear",
|
||||
"func_tracktrain",
|
||||
//"env_ghostanimating",
|
||||
"physicsshadowclone",
|
||||
"prop_ragdoll",
|
||||
"physics_prop_ragdoll",
|
||||
"func_brush"
|
||||
};
|
||||
|
||||
bool CPortal_Base2D_Shared::IsEntityTeleportable( CBaseEntity *pEntity )
|
||||
{
|
||||
switch( pEntity->GetMoveType() )
|
||||
{
|
||||
case MOVETYPE_NONE:
|
||||
case MOVETYPE_PUSH:
|
||||
return false;
|
||||
};
|
||||
|
||||
if( pEntity->GetMoveParent() != NULL )
|
||||
return false;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//client
|
||||
|
||||
if( dynamic_cast<C_BaseDoor *>(pEntity) != NULL )
|
||||
return false;
|
||||
|
||||
#else
|
||||
//server
|
||||
if( dynamic_cast<CBoneFollower *>(pEntity) != NULL )
|
||||
return false;
|
||||
|
||||
CPortal_Player *pHoldingPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pEntity );
|
||||
if ( pHoldingPlayer && pHoldingPlayer->IsUsingVMGrab() )
|
||||
{
|
||||
// No need to teleport with viewmodel grab controller
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0; i != ARRAYSIZE(g_pszPortalNonTeleportable); ++i )
|
||||
{
|
||||
if( FClassnameIs( pEntity, g_pszPortalNonTeleportable[i] ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static char *g_pszPortalPhysicsCloneTouching[] =
|
||||
{
|
||||
"func_brush",
|
||||
"projected_wall_entity"
|
||||
};
|
||||
|
||||
bool CPortal_Base2D_Shared::ShouldPhysicsCloneNonTeleportableEntityAcrossPortals( CBaseEntity *pEntity )
|
||||
{
|
||||
for( int i = 0; i != ARRAYSIZE(g_pszPortalPhysicsCloneTouching); ++i )
|
||||
{
|
||||
if( FClassnameIs( pEntity, g_pszPortalPhysicsCloneTouching[i] ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//unify how we determine the velocity of objects when portalling them
|
||||
Vector Portal_FindUsefulVelocity( CBaseEntity *pOther )
|
||||
{
|
||||
Vector vOtherVelocity;
|
||||
IPhysicsObject *pOtherPhysObject = pOther->VPhysicsGetObject();
|
||||
if( sv_portal_unified_velocity.GetBool() )
|
||||
{
|
||||
if( (pOther->GetMoveType() == MOVETYPE_VPHYSICS) && (pOtherPhysObject != NULL) )
|
||||
{
|
||||
pOtherPhysObject->GetVelocity( &vOtherVelocity, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
vOtherVelocity = pOther->GetAbsVelocity();
|
||||
if( pOtherPhysObject )
|
||||
{
|
||||
Vector vPhysVelocity;
|
||||
pOtherPhysObject->GetVelocity( &vPhysVelocity, NULL );
|
||||
|
||||
if( vPhysVelocity.LengthSqr() > vOtherVelocity.LengthSqr() )
|
||||
{
|
||||
vOtherVelocity = vPhysVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pOther->GetMoveType() == MOVETYPE_VPHYSICS )
|
||||
{
|
||||
if( pOtherPhysObject && (pOtherPhysObject->GetShadowController() == NULL) )
|
||||
{
|
||||
pOtherPhysObject->GetVelocity( &vOtherVelocity, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
pOther->GetVelocity( &vOtherVelocity );
|
||||
#else
|
||||
vOtherVelocity = pOther->GetAbsVelocity();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if ( pOther->IsPlayer() && pOther->VPhysicsGetObject() )
|
||||
{
|
||||
pOther->VPhysicsGetObject()->GetVelocity( &vOtherVelocity, NULL );
|
||||
|
||||
if ( vOtherVelocity == vec3_origin )
|
||||
{
|
||||
vOtherVelocity = pOther->GetAbsVelocity();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
pOther->GetVelocity( &vOtherVelocity );
|
||||
#else
|
||||
vOtherVelocity = pOther->GetAbsVelocity();
|
||||
#endif
|
||||
}
|
||||
|
||||
if( vOtherVelocity == vec3_origin )
|
||||
{
|
||||
// Recorded velocity is sometimes zero under pushed or teleported movement, or after position correction.
|
||||
// In these circumstances, we want implicit velocity ((last pos - this pos) / timestep )
|
||||
if ( pOtherPhysObject )
|
||||
{
|
||||
Vector vOtherImplicitVelocity;
|
||||
pOtherPhysObject->GetImplicitVelocity( &vOtherImplicitVelocity, NULL );
|
||||
vOtherVelocity += vOtherImplicitVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vOtherVelocity;
|
||||
}
|
||||
|
||||
|
||||
bool CPortal_Base2D::ShouldTeleportTouchingEntity( CBaseEntity *pOther )
|
||||
{
|
||||
if( m_hLinkedPortal.Get() == NULL )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
#if !defined ( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "Portal %i not teleporting %s because it has no linked partner portal.\n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName() ) );
|
||||
}
|
||||
#endif
|
||||
if ( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
Msg( "Portal %i not teleporting %s because it has no linked partner portal.\n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName() );
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bMobilePortals = IsMobile() || m_hLinkedPortal->IsMobile();
|
||||
if( bMobilePortals && !sv_allow_mobile_portal_teleportation.GetBool() )
|
||||
return false;
|
||||
|
||||
//can't teleport an entity we don't own, unless this is a mobile portal interaction
|
||||
if( !m_PortalSimulator.OwnsEntity(pOther) && !bMobilePortals )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
#if !defined ( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "Portal %i not teleporting %s because it's not simulated by this portal.\n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName() ) );
|
||||
}
|
||||
#endif
|
||||
if ( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
Msg( "Portal %i not teleporting %s because it's not simulated by this portal. : %f \n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName(), gpGlobals->curtime );
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( !CPortal_Base2D_Shared::IsEntityTeleportable( pOther ) )
|
||||
return false;
|
||||
|
||||
//Vector ptOtherOrigin = pOther->GetAbsOrigin();
|
||||
Vector ptOtherCenter = pOther->WorldSpaceCenter();
|
||||
Vector vOtherVelocity = Portal_FindUsefulVelocity( pOther );
|
||||
vOtherVelocity -= GetAbsVelocity(); //subtract the portal's velocity if it's moving. It's all relative.
|
||||
|
||||
if( vOtherVelocity.Dot( m_PortalSimulator.GetInternalData().Placement.vForward ) > 0.0f )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( bMobilePortals )
|
||||
{
|
||||
return pOther->IsPlayer() && (vOtherVelocity.Dot( m_PortalSimulator.GetInternalData().Placement.vForward ) < -5.0f); //only allow players for now in mobile portal teleportations
|
||||
}
|
||||
|
||||
// Test for entity's center being past portal plane
|
||||
if(m_PortalSimulator.GetInternalData().Placement.PortalPlane.m_Normal.Dot( ptOtherCenter ) < m_PortalSimulator.GetInternalData().Placement.PortalPlane.m_Dist)
|
||||
{
|
||||
//entity wants to go further into the plane
|
||||
if( m_PortalSimulator.EntityIsInPortalHole( pOther ) )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
static int iAntiRecurse = 0;
|
||||
if( pOther->IsPlayer() && (iAntiRecurse == 0) )
|
||||
{
|
||||
++iAntiRecurse;
|
||||
ShouldTeleportTouchingEntity( pOther ); //do it again for debugging
|
||||
--iAntiRecurse;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#if defined( GAME_DLL )
|
||||
else
|
||||
{
|
||||
#if !defined ( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "Portal %i not teleporting %s because it was not in the portal hole.\n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName() ) );
|
||||
}
|
||||
#endif
|
||||
if ( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
Msg( "Portal %i not teleporting %s because it was not in the portal hole.\n", ((m_bIsPortal2)?(2):(1)), pOther->GetDebugName() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPortal_Base2D::TeleportTouchingEntity( CBaseEntity *pOther )
|
||||
{
|
||||
if ( GetPortalCallQueue() )
|
||||
{
|
||||
GetPortalCallQueue()->QueueCall( this, &CPortal_Base2D::TeleportTouchingEntity, pOther );
|
||||
return;
|
||||
}
|
||||
|
||||
PreTeleportTouchingEntity( pOther );
|
||||
|
||||
bool bMobilePortals = IsMobile() || m_hLinkedPortal->IsMobile();
|
||||
|
||||
Assert( m_hLinkedPortal.Get() != NULL );
|
||||
|
||||
Vector ptOtherOrigin = pOther->GetAbsOrigin();
|
||||
Vector ptOtherCenter;
|
||||
|
||||
bool bPlayer = pOther->IsPlayer();
|
||||
QAngle qPlayerEyeAngles;
|
||||
CPortal_Player *pOtherAsPlayer;
|
||||
|
||||
|
||||
if( bPlayer )
|
||||
{
|
||||
//NDebugOverlay::EntityBounds( pOther, 255, 0, 0, 128, 60.0f );
|
||||
pOtherAsPlayer = (CPortal_Player *)pOther;
|
||||
qPlayerEyeAngles = pOtherAsPlayer->pl.v_angle;
|
||||
Warning( "PORTALLING PLAYER SHOULD BE DONE IN GAMEMOVEMENT\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
pOtherAsPlayer = NULL;
|
||||
}
|
||||
|
||||
ptOtherCenter = pOther->WorldSpaceCenter();
|
||||
|
||||
bool bNonPhysical = false; //special case handling for non-physical objects such as the energy ball and player
|
||||
|
||||
|
||||
|
||||
QAngle qOtherAngles;
|
||||
Vector vOtherVelocity;
|
||||
|
||||
vOtherVelocity = Portal_FindUsefulVelocity( pOther );
|
||||
vOtherVelocity -= GetAbsVelocity(); //subtract the portal's velocity if it's moving. It's all relative.
|
||||
|
||||
const PS_InternalData_t &RemotePortalDataAccess = m_hLinkedPortal->m_PortalSimulator.GetInternalData();
|
||||
const PS_InternalData_t &LocalPortalDataAccess = m_PortalSimulator.GetInternalData();
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
bool bCrouchPlayer = false;
|
||||
#endif
|
||||
if( bPlayer )
|
||||
{
|
||||
qOtherAngles = pOtherAsPlayer->EyeAngles();
|
||||
bNonPhysical = true;
|
||||
Vector vWorldUp( 0.0f, 0.0f, 1.0f );
|
||||
vWorldUp = m_matrixThisToLinked.ApplyRotation( vWorldUp );
|
||||
|
||||
if( fabs( vWorldUp.z ) < 0.7071f ) //the transformation will change our notion of UP significantly
|
||||
{
|
||||
//we have to compensate for the fact that AABB's don't rotate ever
|
||||
pOtherAsPlayer->SetGroundEntity( NULL );
|
||||
|
||||
//curl the player up into a little ball
|
||||
#if defined( GAME_DLL )
|
||||
if( !pOtherAsPlayer->IsDucked() )
|
||||
{
|
||||
bCrouchPlayer = true;
|
||||
ptOtherOrigin.z += 18.0f;
|
||||
//pOtherAsPlayer->ForceDuckThisFrame(); NOTE: This should never be used! Players teleport in game movement!!!
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qOtherAngles = pOther->GetAbsAngles();
|
||||
bNonPhysical = FClassnameIs( pOther, "prop_energy_ball" );
|
||||
}
|
||||
|
||||
if( bMobilePortals ) //hack the position a bit so it pops out as far in front of the remote portal as it is in front of the local portal
|
||||
{
|
||||
float fPlaneDist = LocalPortalDataAccess.Placement.PortalPlane.DistTo( ptOtherCenter );
|
||||
Vector vOffset = LocalPortalDataAccess.Placement.vForward * (-(fPlaneDist * 2.0f)); //equidistant from the portal plane, but on the opposite side
|
||||
ptOtherCenter += vOffset;
|
||||
ptOtherOrigin += vOffset;
|
||||
//NDebugOverlay::Box( ptOtherCenter, Vector( -10.f, -10.f, -10.0f ), Vector( 10.0f, 10.0f, 10.0f ), 0, 255, 0, 128, 10.0f );
|
||||
}
|
||||
|
||||
|
||||
Vector ptNewOrigin;
|
||||
QAngle qNewAngles;
|
||||
Vector vNewVelocity;
|
||||
//apply transforms to relevant variables (applied to the entity later)
|
||||
{
|
||||
if( bPlayer )
|
||||
{
|
||||
ptNewOrigin = m_matrixThisToLinked * ptOtherCenter;
|
||||
ptNewOrigin += ( ptOtherOrigin - ptOtherCenter );
|
||||
|
||||
// TODO: For accuracy sake we should handle this for all portal orientations
|
||||
// It's only abusable for floor to floor though.
|
||||
if ( IsFloorPortal( 0.9f ) && m_hLinkedPortal->IsFloorPortal( 0.9f ) )
|
||||
{
|
||||
// When we teleported we were assuming that our center was exactly on the same plane as the portal
|
||||
// We need to see how far off we were from that, Then we need to *double* compensate to push us
|
||||
// exactly as far off in our new position as we were before.
|
||||
ptNewOrigin.z -= 2.0f*( GetAbsOrigin() - ptOtherCenter ).z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ptNewOrigin = m_matrixThisToLinked * ptOtherOrigin;
|
||||
}
|
||||
|
||||
// Reorient object angles, originally we did a transformation on the angles, but that doesn't quite work right for gimbal lock cases
|
||||
qNewAngles = TransformAnglesToWorldSpace( qOtherAngles, m_matrixThisToLinked.As3x4() );
|
||||
|
||||
qNewAngles.x = AngleNormalizePositive( qNewAngles.x );
|
||||
qNewAngles.y = AngleNormalizePositive( qNewAngles.y );
|
||||
qNewAngles.z = AngleNormalizePositive( qNewAngles.z );
|
||||
|
||||
QAngle savedAngles = qNewAngles;
|
||||
|
||||
// Our teleport is going to roll us. See if we could find an over extended angle that would prevent a roll since that's the most disorienting thing.
|
||||
if( bPlayer && qNewAngles[ROLL] != 0.0f )
|
||||
{
|
||||
float bestAngle = fabs( 180.f - qNewAngles.z );
|
||||
float punchMag = 0.0f;
|
||||
QAngle punchAngles;
|
||||
|
||||
// try to adjust the initial angle to see if we can get something better
|
||||
for ( int i = 0; i < 20; i++ )
|
||||
{
|
||||
QAngle qTestAngles = qOtherAngles;
|
||||
float pitchAdjust = 5.f*(1 + i/2);
|
||||
if( i%2 == 1 )
|
||||
{
|
||||
pitchAdjust *= -1.f;
|
||||
}
|
||||
qTestAngles[PITCH] += pitchAdjust;
|
||||
|
||||
qTestAngles = TransformAnglesToWorldSpace( qTestAngles, m_matrixThisToLinked.As3x4() );
|
||||
|
||||
qTestAngles.x = AngleNormalizePositive( qTestAngles.x );
|
||||
qTestAngles.y = AngleNormalizePositive( qTestAngles.y );
|
||||
qTestAngles.z = AngleNormalizePositive( qTestAngles.z );
|
||||
|
||||
|
||||
if( fabs( 180.f - qTestAngles.z ) > (bestAngle + 1.1f*fabs(pitchAdjust)) )
|
||||
{
|
||||
bestAngle = fabs( 180.f - qTestAngles.z );
|
||||
qNewAngles = qTestAngles;
|
||||
punchMag = pitchAdjust;
|
||||
}
|
||||
}
|
||||
|
||||
punchAngles = savedAngles - qNewAngles;
|
||||
|
||||
if( punchAngles.y < 180.f )
|
||||
punchAngles.y += 360.f;
|
||||
if( punchAngles.y > 180.f )
|
||||
punchAngles.y -= 360.f;
|
||||
|
||||
if( fabs( punchAngles.y ) > 120.f )
|
||||
{
|
||||
punchAngles.z = 0.f;
|
||||
punchAngles.y = 0.f;
|
||||
punchAngles.x = -punchMag;
|
||||
}
|
||||
|
||||
pOtherAsPlayer->SetPunchAngle( punchAngles );
|
||||
}
|
||||
|
||||
// Reorient the velocity
|
||||
vNewVelocity = m_matrixThisToLinked.ApplyRotation( vOtherVelocity );
|
||||
}
|
||||
|
||||
//velocity hacks
|
||||
{
|
||||
float fExitMin, fExitMax;
|
||||
CPortal_Base2D::GetExitSpeedRange( this, bPlayer, fExitMin, fExitMax, ptNewOrigin, pOther );
|
||||
|
||||
float fSpeed = vNewVelocity.Length();
|
||||
if( fSpeed == 0.0f )
|
||||
{
|
||||
if( fExitMin >= 0.0f )
|
||||
{
|
||||
vNewVelocity = m_hLinkedPortal->m_vForward * fExitMin;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fSpeed < fExitMin )
|
||||
{
|
||||
vNewVelocity *= (fExitMin / fSpeed);
|
||||
}
|
||||
else if( fSpeed > fExitMax )
|
||||
{
|
||||
vNewVelocity *= (fExitMax / fSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
if( bPlayer )
|
||||
{
|
||||
bool bPitchReorientation = false;
|
||||
if ( IsFloorPortal( 0.9f ) && m_hLinkedPortal->IsFloorPortal( 0.9f ) ) //floor to floor transition
|
||||
{
|
||||
Vector vPlayerEyeForward;
|
||||
pOtherAsPlayer->EyeVectors( &vPlayerEyeForward );
|
||||
if( vPlayerEyeForward.z > fabs( 0.85f ) ) //player looking mostly down into entrance portal
|
||||
{
|
||||
//do a pitch reorientation instead of a corkscrew roll
|
||||
bPitchReorientation = true;
|
||||
}
|
||||
|
||||
const float cfBowieManueverThreshold = sv_bowie_maneuver_threshold.GetFloat();
|
||||
if( vOtherVelocity.LengthSqr() < (cfBowieManueverThreshold * cfBowieManueverThreshold) ) //velocity below the threshold where we'd like to do the David Bowie move from Labyrinth where he was walking around the crazy Escher style room
|
||||
{
|
||||
//compute what direction we'll be facing coming out of the portal, but projected onto the exit portal plane and normalized
|
||||
Vector vPlayerForward;
|
||||
pOtherAsPlayer->GetVectors( &vPlayerForward, NULL, NULL );
|
||||
Vector vBowieForward = LocalPortalDataAccess.Placement.matThisToLinked.ApplyRotation( vPlayerForward );
|
||||
vBowieForward = vBowieForward - (RemotePortalDataAccess.Placement.vForward * vBowieForward.Dot( RemotePortalDataAccess.Placement.vForward )); //project onto the exit portal plane
|
||||
vBowieForward.NormalizeInPlace();
|
||||
|
||||
// negate our forward since we are always going to be facing the wrong way when we come out.
|
||||
vBowieForward = -vBowieForward;
|
||||
|
||||
//figure out which axis our forward is most aligned with, then base magnitude on how far we'd have to go to reach the portal border on that side
|
||||
float fGoToSide = fabs( vBowieForward.Dot( RemotePortalDataAccess.Placement.vRight ) );
|
||||
float fGoToBottom = fabs( vBowieForward.Dot( -RemotePortalDataAccess.Placement.vUp ) );
|
||||
float fPushMagnitude;
|
||||
if( fGoToSide > fGoToBottom )
|
||||
{
|
||||
fPushMagnitude = RemotePortalDataAccess.Placement.fHalfWidth / fGoToSide;
|
||||
}
|
||||
else
|
||||
{
|
||||
fPushMagnitude = RemotePortalDataAccess.Placement.fHalfHeight / fGoToBottom;
|
||||
}
|
||||
|
||||
//calculate existing velocity perpendicular to the exit plane
|
||||
Vector vExistingPlanarVelocity = vNewVelocity - RemotePortalDataAccess.Placement.vForward * vNewVelocity.Dot( RemotePortalDataAccess.Placement.vForward );
|
||||
|
||||
// if the Bowie maneuver velocity would be greater than our existing coplanar velocity, cancel out the existing coplanar velocity and add in the Bowie maneuver velocity
|
||||
// if( 2.0f*(fPushMagnitude * fPushMagnitude) > vExistingPlanarVelocity.LengthSqr() ) // we really prefer to bowie, force this for now.
|
||||
{
|
||||
// We don't want to keep spinning - give us an extra 20% to make sure we get out.
|
||||
Vector vecAdded = 1.2f*((vBowieForward * fPushMagnitude) - vExistingPlanarVelocity);
|
||||
vNewVelocity += vecAdded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pOtherAsPlayer->m_bPitchReorientation = bPitchReorientation;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
//both changing portal environment and teleportation can trigger penetration solving. Disable solving while we're performing the migration and teleportations as we'll be in a bad state until both complete.
|
||||
CPortal_CollisionEvent::DisablePenetrationSolving_Push( true );
|
||||
#endif
|
||||
|
||||
//untouch the portal(s), will force a touch on destination after the teleport
|
||||
if( !bMobilePortals )
|
||||
{
|
||||
m_PortalSimulator.ReleaseOwnershipOfEntity( pOther, true );
|
||||
|
||||
m_hLinkedPortal->m_PortalSimulator.TakeOwnershipOfEntity( pOther );
|
||||
|
||||
//m_hLinkedPortal->PhysicsNotifyOtherOfUntouch( m_hLinkedPortal, pOther );
|
||||
//pOther->PhysicsNotifyOtherOfUntouch( pOther, m_hLinkedPortal );
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
DevMsg( "===PORTAL %i TELEPORTING: %s : %f %f %f : %f===\n", ((m_bIsPortal2)?(2):(1)), pOther->GetClassname(), vOtherVelocity.x, vOtherVelocity.y, vOtherVelocity.z, gpGlobals->curtime );
|
||||
}
|
||||
#if !defined ( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "PORTAL %i TELEPORTING: %s\n", ((m_bIsPortal2)?(2):(1)), pOther->GetClassname() ) );
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//do the actual teleportation
|
||||
{
|
||||
pOther->SetGroundEntity( NULL );
|
||||
|
||||
if( bPlayer )
|
||||
{
|
||||
QAngle qTransformedEyeAngles = TransformAnglesToWorldSpace( qPlayerEyeAngles, m_matrixThisToLinked.As3x4() );
|
||||
qTransformedEyeAngles.x = AngleNormalizePositive( qTransformedEyeAngles.x );
|
||||
qTransformedEyeAngles.y = AngleNormalizePositive( qTransformedEyeAngles.y );
|
||||
qTransformedEyeAngles.z = AngleNormalizePositive( qTransformedEyeAngles.z );
|
||||
|
||||
pOtherAsPlayer->pl.v_angle = qTransformedEyeAngles;
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
pOtherAsPlayer->pl.fixangle = FIXANGLE_ABSOLUTE;
|
||||
pOtherAsPlayer->UpdateVPhysicsPosition( ptNewOrigin, vNewVelocity, 0.0f );
|
||||
pOtherAsPlayer->Teleport( &ptNewOrigin, &qNewAngles, &vNewVelocity );
|
||||
#else
|
||||
pOtherAsPlayer->SetAbsOrigin( ptNewOrigin );
|
||||
pOtherAsPlayer->SetAbsAngles( qNewAngles );
|
||||
pOtherAsPlayer->SetAbsVelocity( vNewVelocity );
|
||||
#endif
|
||||
|
||||
|
||||
//pOtherAsPlayer->UnDuck();
|
||||
|
||||
//pOtherAsPlayer->m_angEyeAngles = qTransformedEyeAngles;
|
||||
//pOtherAsPlayer->pl.v_angle = qTransformedEyeAngles;
|
||||
//pOtherAsPlayer->pl.fixangle = FIXANGLE_ABSOLUTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( bNonPhysical )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
pOther->Teleport( &ptNewOrigin, &qNewAngles, &vNewVelocity );
|
||||
#else
|
||||
pOther->SetAbsOrigin( ptNewOrigin );
|
||||
pOther->SetAbsAngles( qNewAngles );
|
||||
pOther->SetAbsVelocity( vNewVelocity );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//doing velocity in two stages as a bug workaround, setting the velocity to anything other than 0 will screw up how objects rest on this entity in the future
|
||||
#if defined( GAME_DLL )
|
||||
pOther->Teleport( &ptNewOrigin, &qNewAngles, &vec3_origin );
|
||||
#else
|
||||
pOther->SetAbsOrigin( ptNewOrigin );
|
||||
pOther->SetAbsAngles( qNewAngles );
|
||||
pOther->SetAbsVelocity( vec3_origin );
|
||||
#endif
|
||||
// Hacks for the Wheatley battle. We need the futbols to be portalled in an intuitive way.
|
||||
if( FClassnameIs( pOther, "prop_exploding_futbol" ) )
|
||||
{
|
||||
// Exiting a floor portal: Always come out headed towards the top of the exit portal
|
||||
if( m_hLinkedPortal->m_vForward.z == 1.f )
|
||||
{
|
||||
vNewVelocity = m_hLinkedPortal->m_vUp * vNewVelocity.Length();
|
||||
|
||||
// Build our pitch matrix
|
||||
Vector vRotAxis = m_hLinkedPortal->m_vRight;
|
||||
VMatrix mtxRotation;
|
||||
float flRotationAmt = -sv_futbol_floor_exit_angle.GetFloat();
|
||||
MatrixBuildRotationAboutAxis( mtxRotation, vRotAxis, flRotationAmt );
|
||||
|
||||
vNewVelocity = mtxRotation * vNewVelocity;
|
||||
}
|
||||
// Exiting a wall portal: Shoot straight out
|
||||
else
|
||||
{
|
||||
// Magic number so the bombs will fly the same speed everytime
|
||||
vNewVelocity = m_hLinkedPortal->m_vForward * 850.f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pOther->ApplyAbsVelocityImpulse( vNewVelocity );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pOther->RemoveEffects( EF_NOINTERP );
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
CPortal_CollisionEvent::DisablePenetrationSolving_Pop(); //re-enable penetration solving now that we're in the target environment and at the target position
|
||||
if( sv_portal_teleportation_resets_collision_events.GetBool() )
|
||||
{
|
||||
g_Collisions.RemovePenetrationEvents( pOther );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( bMobilePortals )
|
||||
{
|
||||
FindClosestPassableSpace( pOther, LocalPortalDataAccess.Placement.vForward );
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
|
||||
if( (pPhys != NULL) && (pPhys->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
|
||||
{
|
||||
CPortal_Player *pHoldingPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pOther );
|
||||
pHoldingPlayer->ToggleHeldObjectOnOppositeSideOfPortal();
|
||||
if ( pHoldingPlayer->IsHeldObjectOnOppositeSideOfPortal() )
|
||||
{
|
||||
pHoldingPlayer->SetHeldObjectPortal( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
pHoldingPlayer->SetHeldObjectPortal( NULL );
|
||||
}
|
||||
|
||||
CGrabController *pController = GetGrabControllerForPlayer( pHoldingPlayer );
|
||||
if( pController )
|
||||
{
|
||||
pController->CheckPortalOscillation( this, pOther, pHoldingPlayer );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < IProjectedWallEntityAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CProjectedWallEntity *pWall = static_cast< CProjectedWallEntity* >( IProjectedWallEntityAutoList::AutoList()[i] );
|
||||
pWall->DisplaceObstructingEntity( pOther, true );
|
||||
}
|
||||
|
||||
// For alternate ticks, if we're going to simulate physics again make sure the grab controller's target position is up to date with the
|
||||
// teleported object position.
|
||||
if ( PhysIsFinalTick() == false )
|
||||
{
|
||||
UpdateGrabControllerTargetPosition( pHoldingPlayer, NULL, NULL, true );
|
||||
}
|
||||
}
|
||||
else if( bPlayer )
|
||||
{
|
||||
CBaseEntity *pHeldEntity = GetPlayerHeldEntity( pOtherAsPlayer );
|
||||
if( pHeldEntity )
|
||||
{
|
||||
pOtherAsPlayer->ToggleHeldObjectOnOppositeSideOfPortal();
|
||||
if( pOtherAsPlayer->IsHeldObjectOnOppositeSideOfPortal() )
|
||||
{
|
||||
pOtherAsPlayer->SetHeldObjectPortal( m_hLinkedPortal );
|
||||
}
|
||||
else
|
||||
{
|
||||
pOtherAsPlayer->SetHeldObjectPortal( NULL );
|
||||
|
||||
//we need to make sure the held object and player don't interpenetrate when the player's shape changes
|
||||
Vector vTargetPosition;
|
||||
QAngle qTargetOrientation;
|
||||
UpdateGrabControllerTargetPosition( pOtherAsPlayer, &vTargetPosition, &qTargetOrientation );
|
||||
|
||||
pHeldEntity->Teleport( &vTargetPosition, &qTargetOrientation, 0 );
|
||||
|
||||
FindClosestPassableSpace( pHeldEntity, RemotePortalDataAccess.Placement.vForward );
|
||||
}
|
||||
}
|
||||
|
||||
//we haven't found a good way of fixing the problem of "how do you reorient an AABB". So we just move the player so that they fit
|
||||
m_hLinkedPortal->ForceEntityToFitInPortalWall( pOtherAsPlayer );
|
||||
}
|
||||
#endif
|
||||
|
||||
//force the entity to be touching the other portal right this millisecond
|
||||
if( !bMobilePortals )
|
||||
{
|
||||
trace_t Trace;
|
||||
memset( &Trace, 0, sizeof(trace_t) );
|
||||
//UTIL_TraceEntity( pOther, ptNewOrigin, ptNewOrigin, MASK_SOLID, pOther, COLLISION_GROUP_NONE, &Trace ); //fires off some asserts, and we just need a dummy anyways
|
||||
|
||||
pOther->PhysicsMarkEntitiesAsTouching( m_hLinkedPortal.Get(), Trace );
|
||||
m_hLinkedPortal.Get()->PhysicsMarkEntitiesAsTouching( pOther, Trace );
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
// Notify the entity that it's being teleported
|
||||
// Tell the teleported entity of the portal it has just arrived at
|
||||
notify_teleport_params_t paramsTeleport;
|
||||
paramsTeleport.prevOrigin = ptOtherOrigin;
|
||||
paramsTeleport.prevAngles = qOtherAngles;
|
||||
paramsTeleport.physicsRotate = true;
|
||||
notify_system_event_params_t eventParams ( ¶msTeleport );
|
||||
pOther->NotifySystemEvent( this, NOTIFY_EVENT_TELEPORT, eventParams );
|
||||
|
||||
// Notify the portals to fire appropriate outputs
|
||||
OnEntityTeleportedFromPortal( pOther );
|
||||
if ( m_hLinkedPortal )
|
||||
{
|
||||
m_hLinkedPortal->OnEntityTeleportedToPortal( pOther );
|
||||
}
|
||||
|
||||
//notify clients of the teleportation
|
||||
EntityPortalled( this, pOther, ptNewOrigin, qNewAngles, false );
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
Vector ptTestCenter = pOther->WorldSpaceCenter();
|
||||
|
||||
float fNewDist, fOldDist;
|
||||
fNewDist = RemotePortalDataAccess.Placement.PortalPlane.m_Normal.Dot( ptTestCenter ) - RemotePortalDataAccess.Placement.PortalPlane.m_Dist;
|
||||
fOldDist = LocalPortalDataAccess.Placement.PortalPlane.m_Normal.Dot( ptOtherCenter ) - LocalPortalDataAccess.Placement.PortalPlane.m_Dist;
|
||||
AssertMsg( fNewDist >= 0.0f, "Entity portalled behind the destination portal." );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
pOther->NetworkProp()->NetworkStateForceUpdate();
|
||||
if( bPlayer )
|
||||
pOtherAsPlayer->pl.NetworkStateChanged();
|
||||
#endif
|
||||
|
||||
//if( bPlayer )
|
||||
// NDebugOverlay::EntityBounds( pOther, 0, 255, 0, 128, 60.0f );
|
||||
|
||||
Assert( (bPlayer == false) || (pOtherAsPlayer->m_hPortalEnvironment.Get() == m_hLinkedPortal.Get()) );
|
||||
|
||||
PostTeleportTouchingEntity( pOther );
|
||||
|
||||
#if defined( CLIENT_DLL ) && 0 //debugging code
|
||||
{
|
||||
Vector vPos = pOther->GetAbsOrigin();
|
||||
Warning( "CPortal_Base2D::TeleportTouchingEntity(%s), portal %d, time %f %f, End Position: %f %f %f\n", pOther->GetClassname(), m_bIsPortal2 ? 2 : 1, gpGlobals->curtime, prediction->GetSavedTime(), vPos.x, vPos.y, vPos.z );
|
||||
NDebugOverlay::EntityBounds( pOther, 0, 0, 255, 100, 5.0f );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the portal active
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPortal_Base2D::SetActive( bool bActive )
|
||||
{
|
||||
m_bOldActivatedState = m_bActivated;
|
||||
m_bActivated = bActive;
|
||||
}
|
||||
|
||||
bool CPortal_Base2D::IsActivedAndLinked( void ) const
|
||||
{
|
||||
return ( IsActive() && (m_hLinkedPortal.Get() != NULL) && (m_hLinkedPortal.Get()->IsActive()) );
|
||||
}
|
||||
|
||||
bool CPortal_Base2D::IsFloorPortal( float fThreshold ) const
|
||||
{
|
||||
return m_PortalSimulator.GetInternalData().Placement.vForward.z > fThreshold;
|
||||
}
|
||||
|
||||
bool CPortal_Base2D::IsCeilingPortal( float fThreshold ) const
|
||||
{
|
||||
return m_PortalSimulator.GetInternalData().Placement.vForward.z < fThreshold;
|
||||
}
|
||||
|
||||
void CPortal_Base2D::PortalSimulator_TookOwnershipOfEntity( CBaseEntity *pEntity )
|
||||
{
|
||||
if( pEntity->IsPlayer() )
|
||||
{
|
||||
//DevMsg( "Portal %i simulator took ownership of player: %f\n", ((m_bIsPortal2)?(2):(1)), gpGlobals->curtime );
|
||||
((CPortal_Player *)pEntity)->m_hPortalEnvironment = this;
|
||||
}
|
||||
}
|
||||
|
||||
void CPortal_Base2D::PortalSimulator_ReleasedOwnershipOfEntity( CBaseEntity *pEntity )
|
||||
{
|
||||
if( pEntity->IsPlayer() && (((CPortal_Player *)pEntity)->m_hPortalEnvironment.Get() == this) )
|
||||
{
|
||||
//DevMsg( "Portal %i simulator released ownership of player: %f\n", ((m_bIsPortal2)?(2):(1)), gpGlobals->curtime );
|
||||
((CPortal_Player *)pEntity)->m_hPortalEnvironment = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CPortal_Base2D::UpdateCollisionShape( void )
|
||||
{
|
||||
if( m_pCollisionShape )
|
||||
{
|
||||
physcollision->DestroyCollide( m_pCollisionShape );
|
||||
m_pCollisionShape = NULL;
|
||||
}
|
||||
|
||||
Vector vLocalMins = GetLocalMins();
|
||||
Vector vLocalMaxs = GetLocalMaxs();
|
||||
|
||||
if( (vLocalMaxs.x <= vLocalMins.x) || (vLocalMaxs.y <= vLocalMins.y) || (vLocalMaxs.z <= vLocalMins.z) )
|
||||
return; //volume is 0 (or less)
|
||||
|
||||
|
||||
//create the collision shape.... TODO: consider having one shared collideable between all portals
|
||||
float fPlanes[6*4];
|
||||
fPlanes[(0*4) + 0] = 1.0f;
|
||||
fPlanes[(0*4) + 1] = 0.0f;
|
||||
fPlanes[(0*4) + 2] = 0.0f;
|
||||
fPlanes[(0*4) + 3] = vLocalMaxs.x;
|
||||
|
||||
fPlanes[(1*4) + 0] = -1.0f;
|
||||
fPlanes[(1*4) + 1] = 0.0f;
|
||||
fPlanes[(1*4) + 2] = 0.0f;
|
||||
fPlanes[(1*4) + 3] = -vLocalMins.x;
|
||||
|
||||
fPlanes[(2*4) + 0] = 0.0f;
|
||||
fPlanes[(2*4) + 1] = 1.0f;
|
||||
fPlanes[(2*4) + 2] = 0.0f;
|
||||
fPlanes[(2*4) + 3] = vLocalMaxs.y;
|
||||
|
||||
fPlanes[(3*4) + 0] = 0.0f;
|
||||
fPlanes[(3*4) + 1] = -1.0f;
|
||||
fPlanes[(3*4) + 2] = 0.0f;
|
||||
fPlanes[(3*4) + 3] = -vLocalMins.y;
|
||||
|
||||
fPlanes[(4*4) + 0] = 0.0f;
|
||||
fPlanes[(4*4) + 1] = 0.0f;
|
||||
fPlanes[(4*4) + 2] = 1.0f;
|
||||
fPlanes[(4*4) + 3] = vLocalMaxs.z;
|
||||
|
||||
fPlanes[(5*4) + 0] = 0.0f;
|
||||
fPlanes[(5*4) + 1] = 0.0f;
|
||||
fPlanes[(5*4) + 2] = -1.0f;
|
||||
fPlanes[(5*4) + 3] = -vLocalMins.z;
|
||||
|
||||
CPolyhedron *pPolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, 0.00001f, true );
|
||||
Assert( pPolyhedron != NULL );
|
||||
CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pPolyhedron );
|
||||
pPolyhedron->Release();
|
||||
Assert( pConvex != NULL );
|
||||
m_pCollisionShape = physcollision->ConvertConvexToCollide( &pConvex, 1 );
|
||||
}
|
||||
|
||||
|
||||
float CPortal_Base2D::GetMinimumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity )
|
||||
{
|
||||
return -FLT_MAX; //default behavior is to not mess with the speed
|
||||
}
|
||||
|
||||
float CPortal_Base2D::GetMaximumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity )
|
||||
{
|
||||
return FLT_MAX; //default behavior is to not mess with the speed
|
||||
}
|
||||
|
||||
|
||||
void CPortal_Base2D::GetExitSpeedRange( CPortal_Base2D *pEntrancePortal, bool bPlayer, float &fExitMinimum, float &fExitMaximum, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity )
|
||||
{
|
||||
CPortal_Base2D *pExitPortal = pEntrancePortal ? pEntrancePortal->m_hLinkedPortal.Get() : NULL;
|
||||
if( !pExitPortal )
|
||||
{
|
||||
fExitMinimum = -FLT_MAX;
|
||||
fExitMaximum = FLT_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
const float COS_PI_OVER_SIX = 0.86602540378443864676372317075294f; // cos( 30 degrees ) in radians
|
||||
bool bEntranceOnFloor = pEntrancePortal->m_plane_Origin.normal.z > COS_PI_OVER_SIX;
|
||||
bool bExitOnFloor = pExitPortal->m_plane_Origin.normal.z > COS_PI_OVER_SIX;
|
||||
|
||||
fExitMinimum = pExitPortal->GetMinimumExitSpeed( bPlayer, bEntranceOnFloor, bExitOnFloor, vEntityCenterAtExit, pEntity );
|
||||
fExitMaximum = pExitPortal->GetMaximumExitSpeed( bPlayer, bEntranceOnFloor, bExitOnFloor, vEntityCenterAtExit, pEntity );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTAL_BASE2D_SHARED_H
|
||||
#define PORTAL_BASE2D_SHARED_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
class C_Portal_Base2D;
|
||||
#else
|
||||
class CPortal_Base2D;
|
||||
#endif
|
||||
|
||||
class CPhysCollide;
|
||||
|
||||
// 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 CPortal_Base2D_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 bool ShouldPhysicsCloneNonTeleportableEntityAcrossPortals( CBaseEntity *pEntity );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
static CUtlVector<C_Portal_Base2D *> AllPortals; //an array of existing portal entities
|
||||
#else
|
||||
static CUtlVector<CPortal_Base2D *> AllPortals; //an array of existing portal entities
|
||||
#endif //#ifdef CLIENT_DLL
|
||||
|
||||
protected:
|
||||
|
||||
bool ShouldTeleportTouchingEntity( CBaseEntity *pOther ); //assuming the entity is or was just touching the portal, check for teleportation conditions
|
||||
void TeleportTouchingEntity( CBaseEntity *pOther );
|
||||
|
||||
|
||||
void UpdateCollisionShape( void );
|
||||
CPhysCollide *m_pCollisionShape;
|
||||
};
|
||||
|
||||
//make it so shared code can just load the shared header and get the right version
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_portal_base2d.h"
|
||||
#else
|
||||
#include "portal_base2d.h"
|
||||
#endif
|
||||
|
||||
#endif //#ifndef PORTAL_BASE2D_SHARED_H
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "portal_base2d_shared.h"
|
||||
#include "portal_collideable_enumerator.h"
|
||||
|
||||
#define PORTAL_TELEPORTATION_PLANE_OFFSET 7.0f
|
||||
|
||||
CPortalCollideableEnumerator::CPortalCollideableEnumerator( const CPortal_Base2D *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();
|
||||
|
||||
// Ignore 'projected_wall_entity' objects for partial front/behind checks
|
||||
bool bIsProjectedWallEntity = FClassnameIs( pEnt, "projected_wall_entity" );
|
||||
|
||||
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 && !bIsProjectedWallEntity )
|
||||
return ITERATION_CONTINUE; //object wholly behind the portal
|
||||
|
||||
if( !(fPtPlaneDist > fBoundRadius) && (fPtPlaneDist > -fBoundRadius) && !bIsProjectedWallEntity ) //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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
|
||||
//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 CPortal_Base2D *pAssociatedPortal );
|
||||
virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //#ifndef PORTAL_COLLIDEABLE_ENUMERATOR_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,171 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Special handling for Portal usable ladders
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PORTAL_GAMEMOVEMENT_H
|
||||
#define PORTAL_GAMEMOVEMENT_H
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl_gamemovement.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#include "c_portal_player.h"
|
||||
#else
|
||||
#include "portal_player.h"
|
||||
#endif
|
||||
|
||||
class CTrace_PlayerAABB_vs_Portals;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Portal specific movement code
|
||||
//-----------------------------------------------------------------------------
|
||||
class CPortalGameMovement : public CGameMovement
|
||||
{
|
||||
typedef CGameMovement BaseClass;
|
||||
public:
|
||||
|
||||
CPortalGameMovement();
|
||||
|
||||
bool m_bInPortalEnv;
|
||||
// Overrides
|
||||
virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
|
||||
virtual const Vector& GetPlayerMins( bool ducked ) const;
|
||||
virtual const Vector& GetPlayerMaxs( bool ducked ) const;
|
||||
virtual const Vector& GetPlayerViewOffset( bool ducked ) const;
|
||||
virtual void SetupMovementBounds( CMoveData *pMove );
|
||||
virtual bool CheckJumpButton( void );
|
||||
|
||||
Vector PortalFunnel( const Vector &wishdir );
|
||||
|
||||
// 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, CTrace_PlayerAABB_vs_Portals& pm );
|
||||
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 int CheckStuck( void );
|
||||
|
||||
virtual void SetGroundEntity( trace_t *pm );
|
||||
|
||||
void HandlePortalling( void );
|
||||
|
||||
protected:
|
||||
|
||||
CPortal_Player *GetPortalPlayer() const;
|
||||
bool IsInPortalFunnelVolume( const Vector& vPlayerToPortal, const CPortal_Base2D* pPortal, const float flExtentX, const float flExtentY ) const;
|
||||
|
||||
// Does most of the player movement logic.
|
||||
// Returns with origin, angles, and velocity modified in place.
|
||||
// were contacted during the move.
|
||||
virtual void PlayerMove();
|
||||
|
||||
// Handles both ground friction and water friction
|
||||
virtual void Friction();
|
||||
|
||||
virtual void TBeamMove();
|
||||
|
||||
virtual void AirMove();
|
||||
virtual void AirAccelerate( Vector& wishdir, float wishspeed, float accel );
|
||||
|
||||
// Only used by players. Moves along the ground when player is a MOVETYPE_WALK.
|
||||
virtual void WalkMove();
|
||||
|
||||
virtual void WaterMove();
|
||||
|
||||
// Try to keep a walking player on the ground when running down slopes etc
|
||||
virtual void StayOnGround();
|
||||
|
||||
virtual void CheckWallImpact( Vector& primal_velocity );
|
||||
|
||||
// Handle MOVETYPE_WALK.
|
||||
virtual void FullWalkMove();
|
||||
|
||||
// Implement this if you want to know when the player collides during OnPlayerMove
|
||||
virtual void OnTryPlayerMoveCollision( trace_t &tr ) {}
|
||||
|
||||
virtual const Vector& GetPlayerMins() const; // uses local player
|
||||
virtual const Vector& GetPlayerMaxs() const; // uses local player
|
||||
|
||||
// Decompoosed gravity
|
||||
virtual void StartGravity();
|
||||
virtual void FinishGravity();
|
||||
|
||||
// Apply normal ( undecomposed ) gravity
|
||||
virtual void AddGravity();
|
||||
|
||||
// The basic solid body movement clip that slides along multiple planes
|
||||
virtual int TryPlayerMove( Vector *pFirstDest = NULL, trace_t *pFirstTrace = NULL );
|
||||
|
||||
// Slide off of the impacting object
|
||||
// returns the blocked flags:
|
||||
// 0x01 == floor
|
||||
// 0x02 == step / wall
|
||||
virtual int ClipVelocity( Vector& in, Vector& normal, Vector& out, float overbounce );
|
||||
|
||||
// Determine if player is in water, on ground, etc.
|
||||
virtual void CategorizePosition();
|
||||
|
||||
virtual void CheckParameters();
|
||||
|
||||
virtual void PlayerRoughLandingEffects( float fvol );
|
||||
virtual void PlayerWallImpactEffects( float fvol, float normalVelocity );
|
||||
virtual void PlayerCeilingImpactEffects( float fvol );
|
||||
|
||||
// Ducking
|
||||
virtual void Duck();
|
||||
virtual void FinishUnDuck();
|
||||
virtual void FinishDuck();
|
||||
virtual bool CanUnduck();
|
||||
virtual void UpdateDuckJumpEyeOffset();
|
||||
virtual bool CanUnDuckJump( trace_t &trace );
|
||||
virtual void StartUnDuckJump();
|
||||
virtual void FinishUnDuckJump( trace_t &trace );
|
||||
virtual void SetDuckedEyeOffset( float duckFraction );
|
||||
virtual void FixPlayerCrouchStuck( bool moveup );
|
||||
|
||||
virtual void CategorizeGroundSurface( trace_t &pm );
|
||||
|
||||
virtual void StepMove( Vector &vecDestination, trace_t &trace );
|
||||
|
||||
virtual bool GameHasLadders() const { return false; }
|
||||
|
||||
private:
|
||||
virtual bool PlayerShouldFunnel( const CPortal_Base2D* pPortal, const Vector& vPlayerForward, const Vector& wishDir ) const;
|
||||
|
||||
void AirPortalFunnel( Vector& wishdir,
|
||||
const Vector& vPlayerToFunnelPortal,
|
||||
float flExtraFunnelForce,
|
||||
float flTimeToPortal );
|
||||
|
||||
void GroundPortalFunnel( Vector& wishdir,
|
||||
const Vector& vPlayerToPortalFunnel,
|
||||
const Vector& vPortalNormal,
|
||||
float flExtraFunnelForce,
|
||||
float flTimeToPortal );
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
void ClientVerticalElevatorFixes( CBasePlayer *pPlayer, CMoveData *pMove );
|
||||
#endif
|
||||
|
||||
// Stick is done by changing gravity direction because it's easier to think about that way.
|
||||
// Gravity direction is always the negation of the player's stick normal.
|
||||
Vector m_vGravityDirection;
|
||||
|
||||
Vector m_vMoveStartPosition; //where the player started before the movement code ran
|
||||
};
|
||||
|
||||
|
||||
//trace that has special understanding of how to handle portals
|
||||
class CTrace_PlayerAABB_vs_Portals : public CGameTrace
|
||||
{
|
||||
public:
|
||||
CTrace_PlayerAABB_vs_Portals( void );
|
||||
bool HitPortalRamp( const Vector &vUp );
|
||||
|
||||
bool m_bContactedPortalTransitionRamp;
|
||||
};
|
||||
|
||||
|
||||
#endif //PORTAL_GAMEMOVEMENT_H
|
||||
@@ -0,0 +1,457 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
#include "weapon_portalgun_shared.h"
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
#include "player_voice_listener.h"
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#ifndef NO_STEAM
|
||||
#include "steam/steam_api.h"
|
||||
#endif //NO_STEAM
|
||||
#include "c_user_message_register.h"
|
||||
#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 "vscript_server.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
extern ConVar locator_lerp_rest;
|
||||
extern ConVar locator_start_at_crosshair;
|
||||
extern ConVar locator_topdown_style;
|
||||
extern ConVar locator_background_style;
|
||||
extern ConVar locator_background_color;
|
||||
extern ConVar locator_background_thickness_x;
|
||||
extern ConVar locator_background_thickness_y;
|
||||
extern ConVar locator_target_offset_x;
|
||||
extern ConVar locator_target_offset_y;
|
||||
extern ConVar locator_background_shift_x;
|
||||
extern ConVar locator_background_shift_y;
|
||||
extern ConVar locator_background_border_color;
|
||||
extern ConVar locator_icon_min_size_non_ss;
|
||||
extern ConVar locator_icon_max_size_non_ss;
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
REGISTER_GAMERULES_CLASS( CPortalGameRules );
|
||||
|
||||
BEGIN_NETWORK_TABLE_NOBASE( CPortalGameRules, DT_PortalGameRules )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( PortalGameRulesProxy, DT_PortalGameRulesProxy )
|
||||
LINK_ENTITY_TO_CLASS_ALIASED( portal_gamerules, 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
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
CPortalGameRules::CPortalGameRules()
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
g_pCVar->FindVar( "sv_maxreplay" )->SetValue( "1.5" );
|
||||
|
||||
if ( !GlobalEntity_IsInTable( "player_regenerates_health" ) )
|
||||
GlobalEntity_Add( MAKE_STRING("player_regenerates_health"), gpGlobals->mapname, GLOBAL_ON );
|
||||
else
|
||||
GlobalEntity_SetState( MAKE_STRING("player_regenerates_health"), GLOBAL_ON );
|
||||
|
||||
if ( gpGlobals->mapname.ToCStr() && StringHasPrefix( gpGlobals->mapname.ToCStr(), "mp_coop" ) )
|
||||
{
|
||||
static ConVarRef flashlightbrightness( "r_flashlightbrightness" );
|
||||
if ( flashlightbrightness.IsValid() )
|
||||
{
|
||||
// All MP maps use this brightness but don't set it explicitly... we need this here for MP maps in commentary mode
|
||||
flashlightbrightness.SetValue( 0.25f );
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
locator_lerp_rest.SetValue( 0.0f );
|
||||
locator_start_at_crosshair.SetValue( 0 );
|
||||
locator_topdown_style.SetValue( 0 );
|
||||
locator_background_style.SetValue( 0 );
|
||||
locator_background_color.SetValue( "0 0 0 128");
|
||||
locator_target_offset_x.SetValue( 0 );
|
||||
locator_target_offset_y.SetValue( 0 );
|
||||
locator_background_thickness_x.SetValue( 12 );
|
||||
locator_background_thickness_y.SetValue( 12 );
|
||||
locator_background_shift_x.SetValue( 0 );
|
||||
locator_background_shift_y.SetValue( 0 );
|
||||
locator_background_border_color.SetValue( "32 32 32 64" );
|
||||
locator_icon_min_size_non_ss.SetValue( 1.5f );
|
||||
locator_icon_max_size_non_ss.SetValue( 1.75f );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Parse commands coming down from the client
|
||||
// ------------------------------------------------------------------------------------
|
||||
bool CPortalGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
|
||||
{
|
||||
const char *pcmd = args[0];
|
||||
if ( FStrEq( pcmd, "lobby_select_day" ) )
|
||||
{
|
||||
if ( args.ArgC() < 2 )
|
||||
return true;
|
||||
|
||||
//int nDay = atoi( args[1] );
|
||||
// Msg("Selecting day %d\n", nDay );
|
||||
return true;
|
||||
}
|
||||
|
||||
return BaseClass::ClientCommand( pEdict, args );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
//
|
||||
// ------------------------------------------------------------------------------------
|
||||
const char *CPortalGameRules::GetGameDescription( void )
|
||||
{
|
||||
#ifdef PORTAL2
|
||||
return "Portal 2";
|
||||
#else
|
||||
return "Portal";
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
//
|
||||
// ------------------------------------------------------------------------------------
|
||||
bool CPortalGameRules::AllowDamage( CBaseEntity *pVictim, const CTakeDamageInfo &info )
|
||||
{
|
||||
return BaseClass::AllowDamage( pVictim, info );
|
||||
}
|
||||
|
||||
bool CPortalGameRules::IsSavingAllowed( void )
|
||||
{
|
||||
if ( UTIL_GetLocalPlayerOrListenServerHost()->GetBonusChallenge() > 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
//
|
||||
// ------------------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool CPortalGameRules::IsChallengeMode()
|
||||
{
|
||||
CBasePlayer* pPlayer = UTIL_PlayerByIndex( 1 );
|
||||
if ( pPlayer )
|
||||
return pPlayer->GetBonusChallenge() != 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif// !( CLIENT_DLL )
|
||||
|
||||
bool CPortalGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
|
||||
{
|
||||
if ( collisionGroup0 > collisionGroup1 )
|
||||
{
|
||||
// swap so that lowest is always first
|
||||
V_swap(collisionGroup0,collisionGroup1);
|
||||
}
|
||||
|
||||
// Cubes shouldn't collide with debris but should otherwise act like COLLISION_GROUP_NONE
|
||||
if( collisionGroup1 == COLLISION_GROUP_WEIGHTED_CUBE && collisionGroup0 == COLLISION_GROUP_DEBRIS )
|
||||
return false;
|
||||
|
||||
if( collisionGroup0 == COLLISION_GROUP_WEIGHTED_CUBE )
|
||||
collisionGroup0 = COLLISION_GROUP_NONE;
|
||||
|
||||
if( collisionGroup1 == COLLISION_GROUP_WEIGHTED_CUBE )
|
||||
collisionGroup1 = COLLISION_GROUP_NONE;
|
||||
|
||||
return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------ //
|
||||
// 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 );
|
||||
#ifndef PORTAL2
|
||||
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
|
||||
#endif
|
||||
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
|
||||
#ifndef PORTAL2
|
||||
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 );
|
||||
#endif
|
||||
def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, "sk_max_ar2_altfire", 0, 0 );
|
||||
|
||||
def.AddAmmoType("PortalTurretBullet", DMG_BULLET, TRACER_LINE_AND_WHIZ, 3, 3, 150, BULLET_IMPULSE(200, 1225), 0 );
|
||||
}
|
||||
|
||||
return &def;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Portal singleplayer specific global vscript functions
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
#if !defined ( CLIENT_DLL )
|
||||
static bool ScriptIsMultiplayer( void )
|
||||
{
|
||||
return false;//g_pGameRules->IsMultiplayer();
|
||||
}
|
||||
|
||||
static bool TryDLC1InstalledOrCatch( void )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
extern float GetPlayerSilenceDuration( int nPlayer );
|
||||
extern int GetOrangePlayerIndex( void );
|
||||
extern int GetBluePlayerIndex( void );
|
||||
extern int GetCoopSectionIndex( void );
|
||||
extern int GetCoopBranchLevelIndex( int nBranch );
|
||||
extern int GetHighestActiveBranch( void );
|
||||
extern void AddBranchLevelName( int nBranch, const char *pchName );
|
||||
extern void MarkMapComplete( const char *pchName );
|
||||
extern bool IsLevelComplete( int nBranch, int nLevel );
|
||||
extern bool IsPlayerLevelComplete( int nPlayer, int nBranch, int nLevel );
|
||||
extern void AddCoopCreditsName( const char *pchName );
|
||||
|
||||
bool ScriptSteamShowURL( const char *pURL )
|
||||
{
|
||||
#if !defined(NO_STEAM)
|
||||
if ( steamapicontext && steamapicontext->SteamFriends() &&
|
||||
steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->IsOverlayEnabled() )
|
||||
{
|
||||
steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( pURL );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptShowHudMessageAll( const char *pMsg, float flHoldTime )
|
||||
{
|
||||
hudtextparms_t tTextParam = {0};
|
||||
tTextParam.x = -1;
|
||||
tTextParam.y = -1;
|
||||
tTextParam.effect = 0;
|
||||
tTextParam.r1 = 255;
|
||||
tTextParam.g1 = 255;
|
||||
tTextParam.b1 = 255;
|
||||
tTextParam.a1 = 255;
|
||||
tTextParam.r2 = 255;
|
||||
tTextParam.g2 = 255;
|
||||
tTextParam.b2 = 255;
|
||||
tTextParam.a2 = 255;
|
||||
tTextParam.fadeinTime = 0;
|
||||
tTextParam.fadeoutTime = 0;
|
||||
tTextParam.holdTime = flHoldTime;
|
||||
tTextParam.fxTime = 0;
|
||||
tTextParam.channel = 1;
|
||||
UTIL_HudMessageAll( tTextParam, pMsg );
|
||||
}
|
||||
|
||||
void GivePlayerPortalgun( void )
|
||||
{
|
||||
for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
|
||||
{
|
||||
CPortal_Player *pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
|
||||
if ( pPlayer )
|
||||
{
|
||||
pPlayer->GivePlayerPortalGun( false, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradePlayerPortalgun( void )
|
||||
{
|
||||
for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
|
||||
{
|
||||
CPortal_Player *pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
|
||||
if ( pPlayer )
|
||||
{
|
||||
CWeaponPortalgun *pPortalGun = static_cast< CWeaponPortalgun* >( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
|
||||
if ( pPortalGun )
|
||||
{
|
||||
pPortalGun->SetCanFirePortal1();
|
||||
pPortalGun->SetCanFirePortal2();
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "Portalgun upgrade failed! Player not holding a portalgun.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradePlayerPotatogun( void )
|
||||
{
|
||||
for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
|
||||
{
|
||||
CPortal_Player *pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
|
||||
if ( pPlayer )
|
||||
{
|
||||
CWeaponPortalgun *pPortalGun = static_cast< CWeaponPortalgun* >( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
|
||||
if ( pPortalGun )
|
||||
{
|
||||
pPortalGun->SetCanFirePortal1();
|
||||
pPortalGun->SetCanFirePortal2();
|
||||
pPortalGun->SetPotatosOnPortalgun( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "Potatogun upgrade failed! Player not holding a portalgun.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HSCRIPT GetPlayer( void )
|
||||
{
|
||||
return ToHScript( UTIL_GetLocalPlayer() );
|
||||
}
|
||||
|
||||
void CPortalGameRules::RegisterScriptFunctions( void )
|
||||
{
|
||||
ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsMultiplayer, "IsMultiplayer", "Is this a multiplayer game?" );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetPlayerSilenceDuration, "Time that the specified player has been silent on the mic." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetOrangePlayerIndex, "Player index of the orange player." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetBluePlayerIndex, "Player index of the blue player." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetCoopSectionIndex, "Section that the coop players have selected to load." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetCoopBranchLevelIndex, "Given the 'branch' argument, returns the current chosen level." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetHighestActiveBranch, "Returns which branches should be available in the hub." );
|
||||
ScriptRegisterFunction( g_pScriptVM, AddBranchLevelName, "Adds a level to the specified branche's list." );
|
||||
ScriptRegisterFunction( g_pScriptVM, MarkMapComplete, "Marks a maps a complete for both players." );
|
||||
ScriptRegisterFunction( g_pScriptVM, IsLevelComplete, "Returns true if the level in the specified branch is completed by either player." );
|
||||
ScriptRegisterFunction( g_pScriptVM, IsPlayerLevelComplete, "Returns true if the level in the specified branch is completed by a specific player." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GetPlayer, "Returns the player (SP Only)." );
|
||||
ScriptRegisterFunction( g_pScriptVM, PrecacheMovie, "Precaches a named movie. Only valid to call within the entity's 'Precache' function called on mapspawn." );
|
||||
ScriptRegisterFunction( g_pScriptVM, AddCoopCreditsName, "Adds a name to the coop credit's list." );
|
||||
ScriptRegisterFunction( g_pScriptVM, ScriptSteamShowURL, "Bring up the steam overlay and shows the specified URL. (Full address with protocol type is required, e.g. http://www.steamgames.com/)" );
|
||||
ScriptRegisterFunction( g_pScriptVM, ScriptShowHudMessageAll, "Show center print text message." );
|
||||
ScriptRegisterFunction( g_pScriptVM, GivePlayerPortalgun, "Give player the portalgun." );
|
||||
ScriptRegisterFunction( g_pScriptVM, UpgradePlayerPortalgun, "Give player the portalgun." );
|
||||
ScriptRegisterFunction( g_pScriptVM, UpgradePlayerPotatogun, "Give player the portalgun." );
|
||||
ScriptRegisterFunction( g_pScriptVM, TryDLC1InstalledOrCatch, "Tests if the DLC1 is installed for Try/Catch blocks." );
|
||||
g_pScriptVM->RegisterInstance( &PlayerVoiceListener(), "PlayerVoiceListener" );
|
||||
}
|
||||
#endif // !CLIENT_DLL
|
||||
@@ -0,0 +1,79 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Game rules for Portal.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTAL_GAMERULES_H
|
||||
#define PORTAL_GAMERULES_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gamerules.h"
|
||||
#include "hl2_gamerules.h"
|
||||
|
||||
#define DISABLE_DEBUG_HISTORY 1
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CPortalGameRules C_PortalGameRules
|
||||
#define CPortalGameRulesProxy C_PortalGameRulesProxy
|
||||
#endif
|
||||
|
||||
|
||||
class CPortalGameRulesProxy : public CGameRulesProxy
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPortalGameRulesProxy, CGameRulesProxy );
|
||||
DECLARE_NETWORKCLASS();
|
||||
};
|
||||
|
||||
|
||||
class CPortalGameRules : public CHalfLife2
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPortalGameRules, CHalfLife2 );
|
||||
|
||||
CPortalGameRules();
|
||||
virtual ~CPortalGameRules() {}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool IsBonusChallengeTimeBased( void );
|
||||
virtual bool IsChallengeMode();
|
||||
#endif
|
||||
|
||||
virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 );
|
||||
|
||||
private:
|
||||
|
||||
#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.
|
||||
|
||||
public:
|
||||
|
||||
virtual const char * GetGameDescription( void );
|
||||
virtual bool AllowDamage( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
||||
virtual void RegisterScriptFunctions( void );
|
||||
|
||||
virtual bool ShouldBurningPropsEmitLight() { return false; }
|
||||
virtual float FlPlayerFallDamage( CBasePlayer *pPlayer ) { return 0.0f; } //no fall damage in portal
|
||||
virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args );
|
||||
|
||||
virtual bool IsSavingAllowed( void );
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets us at the Portal game rules
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CPortalGameRules* PortalGameRules()
|
||||
{
|
||||
return dynamic_cast<CPortalGameRules*>(g_pGameRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // PORTAL_GAMERULES_H
|
||||
@@ -0,0 +1,108 @@
|
||||
//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
|
||||
#ifdef GAME_DLL
|
||||
#include "GameStats.h"
|
||||
#endif
|
||||
#include "portal_gamestats_shared.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Helper functions for creating key values
|
||||
//
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, int data )
|
||||
{
|
||||
pKV->SetInt( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, uint64 data )
|
||||
{
|
||||
pKV->SetUint64( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, float data )
|
||||
{
|
||||
pKV->SetFloat( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, bool data )
|
||||
{
|
||||
pKV->SetBool( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, const char* data )
|
||||
{
|
||||
pKV->SetString( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, const Color& data )
|
||||
{
|
||||
pKV->SetColor( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, short data )
|
||||
{
|
||||
pKV->SetInt( name, data );
|
||||
}
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, unsigned data )
|
||||
{
|
||||
pKV->SetInt( name, data );
|
||||
}
|
||||
void AddPositionDataToKV( KeyValues* pKV, const char* name, const Vector &data )
|
||||
{
|
||||
// Append the data name to the member
|
||||
pKV->SetFloat( CFmtStr("%s%s", name, "_X"), data.x );
|
||||
pKV->SetFloat( CFmtStr("%s%s", name, "_Y"), data.y );
|
||||
pKV->SetFloat( CFmtStr("%s%s", name, "_Z"), data.z );
|
||||
}
|
||||
|
||||
//=============================================================================//
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Helper functions for creating key values from arrays
|
||||
//
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const short *data, unsigned size )
|
||||
{
|
||||
for( unsigned i=0; i<size; ++i )
|
||||
pKV->SetInt( CFmtStr("%s_%d", name, i) , data[i] );
|
||||
}
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const byte *data, unsigned size )
|
||||
{
|
||||
for( unsigned i=0; i<size; ++i )
|
||||
pKV->SetInt( CFmtStr("%s_%d", name, i), data[i] );
|
||||
}
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const unsigned *data, unsigned size )
|
||||
{
|
||||
for( unsigned i=0; i<size; ++i )
|
||||
pKV->SetInt( CFmtStr("%s_%d", name, i), data[i] );
|
||||
}
|
||||
void AddStringDataToKV( KeyValues* pKV, const char* name, const char*data )
|
||||
{
|
||||
if( name == NULL )
|
||||
return;
|
||||
|
||||
pKV->SetString( name, data );
|
||||
}
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
void IGameStatTracker::PrintGamestatMemoryUsage( void )
|
||||
{
|
||||
StatContainerList_t* pStatList = GetStatContainerList();
|
||||
if( !pStatList )
|
||||
return;
|
||||
|
||||
int iListSize = pStatList->Count();
|
||||
|
||||
// For every stat list being tracked, print out its memory usage
|
||||
for( int i=0; i < iListSize; ++i )
|
||||
{
|
||||
pStatList->operator []( i )->PrintMemoryUsage();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _GAMECONSOLE
|
||||
@@ -0,0 +1,266 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Purpose: Contains everything needed to create different gamestats tracking
|
||||
// systems.
|
||||
//
|
||||
//===============================================================================
|
||||
#if !defined( PORTAL_GAMESTATS_SHARED_H ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
|
||||
#define PORTAL_GAMESTATS_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
#include "cbase.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "tier1/utldict.h"
|
||||
#include "shareddefs.h"
|
||||
#include "fmtstr.h"
|
||||
#include "steamworks_gamestats.h"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Helper functions for creating key values
|
||||
//
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, int data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, uint64 data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, float data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, bool data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, const char* data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, const Color& data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, short data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, unsigned data );
|
||||
void AddDataToKV( KeyValues* pKV, const char* name, const Vector& data );
|
||||
void AddPositionDataToKV( KeyValues* pKV, const char* name, const Vector &data );
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Helper functions for creating key values from arrays
|
||||
//
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const short *data, unsigned size );
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const byte *data, unsigned size );
|
||||
void AddArrayDataToKV( KeyValues* pKV, const char* name, const unsigned *data, unsigned size );
|
||||
void AddStringDataToKV( KeyValues* pKV, const char* name, const char *data );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Macros to ease the creation of SendData method for stats structs/classes
|
||||
#define BEGIN_STAT_TABLE( tableName ) \
|
||||
static const char* GetStatTableName( void ) { return tableName; } \
|
||||
void BuildGamestatDataTable( KeyValues* pKV ) \
|
||||
{ \
|
||||
pKV->SetName( GetStatTableName() );
|
||||
|
||||
#define REGISTER_STAT( varName ) \
|
||||
AddDataToKV(pKV, #varName, varName);
|
||||
|
||||
#define REGISTER_STAT_NAMED( varName, dbName ) \
|
||||
AddDataToKV(pKV, dbName, varName);
|
||||
|
||||
#define REGISTER_STAT_POSITION( varName ) \
|
||||
AddPositionDataToKV(pKV, #varName, varName);
|
||||
|
||||
#define REGISTER_STAT_POSITION_NAMED( varName, dbName ) \
|
||||
AddPositionDataToKV(pKV, dbName, varName);
|
||||
|
||||
#define REGISTER_STAT_ARRAY( varName ) \
|
||||
AddArrayDataToKV( pKV, #varName, varName, ARRAYSIZE( varName ) );
|
||||
|
||||
#define REGISTER_STAT_ARRAY_NAMED( varName, dbName ) \
|
||||
AddArrayDataToKV( pKV, dbName, varName, ARRAYSIZE( varName ) );
|
||||
|
||||
#define REGISTER_STAT_STRING( varName ) \
|
||||
AddStringDataToKV( pKV, #varName, varName );
|
||||
|
||||
#define REGISTER_STAT_STRING_NAMED( varName, dbName ) \
|
||||
AddStringDataToKV( pKV, dbName, varName );
|
||||
|
||||
#define AUTO_STAT_TABLE_KEY() \
|
||||
pKV->SetInt( "TimeSubmitted", GetUniqueIDForStatTable( *this ) );
|
||||
|
||||
#define END_STAT_TABLE() \
|
||||
pKV->SetUint64( "TimeSubmitted", ::BaseStatData::TimeSubmitted ); \
|
||||
GetSteamWorksSGameStatsUploader().AddStatsForUpload( pKV ); \
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Templatized class for getting unique ID's for stat tables that need
|
||||
// to be submitted multiple times per-session.
|
||||
//-----------------------------------------------------------------------------
|
||||
template < typename T >
|
||||
class UniqueStatID_t
|
||||
{
|
||||
public:
|
||||
static unsigned GetNext( void )
|
||||
{
|
||||
return ++s_nLastID;
|
||||
}
|
||||
|
||||
static void Reset( void )
|
||||
{
|
||||
s_nLastID = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static unsigned s_nLastID;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
unsigned UniqueStatID_t< T >::s_nLastID = 0;
|
||||
|
||||
template < typename T >
|
||||
unsigned GetUniqueIDForStatTable( const T &table )
|
||||
{
|
||||
return UniqueStatID_t< T >::GetNext();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// An interface for tracking gamestats.
|
||||
//
|
||||
class IGameStatTracker
|
||||
{
|
||||
public:
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Templatized methods to track a per-mission stat.
|
||||
// The stat is copied, then deleted after it's sent to the SQL server.
|
||||
//-----------------------------------------------------------------------------
|
||||
template < typename T >
|
||||
void SubmitStat( T& stat )
|
||||
{
|
||||
// Make a copy of the stat. All of the stat lists require pointers,
|
||||
// so we need to protect against a stat allocated on the stack
|
||||
T* pT = new T();
|
||||
if( !pT )
|
||||
return;
|
||||
|
||||
*pT = stat;
|
||||
SubmitStat( pT );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Templatized methods to track a per-mission stat (by pointer)
|
||||
// The stat is deleted after it's sent to the SQL server
|
||||
//-----------------------------------------------------------------------------
|
||||
template < typename T >
|
||||
void SubmitStat( T* pStat )
|
||||
{
|
||||
// Get the static stat table for this type and add the stat to it
|
||||
GetStatTable<T>()->AddToTail( pStat );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add all stats to an existing key value file for submit.
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual void SubmitGameStats( KeyValues *pKV ) = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Prints the memory usage of all of the stats being tracked
|
||||
//-----------------------------------------------------------------------------
|
||||
void PrintGamestatMemoryUsage( void );
|
||||
|
||||
protected:
|
||||
//=============================================================================
|
||||
//
|
||||
// Used as a base interface to store a list of all templatized stat containers
|
||||
//
|
||||
class IStatContainer
|
||||
{
|
||||
public:
|
||||
virtual void SendData( KeyValues *pKV ) = 0;
|
||||
virtual void Clear( void ) = 0;
|
||||
virtual void PrintMemoryUsage( void ) = 0;
|
||||
};
|
||||
|
||||
// Defines a list of stat containers.
|
||||
typedef CUtlVector< IStatContainer* > StatContainerList_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Used to get a list of all stats containers being tracked by the deriving class
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual StatContainerList_t* GetStatContainerList( void ) = 0;
|
||||
|
||||
private:
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Templatized list of stats submitted
|
||||
//
|
||||
template < typename T >
|
||||
class CGameStatList : public IStatContainer, public CUtlVector< T* >
|
||||
{
|
||||
public:
|
||||
//-----------------------------------------------------------------------------
|
||||
// Get data ready to send to the SQL server
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual void SendData( KeyValues *pKV )
|
||||
{
|
||||
//ASSERT( pKV != NULL );
|
||||
|
||||
// Duplicate the master KeyValue for each stat instance
|
||||
for( int i=0; i < this->m_Size; ++i )
|
||||
{
|
||||
// Make a copy of the master key value and build the stat table
|
||||
KeyValues *pKVCopy = this->operator [](i)->m_bUseGlobalData ? pKV->MakeCopy() : new KeyValues( "" );
|
||||
this->operator [](i)->BuildGamestatDataTable( pKVCopy );
|
||||
}
|
||||
|
||||
// Reset unique ID counter for the stat type
|
||||
UniqueStatID_t< T >::Reset();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clear and delete every stat in this list
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual void Clear( void )
|
||||
{
|
||||
this->PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Print out details about this lists memory usage
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual void PrintMemoryUsage( void )
|
||||
{
|
||||
if( this->m_Size == 0 )
|
||||
return;
|
||||
|
||||
// Compute the memory used as the size of type times the list count
|
||||
unsigned uMemoryUsed = this->m_Size * ( sizeof( T ) );
|
||||
|
||||
Msg( CFmtStr( " %d\tbytes used by %s table\n", uMemoryUsed, T::GetStatTableName() ) );
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Templatized method to get a single instance of a stat list per data type.
|
||||
//-----------------------------------------------------------------------------
|
||||
template < typename T >
|
||||
CGameStatList< T >* GetStatTable( void )
|
||||
{
|
||||
static CGameStatList< T > *s_vecOfType = 0;
|
||||
if( s_vecOfType == 0 )
|
||||
{
|
||||
s_vecOfType = new CGameStatList< T >();
|
||||
GetStatContainerList()->AddToTail( s_vecOfType );
|
||||
}
|
||||
return s_vecOfType;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct BaseStatData
|
||||
{
|
||||
BaseStatData()
|
||||
{
|
||||
TimeSubmitted = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
|
||||
m_bUseGlobalData = true;
|
||||
}
|
||||
|
||||
bool m_bUseGlobalData;
|
||||
uint64 TimeSubmitted;
|
||||
|
||||
};
|
||||
//=============================================================================
|
||||
#endif // PORTAL_GAMESTATS_SHARED_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Game rules for Portal multiplayer testing.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#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
|
||||
|
||||
// NOTE!!! YOU MUST UPDATE MACROS PropStringArrayArrayInnerList and PropStringArrayArrayOuterList IF YOU CHANGE THESE!!!
|
||||
#define MAX_PORTAL2_COOP_LEVELS_PER_BRANCH 16 // max number of levels per branch
|
||||
#define MAX_PORTAL2_COOP_BRANCHES 6 // max numbers or "branches" or paths in the coop campaign
|
||||
#define MAX_PORTAL2_COOP_LEVEL_NAME_SIZE 64 // max numbers or "branches" or paths in the coop campaign
|
||||
#define MAX_COOP_CREDITS_NAME_LENGTH 128
|
||||
|
||||
//for any code that needs updating, red was combine/terrorists, blue was rebels/counterterrorists
|
||||
enum
|
||||
{
|
||||
TEAM_RED = 2,
|
||||
TEAM_BLUE,
|
||||
};
|
||||
|
||||
enum Coop_CreditsState_t
|
||||
{
|
||||
LIST_NAMES = 0,
|
||||
SHOW_TEXT_BLOCK,
|
||||
LAST_CREDITSSTATE
|
||||
};
|
||||
|
||||
enum Coop_Taunts
|
||||
{
|
||||
TAUNT_HIGHFIVE = 0,
|
||||
TAUNT_WAVE,
|
||||
TAUNT_RPS,
|
||||
TAUNT_LAUGH,
|
||||
TAUNT_ROBOTDANCE,
|
||||
TAUNT_CORETEASE,
|
||||
TAUNT_HUG,
|
||||
TAUNT_TRICKFIRE,
|
||||
MAX_PORTAL2_COOP_TAUNTS,
|
||||
};
|
||||
|
||||
class CPortalMPGameRulesProxy : public CGameRulesProxy
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPortalMPGameRulesProxy, CGameRulesProxy );
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_DATADESC();
|
||||
void InputAddRedTeamScore( inputdata_t &inputdata );
|
||||
void InputAddBlueTeamScore( inputdata_t &inputdata );
|
||||
#endif
|
||||
};
|
||||
|
||||
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 void LevelInitPreEntity();
|
||||
|
||||
#if !defined ( CLIENT_DLL )
|
||||
virtual const char *GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer );
|
||||
#endif
|
||||
|
||||
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;
|
||||
void RunPlayerConditionThink( void );
|
||||
virtual void FrameUpdatePostEntityThink( void );
|
||||
|
||||
virtual bool IsCoOp( void );
|
||||
bool Is2GunsCoOp( void );
|
||||
bool IsVS( void );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool IsChallengeMode();
|
||||
#endif
|
||||
|
||||
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 );
|
||||
virtual void PlayerSpawn( CBasePlayer *pPlayer );
|
||||
virtual bool FPlayerCanRespawn( CBasePlayer *pPlayer );
|
||||
virtual void ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues );
|
||||
|
||||
void AddLevelDesignerPlacedObject( CBaseEntity *pEntity );
|
||||
void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity );
|
||||
void ManageObjectRelocation( void );
|
||||
|
||||
virtual float GetLaserTurretDamage( void );
|
||||
virtual float GetLaserTurretMoveSpeed( void );
|
||||
virtual float GetRocketTurretDamage( void );
|
||||
virtual float FlPlayerFallDamage( CBasePlayer *pPlayer );
|
||||
|
||||
virtual void InitDefaultAIRelationships();
|
||||
|
||||
virtual void RegisterScriptFunctions();
|
||||
|
||||
void SetMapCompleteData( int nPlayer );
|
||||
bool IsPlayerDataReceived( int nPlayer ) const { return m_bDataReceived[ nPlayer ]; }
|
||||
void StartPlayerTransitionThinks( void );
|
||||
|
||||
virtual void ClientDisconnected( edict_t *pClient );
|
||||
#endif
|
||||
|
||||
bool CheckGameOver( void );
|
||||
bool IsIntermission( void );
|
||||
|
||||
void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );
|
||||
|
||||
|
||||
bool IsTeamplay( void ) { return m_bTeamPlayEnabled; }
|
||||
void CheckAllPlayersReady( void );
|
||||
virtual bool ForceSplitScreenPlayersOnToSameTeam() { return false; }
|
||||
int GetCoopSection( void ) { return m_nCoopSectionIndex; }
|
||||
int GetCoopBranchLevel( int nBranch ) { return m_nCoopBranchIndex[nBranch]; }
|
||||
bool IsAnyLevelComplete( void );
|
||||
bool IsFullBranchComplete( int nBranch );
|
||||
bool IsPlayerFullBranchComplete( int nPlayer, int nBranch );
|
||||
bool IsLevelInBranchComplete( int nBranch, int nLevel );
|
||||
bool IsPlayerLevelInBranchComplete( int nPlayer, int nBranch, int nLevel ) { return m_bLevelCompletions[ nPlayer ][ nBranch ][ nLevel ]; }
|
||||
const char *GetBranchLevelName( int nBranch = 0, int nLevel = 0 ) { return m_szLevelNames[nBranch][nLevel]; }
|
||||
int GetBranchTotalLevelCount( int nBranch = 0 ) { return m_nLevelCount[nBranch]; }
|
||||
int GetActiveBranches( void );
|
||||
int GetSelectedDLCCourse( void );
|
||||
|
||||
// CREDITS
|
||||
const char *GetCoopCreditsNameSingle() { return m_szCoopCreditsNameSingle; }
|
||||
const char *GetCoopCreditsJobTitle() { return m_szCoopCreditsJobTitle; }
|
||||
int GetCoopCreditsNameIndex( void ) { return m_nCoopCreditsIndex; }
|
||||
int GetCoopCreditsState( void ) { return m_nCoopCreditsState; }
|
||||
int GetCoopCreditsScanState( void ) { return m_nCoopCreditsScanState; }
|
||||
bool GetCoopCreditsFadeState( void ) { return m_bCoopFadeCreditsState; }
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
void SaveMPStats( void );
|
||||
void AddBranchLevel( int nBranch, const char *pchName );
|
||||
void SetAllMapsComplete( bool bComplete = true, int nPlayer = -1 );
|
||||
void SetBranchComplete( int nBranch, bool bComplete = true );
|
||||
void SetMapComplete( const char *pchName, bool bComplete = true );
|
||||
void SetMapCompleteSimple( int nPlayer, const char *pchName, bool bComplete );
|
||||
void SendAllMapCompleteData( void );
|
||||
bool SupressSpawnPortalgun( int nTeam );
|
||||
void PlayerWinRPS( CBasePlayer* pWinnerPlayer );
|
||||
int GetRPSOutcome( void ) { return m_nRPSOutcome; }
|
||||
void ShuffleRPSOutcome( void ) { m_nRPSOutcome = RandomInt( 0, 4 ); }
|
||||
void AddCreditsName( const char *pchName );
|
||||
void SetGladosJustBlewUpBots( void ) { m_bGladosJustBlewUp = true; }
|
||||
int GetLevelsCompletedThisBranch( void );
|
||||
#endif
|
||||
|
||||
void SetMapComplete( int nPlayer, int nBranch, int nLevel, bool bComplete = true );
|
||||
bool IsLobbyMap( void );
|
||||
bool IsStartMap( void );
|
||||
bool IsCreditsMap( void );
|
||||
bool IsCommunityCoopHub( void );
|
||||
bool IsCommunityCoop( void );
|
||||
|
||||
void PortalPlaced( void ) { m_nNumPortalsPlaced++; }
|
||||
int GetNumPortalsPlaced( void ) { return m_nNumPortalsPlaced; }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void KeyValueBuilder( KeyValues *pKeyValues );
|
||||
void SaveMapCompleteData( void );
|
||||
void LoadMapCompleteData( void );
|
||||
|
||||
bool IsClientCrossplayingPCvsPC() const { return m_bIsClientCrossplayingPCvsPC; }
|
||||
#endif
|
||||
|
||||
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_bGladosJustBlewUp;
|
||||
bool m_bHeardAllPlayersReady;
|
||||
bool m_bIsCoopInMapName;
|
||||
bool m_bIs2GunsInMapName;
|
||||
bool m_bIsVSInMapName;
|
||||
float m_fNextDLCSelectTime;
|
||||
CNetworkVar( int, m_nCoopSectionIndex );
|
||||
CNetworkArray( int, m_nCoopBranchIndex, MAX_PORTAL2_COOP_BRANCHES );
|
||||
CNetworkVar( int, m_nSelectedDLCCourse );
|
||||
CNetworkVar( int, m_nNumPortalsPlaced ); // Number of portals the players have placed so far this round.
|
||||
|
||||
CNetworkVar( bool, m_bMapNamesLoaded );
|
||||
char m_szLevelNames[ MAX_PORTAL2_COOP_BRANCHES ][ MAX_PORTAL2_COOP_LEVELS_PER_BRANCH ][ MAX_PORTAL2_COOP_LEVEL_NAME_SIZE ];
|
||||
CNetworkArray( int, m_nLevelCount, MAX_PORTAL2_COOP_BRANCHES );
|
||||
|
||||
CNetworkVar( bool, m_bCoopCreditsLoaded );
|
||||
CUtlVector< CUtlString > m_szCoopCreditsNames;
|
||||
CNetworkString( m_szCoopCreditsNameSingle, MAX_COOP_CREDITS_NAME_LENGTH );
|
||||
CNetworkString( m_szCoopCreditsJobTitle, MAX_COOP_CREDITS_NAME_LENGTH );
|
||||
CNetworkVar( int, m_nCoopCreditsIndex );
|
||||
CNetworkVar( int, m_nCoopCreditsState );
|
||||
CNetworkVar( int, m_nCoopCreditsScanState );
|
||||
CNetworkVar( bool, m_bCoopFadeCreditsState );
|
||||
|
||||
bool m_bLevelCompletions[ 2 ][ MAX_PORTAL2_COOP_BRANCHES ][ MAX_PORTAL2_COOP_LEVELS_PER_BRANCH ];
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
bool m_bDataReceived[ 2 ];
|
||||
|
||||
int m_nRPSWinCount[ 2 ];
|
||||
int m_nRPSOutcome;
|
||||
#else
|
||||
bool m_bIsClientCrossplayingPCvsPC;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets us at the Half-Life 2 game rules
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CPortalMPGameRules* PortalMPGameRules()
|
||||
{
|
||||
extern CPortalMPGameRules *g_pPortalMPGameRules;
|
||||
return g_pPortalMPGameRules;
|
||||
}
|
||||
|
||||
bool IsLocalSplitScreen( void );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
bool ClientIsCrossplayingWithConsole( void );
|
||||
#endif
|
||||
|
||||
|
||||
#endif // PORTAL_MP_GAMERULES_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTAL_PLACEMENT_H
|
||||
#define PORTAL_PLACEMENT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
struct CPortalCornerFitData;
|
||||
#include "portal_shareddefs.h"
|
||||
#include "CegClientWrapper.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
class C_Prop_Portal;
|
||||
typedef C_Prop_Portal CProp_Portal;
|
||||
#else
|
||||
class CProp_Portal;
|
||||
#endif
|
||||
|
||||
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,
|
||||
PortalPlacedBy_t ePlacedBy, ITraceFilter *pTraceFilterPortalShot,
|
||||
float fHalfWidth, float fHalfHeight,
|
||||
int iRecursions = 0, const CPortalCornerFitData *pPortalCornerFitData = 0, const int *p_piIntersectionIndex = 0, const int *piIntersectionCount = 0 );
|
||||
bool IsPortalIntersectingNoPortalVolume( const Vector &vOrigin, const QAngle &qAngles, const Vector &vForward, float fHalfWidth, float fHalfHeight );
|
||||
PortalPlacementResult_t IsPortalOverlappingOtherPortals( const CProp_Portal *pIgnorePortal, const Vector &vOrigin, const QAngle &qAngles, float fHalfWidth, float fHalfHeight, bool bFizzleAll = false, bool bFizzlePartnerPortals = false );
|
||||
PortalPlacementResult_t VerifyPortalPlacement( const CProp_Portal *pIgnorePortal, Vector &vOrigin, QAngle &qAngles, float fHalfWidth, float fHalfHeight, PortalPlacedBy_t ePlacedBy );
|
||||
PortalPlacementResult_t VerifyPortalPlacementAndFizzleBlockingPortals( const CProp_Portal *pIgnorePortal, Vector &vOrigin, QAngle &qAngles, float fHalfWidth, float fHalfHeight, PortalPlacedBy_t ePlacedBy );
|
||||
bool PortalPlacementSucceeded( PortalPlacementResult_t eResult );
|
||||
bool IsNoPortalMaterial( const trace_t &tr );
|
||||
PortalSurfaceType_t PortalSurfaceType( const trace_t& tr );
|
||||
bool IsOnPortalPaint( const trace_t &tr );
|
||||
void InitSurfNoPortalFlag();
|
||||
void InitPortalPaintPowerValue();
|
||||
|
||||
#endif // PORTAL_PLACEMENT_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,343 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PORTAL_PLAYER_SHARED_H
|
||||
#define PORTAL_PLAYER_SHARED_H
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
#include "paint_color_manager.h"
|
||||
#include "cegclientwrapper.h"
|
||||
|
||||
#define PORTAL_PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f)
|
||||
|
||||
// Max mass the player can lift with +use
|
||||
#define PORTAL_PLAYER_MAX_LIFT_MASS 85
|
||||
#define PORTAL_PLAYER_MAX_LIFT_SIZE 128
|
||||
|
||||
#define PLAYERPORTALDEBUGSPEW 0
|
||||
|
||||
const char *GetEggBotModel( bool bLowRes = false );
|
||||
const char *GetBallBotModel( bool bLowRes = false );
|
||||
|
||||
class ISignifierTarget
|
||||
{
|
||||
public:
|
||||
virtual bool OverrideSignifierPosition( void ) = 0;
|
||||
virtual bool GetSignifierPosition( const Vector &vSource, Vector &vPositionOut, Vector &vNormalOut ) = 0;
|
||||
virtual bool GetSignifierDesignation( char *lpszBuffer, unsigned int nBufferSize ) = 0;
|
||||
virtual bool UseSelectionGlow( void ) = 0;
|
||||
};
|
||||
|
||||
class CSignifierTarget : public ISignifierTarget
|
||||
{
|
||||
public:
|
||||
virtual bool UseSelectionGlow( void ) { return true; }
|
||||
virtual bool OverrideSignifierPosition( void ) { return false; }
|
||||
virtual bool GetSignifierPosition( const Vector &vSource, Vector &vPositionOut, Vector &vNormalOut ) { return false; }
|
||||
virtual bool GetSignifierDesignation( char *lpszBuffer, unsigned int nBufferSize )
|
||||
{
|
||||
V_memset( lpszBuffer, 0, nBufferSize );
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PLAYER_SOUNDS_CITIZEN = 0,
|
||||
PLAYER_SOUNDS_COMBINESOLDIER,
|
||||
PLAYER_SOUNDS_METROPOLICE,
|
||||
PLAYER_SOUNDS_MAX,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CONCEPT_CHELL_IDLE,
|
||||
CONCEPT_CHELL_DEAD,
|
||||
};
|
||||
|
||||
const float PLAYER_HULL_REDUCTION = 0.70f;
|
||||
|
||||
extern const char *g_pszChellConcepts[];
|
||||
int GetChellConceptIndexFromString( const char *pszConcept );
|
||||
extern ConVar sv_portal_coop_ping_hud_indicitator_duration;
|
||||
|
||||
struct PaintPowerInfo_t;
|
||||
|
||||
const float STEEP_SLOPE = 0.7;
|
||||
char const* const PORTAL_PREDICTED_CONTEXT = "Portal Predicted Powers";
|
||||
|
||||
enum JumpButtonPress
|
||||
{
|
||||
JUMP_ON_TOUCH = 0,
|
||||
PRESS_JUMP_TO_BOUNCE,
|
||||
HOLD_JUMP_TO_BOUNCE,
|
||||
TRAMPOLINE_BOUNCE
|
||||
};
|
||||
|
||||
enum InAirState
|
||||
{
|
||||
ON_GROUND,
|
||||
IN_AIR_JUMPED,
|
||||
IN_AIR_BOUNCED,
|
||||
IN_AIR_FELL
|
||||
};
|
||||
|
||||
enum PaintSurfaceType
|
||||
{
|
||||
FLOOR_SURFACE = 0,
|
||||
WALL_SURFACE,
|
||||
CEILING_SURFACE
|
||||
};
|
||||
|
||||
enum StickCameraState
|
||||
{
|
||||
STICK_CAMERA_SURFACE_TRANSITION = 0,
|
||||
STICK_CAMERA_ROLL_CORRECT,
|
||||
STICK_CAMERA_PORTAL,
|
||||
STICK_CAMERA_WALL_STICK_DEACTIVATE_TRANSITION,
|
||||
STICK_CAMERA_SWITCH_TO_ABS_UP_MODE,
|
||||
STICK_CAMERA_ABS_UP_MODE,
|
||||
STICK_CAMERA_SWITCH_TO_LOCAL_UP,
|
||||
STICK_CAMERA_SWITCH_TO_LOCAL_UP_LOOKING_UP,
|
||||
STICK_CAMERA_LOCAL_UP_LOOKING_UP,
|
||||
STICK_CAMERA_UPRIGHT
|
||||
};
|
||||
|
||||
enum StickCameraCorrectionMethod
|
||||
{
|
||||
QUATERNION_CORRECT = 0,
|
||||
ROTATE_UP,
|
||||
SNAP_UP,
|
||||
DO_NOTHING
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Paint Power Helper Functions
|
||||
//=============================================================================
|
||||
const Vector ComputeBouncePostVelocityNoReflect( const Vector& preVelocity,
|
||||
const Vector& normal,
|
||||
const Vector& up );
|
||||
|
||||
const Vector ComputeBouncePostVelocityReflection( const Vector& preVelocity,
|
||||
const Vector& normal,
|
||||
const Vector& localUp );
|
||||
|
||||
void ExpandAABB( Vector& boxMin, Vector& boxMax, const Vector& sweepVector );
|
||||
|
||||
//=============================================================================
|
||||
// Paint Power Choice
|
||||
//=============================================================================
|
||||
struct PaintPowerChoiceCriteria_t
|
||||
{
|
||||
Vector vNormInputDir;
|
||||
Vector vNormVelocity;
|
||||
bool bInPortal;
|
||||
};
|
||||
|
||||
struct PaintPowerChoiceResult_t
|
||||
{
|
||||
const PaintPowerInfo_t* pPaintPower;
|
||||
float flInputCos;
|
||||
float flVelocityCos;
|
||||
bool bWasIgnored;
|
||||
|
||||
inline void Initialize()
|
||||
{
|
||||
pPaintPower = NULL;
|
||||
flInputCos = 1.0f;
|
||||
flVelocityCos = 2.0f;
|
||||
bWasIgnored = false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef CUtlVectorFixed< PaintPowerChoiceResult_t, PAINT_POWER_TYPE_COUNT_PLUS_NO_POWER > PaintPowerChoiceResultArray;
|
||||
|
||||
struct CachedPaintPowerChoiceResult
|
||||
{
|
||||
Vector surfaceNormal;
|
||||
CBaseHandle surfaceEntity;
|
||||
bool wasValid;
|
||||
bool wasIgnored;
|
||||
|
||||
inline void Initialize()
|
||||
{
|
||||
surfaceNormal = Vector( 0, 0, 0 );
|
||||
surfaceEntity = NULL;
|
||||
wasValid = false;
|
||||
wasIgnored = false;
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Contact Determination (used for determining available paint powers)
|
||||
//=============================================================================
|
||||
const int ALL_CONTENT = 0xFFFFFFFF;
|
||||
|
||||
struct BrushContact
|
||||
{
|
||||
Vector point;
|
||||
Vector normal;
|
||||
CBaseEntity* pBrushEntity;
|
||||
bool isOnThinSurface;
|
||||
|
||||
void Initialize( const Vector& contactPt,
|
||||
const Vector& normal,
|
||||
CBaseEntity* pBrushEntity,
|
||||
bool onThinSurface );
|
||||
void Initialize( const fltx4& contactPt,
|
||||
const fltx4& normal,
|
||||
CBaseEntity* pBrushEntity,
|
||||
bool onThinSurface );
|
||||
};
|
||||
|
||||
typedef CUtlVector<BrushContact> ContactVector;
|
||||
typedef CUtlVector<cplane_t> CollisionPlaneVector;
|
||||
void ComputeAABBContactsWithBrushEntity( ContactVector& contacts, const Vector& boxOrigin, const Vector& boxMin, const Vector& boxMax, CBaseEntity* pBrushEntity, int contentsMask = CONTENTS_BRUSH_PAINT );
|
||||
void ComputeAABBContactsWithBrushEntity( ContactVector& contacts, const cplane_t *pClipPlanes, int iClipPlaneCount, const Vector& boxOrigin, const Vector& boxMin, const Vector& boxMax, CBaseEntity* pBrushEntity, int contentsMask = CONTENTS_BRUSH_PAINT );
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
class CPortal_Player;
|
||||
#else
|
||||
class C_Portal_Player;
|
||||
#define CPortal_Player C_Portal_Player
|
||||
#endif
|
||||
|
||||
void TracePlayerBoxAgainstCollidables( trace_t& trace,
|
||||
const CPortal_Player* player,
|
||||
const Vector& startPos,
|
||||
const Vector& endPos,
|
||||
const Vector& boxLocalMin,
|
||||
const Vector& boxLocalMax );
|
||||
|
||||
struct StringCompare_t
|
||||
{
|
||||
StringCompare_t( char const* str ) : m_str( str ) {}
|
||||
|
||||
char const* const m_str;
|
||||
|
||||
inline bool operator()( char const* str ) const
|
||||
{
|
||||
return V_strcmp( m_str, str ) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#define PERMANENT_CONDITION -1
|
||||
|
||||
// Player conditions for animations
|
||||
enum
|
||||
{
|
||||
PORTAL_COND_TAUNTING = 0,
|
||||
PORTAL_COND_POINTING,
|
||||
PORTAL_COND_DROWNING,
|
||||
PORTAL_COND_DEATH_CRUSH,
|
||||
PORTAL_COND_DEATH_GIB,
|
||||
PORTAL_COND_LAST
|
||||
};
|
||||
|
||||
|
||||
class CPortalPlayerShared
|
||||
{
|
||||
public:
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
friend class C_Portal_Player;
|
||||
typedef C_Portal_Player OuterClass;
|
||||
DECLARE_PREDICTABLE();
|
||||
|
||||
// Server specific.
|
||||
#else
|
||||
|
||||
friend class CPortal_Player;
|
||||
typedef CPortal_Player OuterClass;
|
||||
|
||||
#endif
|
||||
|
||||
DECLARE_EMBEDDED_NETWORKVAR()
|
||||
DECLARE_CLASS_NOBASE( CPortalPlayerShared );
|
||||
|
||||
// Initialization.
|
||||
CPortalPlayerShared();
|
||||
void Init( OuterClass *pOuter );
|
||||
|
||||
// Condition (PORTAL_COND_*)
|
||||
int GetCond() const { return m_nPlayerCond; }
|
||||
void SetCond( int nCond ) { m_nPlayerCond = nCond; }
|
||||
void AddCond( int nCond, float flDuration = PERMANENT_CONDITION );
|
||||
void RemoveCond( int nCond );
|
||||
bool InCond( int nCond );
|
||||
void RemoveAllCond();
|
||||
void OnConditionAdded( int nCond );
|
||||
void OnConditionRemoved( int nCond );
|
||||
void ConditionThink( void );
|
||||
float GetConditionDuration( int nCond );
|
||||
|
||||
void ConditionGameRulesThink( void );
|
||||
void DebugPrintConditions( void );
|
||||
|
||||
bool IsLoadoutUnavailable( void ) { return m_bLoadoutUnavailable; }
|
||||
void SetLoadoutUnavailable( bool bUnavailable ) { m_bLoadoutUnavailable = bUnavailable; }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// This class only receives calls for these from C_TFPlayer, not
|
||||
// natively from the networking system
|
||||
virtual void OnPreDataChanged( void );
|
||||
virtual void OnDataChanged( void );
|
||||
|
||||
// check the newly networked conditions for changes
|
||||
void UpdateConditions( void );
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Vars that are networked.
|
||||
CNetworkVar( int, m_nPlayerCond ); // Player condition flags.
|
||||
CNetworkVar( bool, m_bLoadoutUnavailable );
|
||||
float m_flCondExpireTimeLeft[PORTAL_COND_LAST]; // Time until each condition expires
|
||||
|
||||
// Vars that are not networked.
|
||||
OuterClass *m_pOuter; // C_TFPlayer or CTFPlayer (client/server).
|
||||
|
||||
int m_nOldConditions;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
float m_flNextCritUpdate;
|
||||
// FIXME: CUtlVector<CTFDamageEvent> m_DamageEvents;
|
||||
|
||||
float m_flTauntRemoveTime;
|
||||
|
||||
// store damage info, so we can kill the player with this damage after crush animation is done
|
||||
CTakeDamageInfo m_damageInfo;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct PortalPlayerStatistics_t
|
||||
{
|
||||
DECLARE_CLASS_NOBASE( PortalPlayerStatistics_t );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
#endif
|
||||
|
||||
CNetworkVar( int, iNumPortalsPlaced );
|
||||
CNetworkVar( int, iNumStepsTaken );
|
||||
CNetworkVar( float, fNumSecondsTaken );
|
||||
CNetworkVar( float, fDistanceTaken );
|
||||
};
|
||||
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#define CPortal_Player C_Portal_Player
|
||||
#define CPortalPlayerLocalData C_PortalPlayerLocalData
|
||||
#endif
|
||||
|
||||
|
||||
#endif //PORTAL_PLAYER_SHARED_h
|
||||
@@ -0,0 +1,689 @@
|
||||
//====== Copyright © 1996-2003, 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"
|
||||
#include "movevars_shared.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "C_Portal_Player.h"
|
||||
#include "C_Weapon_Portalgun.h"
|
||||
#include "c_te_effect_dispatch.h"
|
||||
#include "particle_parse.h"
|
||||
#else
|
||||
#include "Portal_Player.h"
|
||||
#include "Weapon_Portalgun.h"
|
||||
#endif
|
||||
|
||||
#define PORTAL_RUN_SPEED 320.0f
|
||||
#define PORTAL_CROUCHWALK_SPEED 110.0f
|
||||
|
||||
ConVar anim_forcedamaged( "anim_forcedamaged", "0", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Force the player to use the secondary damaged animations." );
|
||||
ConVar anim_min_collision_speed_threshold("anim_min_collision_speed_threshold", "195.f", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
m_bLanding = false;
|
||||
m_bWasInTractorBeam = false;
|
||||
m_bFirstTractorBeamFrame = false;
|
||||
m_bBridgeRemovedFromUnder = false;
|
||||
m_bDying = false;
|
||||
|
||||
m_nDamageStage = DAMAGE_STAGE_NONE;
|
||||
|
||||
m_fNextBouncePredictTime = 0.0f;
|
||||
m_fPrevBouncePredict = 4.0f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPortalPlayerAnimState::ClearAnimationState( void )
|
||||
{
|
||||
m_bInAirWalk = false;
|
||||
m_vLastVelocity = vec3_origin;
|
||||
m_bLanding = false;
|
||||
m_bWasInTractorBeam = false;
|
||||
m_bFirstTractorBeamFrame = false;
|
||||
m_bBridgeRemovedFromUnder = false;
|
||||
m_bDying = false;
|
||||
|
||||
m_nDamageStage = DAMAGE_STAGE_NONE;
|
||||
|
||||
BaseClass::ClearAnimationState();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : actDesired -
|
||||
// Output : Activity
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CPortalPlayerAnimState::TranslateActivity( Activity actDesired )
|
||||
{
|
||||
Activity translateActivity = BaseClass::TranslateActivity( actDesired );
|
||||
|
||||
// if injured
|
||||
if ( m_nDamageStage == DAMAGE_STAGE_FINAL )
|
||||
{
|
||||
switch ( translateActivity )
|
||||
{
|
||||
case ACT_MP_STAND_IDLE:
|
||||
{
|
||||
translateActivity = ACT_MP_STAND_SECONDARY;
|
||||
break;
|
||||
}
|
||||
case ACT_MP_RUN:
|
||||
{
|
||||
translateActivity = ACT_MP_RUN_SECONDARY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( GetPortalPlayer()->GetActiveWeapon() )
|
||||
{
|
||||
translateActivity = GetPortalPlayer()->GetActiveWeapon()->ActivityOverride( translateActivity, false );
|
||||
}
|
||||
|
||||
return translateActivity;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : event -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPortalPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
|
||||
{
|
||||
Activity iWeaponActivity = ACT_INVALID;
|
||||
|
||||
#if defined CLIENT_DLL
|
||||
RANDOM_CEG_TEST_SECRET();
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
case PLAYERANIMEVENT_JUMP:
|
||||
{
|
||||
m_bInAirWalk = false;
|
||||
m_bLanding = false;
|
||||
BaseClass::DoAnimationEvent( event, nData );
|
||||
break;
|
||||
}
|
||||
case PLAYERANIMEVENT_DIE:
|
||||
{
|
||||
m_bDying = true;
|
||||
break;
|
||||
}
|
||||
case PLAYERANIMEVENT_FLINCH_CHEST:
|
||||
case PLAYERANIMEVENT_FLINCH_HEAD:
|
||||
case PLAYERANIMEVENT_FLINCH_LEFTARM:
|
||||
case PLAYERANIMEVENT_FLINCH_RIGHTARM:
|
||||
case PLAYERANIMEVENT_FLINCH_LEFTLEG:
|
||||
case PLAYERANIMEVENT_FLINCH_RIGHTLEG:
|
||||
{
|
||||
IncreaseDamageStage();
|
||||
BaseClass::DoAnimationEvent( event, nData );
|
||||
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::Update( float eyeYaw, float eyePitch )
|
||||
{
|
||||
// Profile the animation update.
|
||||
VPROF( "CPortalPlayerAnimState::Update" );
|
||||
|
||||
// Get the player
|
||||
CPortal_Player *pPlayer = GetPortalPlayer();
|
||||
if ( pPlayer == NULL )
|
||||
return;
|
||||
|
||||
// Get the studio header for the player.
|
||||
CStudioHdr *pStudioHdr = pPlayer->GetModelPtr();
|
||||
if ( !pStudioHdr )
|
||||
return;
|
||||
|
||||
// Check to see if we should be updating the animation state - dead, ragdolled?
|
||||
if ( !ShouldUpdateAnimState() )
|
||||
{
|
||||
ClearAnimationState();
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the eye angles.
|
||||
m_flEyeYaw = AngleNormalize( eyeYaw );
|
||||
m_flEyePitch = AngleNormalize( eyePitch );
|
||||
|
||||
// Compute the player sequences.
|
||||
ComputeSequences( pStudioHdr );
|
||||
|
||||
if ( SetupPoseParameters( pStudioHdr ) )
|
||||
{
|
||||
// Pose parameter - what direction are the player's legs running in.
|
||||
ComputePoseParam_MoveYaw( pStudioHdr );
|
||||
|
||||
// Pose parameter - Torso aiming (up/down).
|
||||
ComputePoseParam_AimPitch( pStudioHdr );
|
||||
|
||||
// Pose parameter - Torso aiming (rotation).
|
||||
ComputePoseParam_AimYaw( pStudioHdr );
|
||||
}
|
||||
|
||||
// Store this for collision results
|
||||
GetOuterAbsVelocity( m_vLastVelocity );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CEG_NOINLINE 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 )
|
||||
{
|
||||
#if defined GAME_DLL
|
||||
CEG_PROTECT_MEMBER_FUNCTION( CPortalPlayerAnimState_Teleport );
|
||||
#endif
|
||||
// Snap the yaw pose parameter lerping variables to face new angles.
|
||||
m_flCurrentFeetYaw = m_flGoalFeetYaw = m_flEyeYaw = pPlayer->EyeAngles()[YAW];
|
||||
}
|
||||
}
|
||||
|
||||
void CPortalPlayerAnimState::TransformYAWs( const matrix3x4_t &matTransform )
|
||||
{
|
||||
QAngle qOldAngles = vec3_angle;
|
||||
QAngle qAngles;
|
||||
|
||||
qOldAngles[YAW] = m_flEyeYaw;
|
||||
qAngles = TransformAnglesToWorldSpace( qOldAngles, matTransform );
|
||||
m_flEyeYaw = qAngles[YAW];
|
||||
|
||||
qOldAngles[YAW] = m_flGoalFeetYaw;
|
||||
qAngles = TransformAnglesToWorldSpace( qOldAngles, matTransform );
|
||||
m_flGoalFeetYaw = qAngles[YAW];
|
||||
|
||||
qOldAngles[YAW] = m_flCurrentFeetYaw;
|
||||
qAngles = TransformAnglesToWorldSpace( qOldAngles, matTransform );
|
||||
m_flCurrentFeetYaw = qAngles[YAW];
|
||||
}
|
||||
|
||||
bool CPortalPlayerAnimState::ShouldLongFall( void ) const
|
||||
{
|
||||
CPortal_Player *pPortalPlayer = GetPortalPlayer();
|
||||
|
||||
return ( m_bWasInTractorBeam ||
|
||||
m_bBridgeRemovedFromUnder ||
|
||||
( !pPortalPlayer->GetTractorBeam() &&
|
||||
pPortalPlayer->GetAirTime() > 2.0f &&
|
||||
pPortalPlayer->GetAbsVelocity().AsVector2D().Length() < 450.0f ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *idealActivity -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CPortalPlayerAnimState::HandleMoving( Activity &idealActivity )
|
||||
{
|
||||
float flSpeed = GetOuterXYSpeed();
|
||||
CPortal_Player *pPortalPlayer = GetPortalPlayer();
|
||||
|
||||
// If we're off the ground and not moving, do an airwalk
|
||||
bool bOnGround = ( pPortalPlayer->GetFlags() & FL_ONGROUND );
|
||||
if ( bOnGround == false )
|
||||
{
|
||||
if ( m_bWasInTractorBeam || m_bBridgeRemovedFromUnder )
|
||||
{
|
||||
idealActivity = ACT_MP_LONG_FALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
idealActivity = ACT_MP_AIRWALK;
|
||||
}
|
||||
|
||||
m_bInAirWalk = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CEG_GCV_PRE();
|
||||
static const int CEG_SPEED_POWER = CEG_GET_CONSTANT_VALUE( PaintSpeedPower );
|
||||
CEG_GCV_POST();
|
||||
bool bHasSpeedPower = pPortalPlayer->GetPaintPower( CEG_SPEED_POWER ).m_State == ACTIVE_PAINT_POWER;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
if ( engine->HasPaintmap() && !bHasSpeedPower && !pPortalPlayer->IsLocalPlayer() )
|
||||
{
|
||||
// FIXME: Is this doing extra work in splitscreen?
|
||||
// Non-local players don't update paint powers on the client because this has to happen in gamemovement!
|
||||
// Quickly figure out if speed paint should be active
|
||||
CPortal_Player::PaintPowerInfoVector activePowers;
|
||||
pPortalPlayer->ChooseActivePaintPowers( activePowers );
|
||||
|
||||
PaintPowerConstRange activeRange = GetConstRange( activePowers );
|
||||
for( PaintPowerConstIter i = activeRange.first; i != activeRange.second; ++i )
|
||||
{
|
||||
const PaintPowerInfo_t &newPower = *i;
|
||||
if ( newPower.m_PaintPowerType == CEG_SPEED_POWER )
|
||||
{
|
||||
bHasSpeedPower = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the surface information
|
||||
// NOTE: Calling this after Activating/Using/Deactivating paint powers makes sticky boxes not very sticky
|
||||
// and that's why I moved it back over here. -Brett
|
||||
pPortalPlayer->ClearSurfacePaintPowerInfo();
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( flSpeed > MOVING_MINIMUM_SPEED && bHasSpeedPower )
|
||||
{
|
||||
idealActivity = ACT_MP_RUN_SPEEDPAINT;
|
||||
}
|
||||
else 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 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( idealActivity == ACT_MP_RUN && anim_forcedamaged.GetBool() )
|
||||
{
|
||||
idealActivity = ACT_MP_RUN_SECONDARY;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CPortalPlayerAnimState::CalcMainActivity()
|
||||
{
|
||||
Activity idealActivity = BaseClass::CalcMainActivity();
|
||||
if ( HandleBouncing( idealActivity ) ||
|
||||
HandleLanding() ||
|
||||
HandleTractorBeam( idealActivity ) ||
|
||||
HandleInAir( idealActivity ) )
|
||||
{
|
||||
if ( idealActivity == ACT_MP_DOUBLEJUMP && m_eCurrentMainSequenceActivity != ACT_MP_DOUBLEJUMP )
|
||||
{
|
||||
m_pPlayer->SetCycle( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if (idealActivity == ACT_MP_STAND_IDLE && anim_forcedamaged.GetBool())
|
||||
{
|
||||
idealActivity = ACT_MP_STAND_SECONDARY;
|
||||
}
|
||||
return idealActivity;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *idealActivity -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CPortalPlayerAnimState::HandleDucking( Activity &idealActivity )
|
||||
{
|
||||
if ( ( GetBasePlayer()->GetFlags() & FL_DUCKING ) || 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;
|
||||
}
|
||||
|
||||
|
||||
bool CPortalPlayerAnimState::HandleDying( Activity &idealActivity )
|
||||
{
|
||||
if ( m_bDying )
|
||||
{
|
||||
if ( m_bFirstDyingFrame )
|
||||
{
|
||||
// Reset the animation.
|
||||
RestartMainSequence();
|
||||
m_bFirstDyingFrame = false;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//DispatchParticleEffect( "bot_death_B_gib", GetPortalPlayer()->WorldSpaceCenter(), GetPortalPlayer()->GetAbsAngles(), GetPortalPlayer() );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( GetPortalPlayer()->m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) )
|
||||
{
|
||||
idealActivity = ACT_MP_DEATH_CRUSH;
|
||||
}
|
||||
else
|
||||
{
|
||||
idealActivity = ACT_DIESIMPLE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !m_bFirstDyingFrame )
|
||||
{
|
||||
m_bFirstDyingFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CPortalPlayerAnimState::HandleInAir( Activity &idealActivity )
|
||||
{
|
||||
CPortal_Player *pPortalPlayer = GetPortalPlayer();
|
||||
if ( pPortalPlayer->GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_bWasInTractorBeam || m_bBridgeRemovedFromUnder )
|
||||
{
|
||||
m_bLanding = true;
|
||||
idealActivity = ACT_MP_LONG_FALL;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vecVelocity;
|
||||
GetOuterAbsVelocity( vecVelocity );
|
||||
if ( vecVelocity.z > 300.0f || m_bInAirWalk )
|
||||
{
|
||||
// In an air walk.
|
||||
m_bJumping = false;
|
||||
idealActivity = ACT_MP_AIRWALK;
|
||||
m_bInAirWalk = true;
|
||||
m_bLanding = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ConVar sv_bounce_anim_time_predict( "sv_bounce_anim_time_predict", "0.2", FCVAR_REPLICATED );
|
||||
ConVar sv_bounce_anim_time_continue( "sv_bounce_anim_time_continue", "0.5", FCVAR_REPLICATED );
|
||||
|
||||
|
||||
bool CPortalPlayerAnimState::HandleBouncing( Activity &idealActivity )
|
||||
{
|
||||
CPortal_Player *pPortalPlayer = GetPortalPlayer();
|
||||
float fNextBounceOffsetTime = pPortalPlayer->PredictedBounce();
|
||||
bool bPredictedBounce = fNextBounceOffsetTime < sv_bounce_anim_time_predict.GetFloat();
|
||||
if ( bPredictedBounce || pPortalPlayer->GetPortalPlayerLocalData().m_fBouncedTime + sv_bounce_anim_time_continue.GetFloat() > gpGlobals->curtime )
|
||||
{
|
||||
// They're anticipating to hit a bounce soon
|
||||
if ( bPredictedBounce )
|
||||
{
|
||||
pPortalPlayer->OnBounced( fNextBounceOffsetTime );
|
||||
}
|
||||
|
||||
m_bJumping = true;
|
||||
m_bInAirWalk = true;
|
||||
m_bLanding = true;
|
||||
idealActivity = ACT_MP_DOUBLEJUMP;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CPortalPlayerAnimState::HandleTractorBeam( Activity &idealActivity )
|
||||
{
|
||||
if ( GetPortalPlayer()->GetPortalPlayerLocalData().m_hTractorBeam.Get() )
|
||||
{
|
||||
if ( m_bFirstTractorBeamFrame )
|
||||
{
|
||||
RestartMainSequence();
|
||||
m_bFirstTractorBeamFrame = false;
|
||||
}
|
||||
|
||||
m_bWasInTractorBeam = true;
|
||||
|
||||
idealActivity = ACT_MP_TRACTORBEAM_FLOAT;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !m_bFirstTractorBeamFrame )
|
||||
{
|
||||
RANDOM_CEG_TEST_SECRET_PERIOD( 8, 15 );
|
||||
m_bFirstTractorBeamFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CPortalPlayerAnimState::HandleLanding()
|
||||
{
|
||||
// Check to see if we were in the air and now we are basically on the ground or water.
|
||||
if ( m_bLanding && GetBasePlayer()->GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
m_bJumping = false;
|
||||
m_bInAirWalk = false;
|
||||
m_bLanding = false;
|
||||
m_bWasInTractorBeam = false;
|
||||
m_bBridgeRemovedFromUnder = false;
|
||||
RestartMainSequence();
|
||||
RANDOM_CEG_TEST_SECRET_PERIOD( 91, 172 );
|
||||
RestartGesture( GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CPortalPlayerAnimState::HandleJumping( Activity &idealActivity )
|
||||
{
|
||||
Vector vecVelocity;
|
||||
GetOuterAbsVelocity( vecVelocity );
|
||||
|
||||
// Jumping.
|
||||
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 )
|
||||
{
|
||||
// In an air walk.
|
||||
m_bJumping = false;
|
||||
idealActivity = ACT_MP_AIRWALK;
|
||||
m_bInAirWalk = true;
|
||||
}
|
||||
|
||||
// if we're still jumping
|
||||
if ( m_bJumping )
|
||||
{
|
||||
idealActivity = ACT_MP_JUMP_START;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_bJumping )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CPortalPlayerAnimState::SetupPoseParameters( CStudioHdr *pStudioHdr )
|
||||
{
|
||||
CPortal_Player *pPortalPlayer = ToPortalPlayer( GetBasePlayer() );
|
||||
if ( pPortalPlayer && ( pPortalPlayer->m_Shared.InCond( PORTAL_COND_TAUNTING ) || pPortalPlayer->m_Shared.InCond( PORTAL_COND_DROWNING ) ) )
|
||||
return false;
|
||||
|
||||
return BaseClass::SetupPoseParameters( pStudioHdr );
|
||||
}
|
||||
|
||||
|
||||
void CPortalPlayerAnimState::IncreaseDamageStage()
|
||||
{
|
||||
if ( m_nDamageStage < DAMAGE_STAGE_FINAL )
|
||||
{
|
||||
//Disable this for E3
|
||||
//m_nDamageStage++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
//};
|
||||
|
||||
enum PlayerAnimDamageStage_t
|
||||
{
|
||||
DAMAGE_STAGE_NONE = 0,
|
||||
DAMAGE_STAGE_FINAL = 3
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
// 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 ) const { return m_pPortalPlayer; }
|
||||
|
||||
virtual void ClearAnimationState();
|
||||
|
||||
virtual Activity TranslateActivity( Activity actDesired );
|
||||
virtual bool SetupPoseParameters( CStudioHdr *pStudioHdr );
|
||||
virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 );
|
||||
virtual void Update( float eyeYaw, float eyePitch );
|
||||
virtual Activity CalcMainActivity();
|
||||
|
||||
void Teleport( const Vector *pNewOrigin, const QAngle *pNewAngles, CPortal_Player* pPlayer );
|
||||
|
||||
void TransformYAWs( const matrix3x4_t &matTransform );
|
||||
|
||||
virtual bool ShouldLongFall( void ) const;
|
||||
|
||||
virtual bool HandleMoving( Activity &idealActivity );
|
||||
virtual bool HandleJumping( Activity &idealActivity );
|
||||
virtual bool HandleDucking( Activity &idealActivity );
|
||||
virtual bool HandleDying( Activity &idealActivity );
|
||||
|
||||
void BridgeRemovedFromUnder( void ) { m_bBridgeRemovedFromUnder = true; }
|
||||
|
||||
float m_fNextBouncePredictTime;
|
||||
float m_fPrevBouncePredict;
|
||||
|
||||
private:
|
||||
bool HandleInAir( Activity &idealActivity );
|
||||
bool HandleBouncing( Activity &idealActivity );
|
||||
bool HandleTractorBeam( Activity &idealActivity );
|
||||
bool HandleLanding();
|
||||
|
||||
void IncreaseDamageStage();
|
||||
|
||||
CPortal_Player *m_pPortalPlayer;
|
||||
bool m_bInAirWalk;
|
||||
bool m_bLanding;
|
||||
|
||||
Vector m_vLastVelocity;
|
||||
float m_flHoldDeployedPoseUntilTime;
|
||||
unsigned int m_nDamageStage;
|
||||
|
||||
// tractor beam
|
||||
bool m_bWasInTractorBeam;
|
||||
bool m_bFirstTractorBeamFrame;
|
||||
bool m_bBridgeRemovedFromUnder;
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
@@ -0,0 +1,15 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "portal_shareddefs.h"
|
||||
|
||||
char *g_ppszPortalPassThroughMaterials[] =
|
||||
{
|
||||
"lights/light_orange001",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int g_nPortal2PromoFlags = 0;
|
||||
@@ -0,0 +1,152 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTAL_SHAREDDEFS_H
|
||||
#define PORTAL_SHAREDDEFS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#define PORTAL2_MP_SAVE_FILE "coop_data.txt"
|
||||
#define PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH 64
|
||||
|
||||
#define PORTAL_PLAYER_PREDICTION
|
||||
|
||||
#define PORTAL_HALF_DEPTH 2.0f
|
||||
#define PORTAL_BUMP_FORGIVENESS 2.0f
|
||||
|
||||
enum PortalPlacementResult_t
|
||||
{
|
||||
// Success cases
|
||||
PORTAL_PLACEMENT_SUCCESS, // Placed exactly where shot
|
||||
PORTAL_PLACEMENT_USED_HELPER, // A placement helper positioned this portal
|
||||
PORTAL_PLACEMENT_BUMPED, // Placed but needed to move to fit
|
||||
|
||||
// Fail cases
|
||||
PORTAL_PLACEMENT_CANT_FIT, // No space to fit
|
||||
PORTAL_PLACEMENT_CLEANSER, // Hit a cleanser
|
||||
PORTAL_PLACEMENT_OVERLAP_LINKED, // Hit a linked portal that cannot move
|
||||
PORTAL_PLACEMENT_OVERLAP_PARTNER_PORTAL,// Hit a partner portal that cannot move
|
||||
PORTAL_PLACEMENT_INVALID_VOLUME, // Inside a "no portal" volume
|
||||
PORTAL_PLACEMENT_INVALID_SURFACE, // Hit a "no portal" surface
|
||||
PORTAL_PLACEMENT_PASSTHROUGH_SURFACE, // Error case (portal trace failed to hit anything)
|
||||
};
|
||||
|
||||
#define MIN_FLING_SPEED 300
|
||||
|
||||
#define PORTAL_HIDE_PLAYER_RAGDOLL
|
||||
|
||||
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 PortalPlacedBy_t
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
enum PortalEvent_t
|
||||
{
|
||||
PORTALEVENT_LINKED, // This portal has linked to another portal and opened
|
||||
PORTALEVENT_FIZZLE, // Portal has fizzled
|
||||
PORTALEVENT_MOVED, // Portal has moved its position
|
||||
PORTALEVENT_ENTITY_TELEPORTED_TO, // Entity (player or not) has teleported to this portal
|
||||
PORTALEVENT_ENTITY_TELEPORTED_FROM, // Entity (player or not) has teleported away from this portal
|
||||
PORTALEVENT_PLAYER_TELEPORTED_TO, // Player has teleported to this portal
|
||||
PORTALEVENT_PLAYER_TELEPORTED_FROM, // Player has teleported away from this portal
|
||||
};
|
||||
|
||||
enum TeamTauntState_t
|
||||
{
|
||||
TEAM_TAUNT_NONE,
|
||||
TEAM_TAUNT_NEED_PARTNER,
|
||||
TEAM_TAUNT_HAS_PARTNER,
|
||||
TEAM_TAUNT_SUCCESS,
|
||||
TEAM_TAUNT_STATE_TOTAL
|
||||
};
|
||||
|
||||
enum PortalSurfaceType_t
|
||||
{
|
||||
PORTAL_SURFACE_PAINT = 0,
|
||||
PORTAL_SURFACE_VALID = 1,
|
||||
PORTAL_SURFACE_INVALID
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
P2BOT_INVALID = 0,
|
||||
|
||||
P2BOT_PBODY = 1,
|
||||
P2BOT_ATLAS,
|
||||
|
||||
P2BOT_NUMBOTS,
|
||||
};
|
||||
|
||||
enum GameType_t
|
||||
{
|
||||
GAMETYPE_STANDARD_SP = 0,
|
||||
GAMETYPE_STANDARD_COOP,
|
||||
GAMETYPE_CHALLENGE_SP,
|
||||
GAMETYPE_CHALLENGE_COOP,
|
||||
GAMETYPE_COMMUNITY_SP,
|
||||
GAMETYPE_COMMUNITY_SP_QUICKPLAY,
|
||||
GAMETYPE_COMMUNITY_COOP,
|
||||
GAMETYPE_COMMUNITY_COOP_QUICKPLAY,
|
||||
GAMETYPE_PUZZLEMAKER_SP_EDITING,
|
||||
GAMETYPE_PUZZLEMAKER_SP_PREVIEWING,
|
||||
GAMETYPE_PUZZLEMAKER_COOP_EDITING,
|
||||
GAMETYPE_PUZZLEMAKER_COOP_PREVIEWING
|
||||
};
|
||||
|
||||
#define PORTAL2_PROMO_SKINS ( 1 << 0 )
|
||||
#define PORTAL2_PROMO_HELMETS ( 1 << 1 )
|
||||
#define PORTAL2_PROMO_ANTENNA ( 1 << 2 )
|
||||
|
||||
extern int g_nPortal2PromoFlags;
|
||||
|
||||
|
||||
extern char *g_ppszPortalPassThroughMaterials[];
|
||||
|
||||
|
||||
#define USE_SLOWTIME 0
|
||||
|
||||
|
||||
#endif // PORTAL_SHAREDDEFS_H
|
||||
@@ -0,0 +1,97 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "usermessages.h"
|
||||
#include "shake.h"
|
||||
#include "voice_gamemgr.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( "Tilt", 22 );
|
||||
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( "CloseCaptionDirect", -1 ); // Show a forced 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( "LogoTimeMsg", 4 );
|
||||
usermessages->Register( "AchievementEvent", -1 );
|
||||
usermessages->Register( "UpdateJalopyRadar", -1 );
|
||||
usermessages->Register( "CurrentTimescale", 4 ); // Send one float for the new timescale
|
||||
usermessages->Register( "DesiredTimescale", 13 ); // Send timescale and some blending vars
|
||||
|
||||
//new stuff for portal
|
||||
usermessages->Register( "CreditsPortalMsg", 1 );
|
||||
|
||||
#ifdef PORTAL2
|
||||
usermessages->Register( "InventoryFlash", sizeof( float ) + 1 );
|
||||
usermessages->Register( "IndicatorFlash", sizeof( float ) + 1 );
|
||||
usermessages->Register( "ControlHelperAnimate", 2 );
|
||||
usermessages->Register( "TakePhoto", sizeof( long ) + sizeof( uint8 ) );
|
||||
usermessages->Register( "Flash", sizeof( float ) + sizeof( Vector ) );
|
||||
usermessages->Register( "HudPingIndicator", sizeof( Vector ) );
|
||||
usermessages->Register( "OpenRadialMenu", -1 );
|
||||
usermessages->Register( "AddLocator", -1 );
|
||||
usermessages->Register( "MPMapCompleted", sizeof( char ) + sizeof( char ) );
|
||||
usermessages->Register( "MPMapIncomplete", sizeof( char ) + sizeof( char ) );
|
||||
usermessages->Register( "MPMapCompletedData", -1 );
|
||||
usermessages->Register( "MPTauntEarned", -1 );
|
||||
usermessages->Register( "MPTauntUnlocked", -1 );
|
||||
usermessages->Register( "MPTauntLocked", -1 );
|
||||
usermessages->Register( "MPAllTauntsLocked", -1 );
|
||||
|
||||
// Portal effects
|
||||
usermessages->Register( "PortalFX_Surface", -1 );
|
||||
|
||||
// Paint messages
|
||||
usermessages->Register( "PaintWorld", -1 );
|
||||
usermessages->Register( "PaintEntity", sizeof( long ) + sizeof( uint8 ) + sizeof( Vector ) );
|
||||
usermessages->Register( "ChangePaintColor", sizeof( long ) + sizeof( uint8 ) );
|
||||
usermessages->Register( "PaintBombExplode", sizeof( Vector ) + sizeof( uint8 ) + sizeof( uint8 ) + sizeof( BYTE ) );
|
||||
usermessages->Register( "RemoveAllPaint", 0 );
|
||||
usermessages->Register( "PaintAllSurfaces", sizeof( BYTE ) );
|
||||
usermessages->Register( "RemovePaint", sizeof( long ) );
|
||||
|
||||
usermessages->Register( "StartSurvey", sizeof( long ) );
|
||||
usermessages->Register( "ApplyHitBoxDamageEffect", sizeof( long ) + sizeof( uint8 ) + sizeof( uint8 ) );
|
||||
usermessages->Register( "SetMixLayerTriggerFactor", -1 );
|
||||
usermessages->Register( "TransitionFade", sizeof( float ) );
|
||||
|
||||
usermessages->Register( "ScoreboardTempUpdate", sizeof( long ) + sizeof( long ) );
|
||||
usermessages->Register( "ChallengeModeCheatSession", -1 );
|
||||
usermessages->Register( "ChallengeModeCloseAllUI", -1 );
|
||||
|
||||
// FIXME: Bring this back for DLC2
|
||||
//usermessages->Register( "MPVSGameStart", sizeof( char ) );
|
||||
//usermessages->Register( "MPVSGameOver", sizeof( BYTE ) );
|
||||
//usermessages->Register( "MPVSRoundEnd", sizeof( BYTE ) );
|
||||
#endif // PORTAL2
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,201 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
#include "paint_color_manager.h"
|
||||
|
||||
extern bool g_bBulletPortalTrace;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "client_class.h"
|
||||
#include "tier1/interpolatedvar.h"
|
||||
class CPortalRenderable_FlatBasic;
|
||||
class C_Portal_Base2D;
|
||||
#define CPortal_Base2D C_Portal_Base2D
|
||||
class C_BasePlayer;
|
||||
typedef C_BasePlayer CBasePlayer;
|
||||
#else
|
||||
class CPortal_Base2D;
|
||||
class CBasePlayer;
|
||||
#endif
|
||||
|
||||
//When tracing through portals, a line becomes a discontinuous collection of segments as it travels
|
||||
struct ComplexPortalTrace_t
|
||||
{
|
||||
CPortal_Base2D *pSegmentStartPortal;
|
||||
CPortal_Base2D *pSegmentEndPortal;
|
||||
Vector vNormalizedDelta;
|
||||
trace_t trSegment;
|
||||
};
|
||||
|
||||
#if defined ( CLIENT_DLL )
|
||||
Color UTIL_Portal_Color( int iPortal, int iTeamNumber = 0 );
|
||||
Color UTIL_Portal_Color_Particles( int iPortal, int iTeamNumber = 0 );
|
||||
#endif
|
||||
|
||||
void UTIL_Portal_Trace_Filter( class CTraceFilterSimpleClassnameList *traceFilterPortalShot );
|
||||
|
||||
CPortal_Base2D* UTIL_Portal_FirstAlongRay( const Ray_t &ray, float &fMustBeCloserThan, CPortal_Base2D **pSearchArray, int iSearchArrayCount );
|
||||
CPortal_Base2D* UTIL_Portal_FirstAlongRay( const Ray_t &ray, float &fMustBeCloserThan );
|
||||
|
||||
bool UTIL_Portal_TraceRay_Bullets( const CPortal_Base2D *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true );
|
||||
CPortal_Base2D* UTIL_Portal_TraceRay_Beam( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, float *pfFraction );
|
||||
|
||||
void UTIL_Portal_TraceRay_With( const CPortal_Base2D *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall = true );
|
||||
CPortal_Base2D* 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
|
||||
CPortal_Base2D* 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 CPortal_Base2D *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 CPortal_Base2D *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall = true );
|
||||
|
||||
void UTIL_PortalLinked_TraceRay( const CPortal_Base2D *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 CPortal_Base2D *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall = true );
|
||||
|
||||
abstract_class ICountedPartitionEnumerator : public IPartitionEnumerator
|
||||
{
|
||||
public:
|
||||
virtual int GetCount() const = 0;
|
||||
};
|
||||
|
||||
int UTIL_Portal_EntitiesAlongRayComplex( int *entSegmentIndices, int *segCount, int maxEntities, ComplexPortalTrace_t *pResultSegmentArray, int maxSegments, const Ray_t& ray, ICountedPartitionEnumerator* pEnum, ITraceFilter* pTraceFilter, int fStopTraceContents );
|
||||
|
||||
// tests if a ray's trace hits any portals
|
||||
bool UTIL_DidTraceTouchPortals ( const Ray_t& ray, const trace_t& trace, CPortal_Base2D** pOutLocal = NULL, CPortal_Base2D** 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 );
|
||||
|
||||
//Starts off as a normal trace, but as it hits portals it adds segments up to the limit. Returns number of segments used. Assumes a ray only travels through a portal if the ray's center hits the quad
|
||||
int UTIL_Portal_ComplexTraceRay( const Ray_t &ray, unsigned int mask, ITraceFilter *pTraceFilter, ComplexPortalTrace_t *pResultSegmentArray, int iMaxSegments );
|
||||
|
||||
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, float fHalfWidth, float fHalfHeight, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] );
|
||||
void UTIL_Portal_Triangles( const CPortal_Base2D *pPortal, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] );
|
||||
void UTIL_Portal_AABB( const CPortal_Base2D *pPortal, Vector &vMin, Vector &vMax );
|
||||
|
||||
float UTIL_Portal_DistanceThroughPortal( const CPortal_Base2D *pPortal, const Vector &vPoint1, const Vector &vPoint2 );
|
||||
float UTIL_Portal_DistanceThroughPortalSqr( const CPortal_Base2D *pPortal, const Vector &vPoint1, const Vector &vPoint2 );
|
||||
float UTIL_Portal_ShortestDistance( const Vector &vPoint1, const Vector &vPoint2, CPortal_Base2D **pShortestDistPortal_Out = NULL, bool bRequireStraightLine = false );
|
||||
float UTIL_Portal_ShortestDistanceSqr( const Vector &vPoint1, const Vector &vPoint2, CPortal_Base2D **pShortestDistPortal_Out = NULL, bool bRequireStraightLine = false );
|
||||
|
||||
void UTIL_Portal_VectorToGlobalTransforms( const Vector &vPoint2, CUtlVector< Vector > *utlVecPositions );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// 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 CPortal_Base2D *pPortal );
|
||||
|
||||
bool UTIL_IntersectRayWithPortalOBB( const CPortal_Base2D *pPortal, const Ray_t &ray, trace_t *pTrace );
|
||||
bool UTIL_IntersectRayWithPortalOBBAsAABB( const CPortal_Base2D *pPortal, const Ray_t &ray, trace_t *pTrace );
|
||||
|
||||
bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const Vector &ptPortalCenter, const QAngle &qPortalAngles, float fPortalHalfWidth, float fPortalHalfHeight, float flTolerance = 0.0f );
|
||||
bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const CPortal_Base2D *pPortal, float flTolerance = 0.0f );
|
||||
|
||||
CPortal_Base2D *UTIL_IntersectEntityExtentsWithPortal( const CBaseEntity *pEntity );
|
||||
|
||||
void UTIL_Portal_NDebugOverlay( const Vector &ptPortalCenter, const QAngle &qPortalAngles, float fHalfWidth, float fHalfHeight, int r, int g, int b, int a, bool noDepthTest, float duration );
|
||||
void UTIL_Portal_NDebugOverlay( const CPortal_Base2D *pPortal, int r, int g, int b, int a, bool noDepthTest, float duration );
|
||||
|
||||
bool UTIL_FindClosestPassableSpace_InPortal( const CPortal_Base2D *pPortal, const Vector &vCenter, const Vector &vExtents, const Vector &vIndecisivePush, ITraceFilter *pTraceFilter, unsigned int fMask, unsigned int iIterations, Vector &vCenterOut );
|
||||
bool UTIL_FindClosestPassableSpace_InPortal_CenterMustStayInFront( const CPortal_Base2D *pPortal, const Vector &vCenter, const Vector &vExtents, const Vector &vIndecisivePush, ITraceFilter *pTraceFilter, unsigned int fMask, unsigned int iIterations, Vector &vCenterOut );
|
||||
bool FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask = MASK_SOLID ); //assumes the object is already in a mostly passable space
|
||||
bool UTIL_FindClosestPassableSpace_CenterMustStayInFrontOfPlane( const Vector &vCenter, const Vector &vExtents, const Vector &vIndecisivePush, ITraceFilter *pTraceFilter, unsigned int fMask, unsigned int iIterations, Vector &vCenterOut, const VPlane &stayInFrontOfPlane );
|
||||
|
||||
CPortal_Base2D *UTIL_PointIsOnPortalQuad( const Vector vPoint, float fOnPlaneEpsilon, CPortal_Base2D * const *pPortalsToCheck, int iArraySize );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void UTIL_TransformInterpolatedAngle( CInterpolatedVar< QAngle > &qInterped, matrix3x4_t matTransform, float fUpToTime );
|
||||
void UTIL_TransformInterpolatedPosition( CInterpolatedVar< Vector > &vInterped, const VMatrix& matTransform, float fUpToTime );
|
||||
#endif
|
||||
|
||||
bool UTIL_Portal_EntityIsInPortalHole( const CPortal_Base2D *pPortal, const CBaseEntity *pEntity );
|
||||
|
||||
extern const Vector UTIL_ProjectPointOntoPlane( const Vector& point, const cplane_t& plane );
|
||||
bool UTIL_PointIsNearPortal( const Vector& point, const CPortal_Base2D* pPortal2D, float planeDist, float radiusReduction = 0.0f );
|
||||
|
||||
bool UTIL_IsEntityMovingOrRotating( CBaseEntity* pEntity );
|
||||
|
||||
|
||||
// mainly use for stick camera
|
||||
void UTIL_NormalizedAngleDiff( const QAngle& start, const QAngle& end, QAngle* result );
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
void UTIL_Portal_ComputeMatrix( CPortalRenderable_FlatBasic *pLocalPortal, CPortalRenderable_FlatBasic *pRemotePortal );
|
||||
#else
|
||||
void UTIL_Portal_ComputeMatrix( CPortal_Base2D *pLocalPortal, CPortal_Base2D *pRemotePortal );
|
||||
#endif
|
||||
|
||||
|
||||
// PAINT
|
||||
bool UTIL_IsPaintableSurface( const csurface_t& surface );
|
||||
|
||||
float UTIL_PaintBrushEntity( CBaseEntity* pBrushEntity, const Vector& contactPoint, PaintPowerType power, float flPaintRadius, float flAlphaPercent );
|
||||
PaintPowerType UTIL_Paint_TracePower( CBaseEntity* pBrushEntity, const Vector& contactPoint, const Vector& vContactNormal );
|
||||
|
||||
// output start point and reflect dir
|
||||
bool UTIL_Paint_Reflect( const trace_t& tr, Vector& vStart, Vector& vDir, PaintPowerType reflectPower = REFLECT_POWER );
|
||||
|
||||
|
||||
|
||||
//To extend radius's through portals (for explosions, etc.)
|
||||
struct PortalRadiusExtension_t
|
||||
{
|
||||
CPortal_Base2D *pPortalFrom;
|
||||
CPortal_Base2D *pPortalTo;
|
||||
Vector vecOrigin;
|
||||
QAngle vecAngles;
|
||||
};
|
||||
typedef CUtlVector<PortalRadiusExtension_t> PortalRadiusExtensionVector;
|
||||
void ExtendRadiusThroughPortals( const Vector &vecOrigin, const QAngle &vecAngles, float flRadius, PortalRadiusExtensionVector &portalRadiusExtensions );
|
||||
|
||||
void UTIL_Portal_Laser_Prevent_Tilting( Vector& vDirection );
|
||||
|
||||
class CPolyhedron;
|
||||
class CPhysCollide;
|
||||
void UTIL_DebugOverlay_Polyhedron( const CPolyhedron *pPolyhedron, int red, int green, int blue, bool noDepthTest, float flDuration, const matrix3x4_t *pTransform = NULL );
|
||||
void UTIL_DebugOverlay_CPhysCollide( const CPhysCollide *pCollide, int red, int green, int blue, bool noDepthTest, float flDuration, const matrix3x4_t *pTransform = NULL );
|
||||
|
||||
bool UTIL_IsCollideableIntersectingPhysCollide( ICollideable *pCollideable, const CPhysCollide *pCollide, const Vector &vPhysCollideOrigin, const QAngle &qPhysCollideAngles );
|
||||
|
||||
CBasePlayer* UTIL_OtherPlayer( CBasePlayer const* pPlayer );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CBasePlayer* UTIL_OtherConnectedPlayer( CBasePlayer const* pPlayer );
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
class CBrushEntityList : public IEntityEnumerator
|
||||
{
|
||||
public:
|
||||
virtual bool EnumEntity( IHandleEntity *pHandleEntity );
|
||||
|
||||
CUtlVectorFixedGrowable< CBaseEntity*, 32 > m_BrushEntitiesToPaint;
|
||||
};
|
||||
|
||||
void UTIL_FindBrushEntitiesInSphere( CBrushEntityList& brushEnum, const Vector& vCenter, float flRadius );
|
||||
|
||||
#endif
|
||||
|
||||
#endif //#ifndef PORTAL_UTIL_SHARED_H
|
||||
@@ -0,0 +1,36 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include <KeyValues.h>
|
||||
#include "portal_weapon_parse.h"
|
||||
#include "ammodef.h"
|
||||
|
||||
FileWeaponInfo_t* CreateWeaponInfo()
|
||||
{
|
||||
#ifdef PORTAL2
|
||||
return new FileWeaponInfo_t;
|
||||
#else
|
||||
return new CPortalSWeaponInfo;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
CPortalSWeaponInfo::CPortalSWeaponInfo()
|
||||
{
|
||||
m_iPlayerDamage = 0;
|
||||
}
|
||||
|
||||
|
||||
void CPortalSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
|
||||
{
|
||||
BaseClass::Parse( pKeyValuesData, szWeaponName );
|
||||
|
||||
m_iPlayerDamage = pKeyValuesData->GetInt( "damage", 0 );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,684 @@
|
||||
//========= Copyright © 1996-2006, 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 PropPolyhedronGroup_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_CLONES_ENTITY_ACROSS_PORTAL_FROM_MAIN = (1 << 4), //the entity is not "owned" by the portal, but creates a physics clone across the portal anyway
|
||||
//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,
|
||||
PSPOST_LOCAL_DISPLACEMENT,
|
||||
};
|
||||
|
||||
enum RayInPortalHoleResult_t
|
||||
{
|
||||
RIPHR_NOT_TOUCHING_HOLE = 0,
|
||||
RIPHR_TOUCHING_HOLE_NOT_WALL, //only the hole
|
||||
RIPHR_TOUCHING_HOLE_AND_WALL, //both hole and surrounding wall
|
||||
};
|
||||
|
||||
struct PortalTransformAsAngledPosition_t //a matrix transformation from this portal to the linked portal, stored as vector and angle transforms
|
||||
{
|
||||
Vector ptOriginTransform;
|
||||
QAngle qAngleTransform;
|
||||
|
||||
Vector ptShrinkAlignedOrigin; //when there's a discrepancy between visual surface and collision surface, this is adjusted to compensate in traces
|
||||
};
|
||||
|
||||
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;
|
||||
float fHalfWidth, fHalfHeight;
|
||||
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
|
||||
CPhysCollide *pInvHoleShapeCollideable; //A very thin, but wide wall with the portal hole cut out in the middle. Used to test if traces are fully encapsulated in a portal hole
|
||||
CPhysCollide *pAABBAngleTransformCollideable; //used for player traces so we can slide into the portal gracefully if there's an angular difference such that our transformed AABB is in solid until the center reaches the plane
|
||||
Vector vecCurAABBMins;
|
||||
Vector vecCurAABBMaxs;
|
||||
Vector vCollisionCloneExtents; //how far in each direction (in front of the portal) we clone collision data from the real world.
|
||||
EHANDLE hPortalPlacementParent;
|
||||
bool bParentIsVPhysicsSolidBrush; //VPhysics solid brushes present an interesting collision challenge where their visuals are separated 0.5 inches from their collision
|
||||
PS_PlacementData_t( void )
|
||||
{
|
||||
memset( this, 0, sizeof( PS_PlacementData_t ) );
|
||||
ptCenter.Invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
struct PS_SD_Static_CarvedBrushCollection_t
|
||||
{
|
||||
CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision
|
||||
CPhysCollide *pCollideable;
|
||||
#ifndef CLIENT_DLL
|
||||
IPhysicsObject *pPhysicsObject;
|
||||
PS_SD_Static_CarvedBrushCollection_t() : pCollideable(NULL), pPhysicsObject(NULL) {};
|
||||
#else
|
||||
PS_SD_Static_CarvedBrushCollection_t() : pCollideable(NULL) {};
|
||||
#endif
|
||||
};
|
||||
|
||||
struct PS_SD_Static_BrushSet_t : public PS_SD_Static_CarvedBrushCollection_t
|
||||
{
|
||||
PS_SD_Static_BrushSet_t() : iSolidMask(0) {};
|
||||
int iSolidMask;
|
||||
};
|
||||
|
||||
struct PS_SD_Static_World_Brushes_t
|
||||
{
|
||||
PS_SD_Static_BrushSet_t BrushSets[4];
|
||||
|
||||
PS_SD_Static_World_Brushes_t()
|
||||
{
|
||||
BrushSets[0].iSolidMask = MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE;
|
||||
BrushSets[1].iSolidMask = CONTENTS_GRATE;
|
||||
BrushSets[2].iSolidMask = CONTENTS_PLAYERCLIP;
|
||||
BrushSets[3].iSolidMask = CONTENTS_MONSTERCLIP;
|
||||
}
|
||||
};
|
||||
|
||||
struct PS_SD_Static_World_Displacements_t
|
||||
{
|
||||
CPhysCollide *pCollideable;
|
||||
#ifndef CLIENT_DLL
|
||||
IPhysicsObject *pPhysicsObject;
|
||||
PS_SD_Static_World_Displacements_t() : pCollideable(NULL), pPhysicsObject(NULL) {};
|
||||
#else
|
||||
PS_SD_Static_World_Displacements_t() : pCollideable(NULL) {};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct PS_SD_Static_World_StaticProps_ClippedProp_t
|
||||
{
|
||||
PropPolyhedronGroup_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
|
||||
#ifndef CLIENT_DLL
|
||||
bool bPhysicsExists; //the shortcut to know if physics obects exist for each prop
|
||||
#endif
|
||||
PS_SD_Static_World_StaticProps_t( void ) : bCollisionExists( false )
|
||||
#ifndef CLIENT_DLL
|
||||
, bPhysicsExists( false )
|
||||
#endif
|
||||
{ };
|
||||
};
|
||||
|
||||
struct PS_SD_Static_World_t //stuff in front of the portal
|
||||
{
|
||||
PS_SD_Static_World_Brushes_t Brushes;
|
||||
PS_SD_Static_World_Displacements_t Displacements;
|
||||
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
|
||||
{
|
||||
PS_SD_Static_BrushSet_t BrushSets[4];
|
||||
#if defined( GAME_DLL )
|
||||
PS_SD_Static_CarvedBrushCollection_t Carved_func_clip_vphysics; //physics only, no tracing
|
||||
#endif
|
||||
|
||||
PS_SD_Static_Wall_Local_Brushes_t()
|
||||
{
|
||||
BrushSets[0].iSolidMask = MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE;
|
||||
BrushSets[1].iSolidMask = CONTENTS_GRATE;
|
||||
BrushSets[2].iSolidMask = CONTENTS_PLAYERCLIP;
|
||||
BrushSets[3].iSolidMask = CONTENTS_MONSTERCLIP;
|
||||
}
|
||||
};
|
||||
|
||||
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 *pPhysicsObjects[ARRAYSIZE(((PS_SD_Static_World_Brushes_t *)NULL)->BrushSets)];
|
||||
PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t()
|
||||
{
|
||||
for( int i = 0; i != ARRAYSIZE(pPhysicsObjects); ++i )
|
||||
{
|
||||
pPhysicsObjects[i] = 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;
|
||||
|
||||
CUtlVector<CBaseEntity *> ShouldCloneToRemotePortal; //non-owned entities that we should push a clone for
|
||||
};
|
||||
|
||||
|
||||
struct PS_SD_Dynamic_CarvedEntities_CarvedEntity_t
|
||||
{
|
||||
PropPolyhedronGroup_t UncarvedPolyhedronGroup;
|
||||
PropPolyhedronGroup_t CarvedPolyhedronGroup;
|
||||
CPhysCollide * pCollide;
|
||||
#ifndef CLIENT_DLL
|
||||
IPhysicsObject * pPhysicsObject;
|
||||
#endif
|
||||
CBaseEntity * pSourceEntity;
|
||||
};
|
||||
|
||||
struct PS_SD_Dynamic_CarvedEntities_t
|
||||
{
|
||||
bool bCollisionExists; //the shortcut to know if collideables exist for each entity
|
||||
#ifndef CLIENT_DLL
|
||||
bool bPhysicsExists; //the shortcut to know if physics obects exist for each entity
|
||||
#endif
|
||||
CUtlVector<CPolyhedron *> Polyhedrons;
|
||||
CUtlVector<PS_SD_Dynamic_CarvedEntities_CarvedEntity_t> CarvedRepresentations;
|
||||
|
||||
PS_SD_Dynamic_CarvedEntities_t( void ) : bCollisionExists( false )
|
||||
#ifndef CLIENT_DLL
|
||||
, bPhysicsExists( false )
|
||||
#endif
|
||||
{ };
|
||||
};
|
||||
|
||||
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
|
||||
CUtlVector<CBaseEntity *> OwnedEntities;
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
PS_SD_Dynamic_PhysicsShadowClones_t ShadowClones;
|
||||
#endif
|
||||
|
||||
uint32 HasCarvedVersionOfEntity[(MAX_EDICTS + (sizeof(uint32) * 8) - 1)/(sizeof(uint32) * 8)]; //a bit for every possible ent index rounded up to the next integer, not stored as a PortalSimulationEntityFlags_t because those are all serverside at the moment
|
||||
|
||||
PS_SD_Dynamic_CarvedEntities_t CarvedEntities;
|
||||
|
||||
PS_SD_Dynamic_t()
|
||||
{
|
||||
memset( EntFlags, 0, sizeof( EntFlags ) );
|
||||
memset( HasCarvedVersionOfEntity, 0, sizeof( HasCarvedVersionOfEntity ) );
|
||||
}
|
||||
};
|
||||
|
||||
class CPortalSimulator;
|
||||
|
||||
class CPSCollisionEntity : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CPSCollisionEntity, CBaseEntity );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_SERVERCLASS();
|
||||
#else
|
||||
DECLARE_CLIENTCLASS();
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
private:
|
||||
CPortalSimulator *m_pOwningSimulator;
|
||||
#endif
|
||||
|
||||
public:
|
||||
CPSCollisionEntity( void );
|
||||
virtual ~CPSCollisionEntity( void );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Activate( void );
|
||||
virtual int ObjectCaps( void );
|
||||
virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax );
|
||||
virtual void UpdateOnRemove( void );
|
||||
virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const;
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) {}
|
||||
virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ) {}
|
||||
virtual int UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
#else
|
||||
virtual void UpdatePartitionListEntry(); //make this trigger touchable on the client
|
||||
#endif
|
||||
|
||||
static bool IsPortalSimulatorCollisionEntity( const CBaseEntity *pEntity );
|
||||
friend class CPortalSimulator;
|
||||
};
|
||||
|
||||
struct PS_SimulationData_t //compartmentalized data for coherent management
|
||||
{
|
||||
DECLARE_CLASS_NOBASE( PS_SimulationData_t );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
PS_SD_Static_t Static;
|
||||
|
||||
PS_SD_Dynamic_t Dynamic;
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
IPhysicsEnvironment *pPhysicsEnvironment;
|
||||
CNetworkHandle( CPSCollisionEntity, hCollisionEntity );
|
||||
|
||||
PS_SimulationData_t() : pPhysicsEnvironment(NULL){ hCollisionEntity = NULL; }
|
||||
#else
|
||||
typedef CHandle<CPSCollisionEntity> CollisionEntityHandle_t;
|
||||
CollisionEntityHandle_t hCollisionEntity; //the entity we'll be tying physics objects to for collision
|
||||
|
||||
PS_SimulationData_t() : hCollisionEntity(NULL) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct PS_DebuggingData_t
|
||||
{
|
||||
Color overlayColor; //a good base color to use when showing overlays
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
EXTERN_SEND_TABLE( DT_PS_SimulationData_t );
|
||||
#else
|
||||
EXTERN_RECV_TABLE( DT_PS_SimulationData_t );
|
||||
#endif
|
||||
|
||||
struct PS_InternalData_t
|
||||
{
|
||||
DECLARE_CLASS_NOBASE( PS_InternalData_t );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
PS_PlacementData_t Placement;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CNetworkVarEmbedded( PS_SimulationData_t, Simulation);
|
||||
#else
|
||||
PS_SimulationData_t Simulation;
|
||||
#endif
|
||||
|
||||
PS_DebuggingData_t Debugging;
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
EXTERN_SEND_TABLE( DT_PS_InternalData_t );
|
||||
#else
|
||||
EXTERN_RECV_TABLE( DT_PS_InternalData_t );
|
||||
#endif
|
||||
|
||||
|
||||
class CPortalSimulator
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS_NOBASE( CPortalSimulator );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
public:
|
||||
CPortalSimulator( void );
|
||||
~CPortalSimulator( void );
|
||||
|
||||
void SetSize( float fHalfWidth, float fHalfHeight );
|
||||
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;
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
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
|
||||
#endif
|
||||
|
||||
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, bool bUseCollisionAABB ) 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
|
||||
|
||||
RayInPortalHoleResult_t IsRayInPortalHole( 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 *GetSimulatorThatCreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType = NULL );
|
||||
static void Pre_UTIL_Remove( CBaseEntity *pEntity );
|
||||
static void Post_UTIL_Remove( CBaseEntity *pEntity );
|
||||
|
||||
//void TeleportEntityToLinkedPortal( CBaseEntity *pEntity );
|
||||
void StartCloningEntityFromMain( CBaseEntity *pEntity );
|
||||
void StopCloningEntityFromMain( CBaseEntity *pEntity );
|
||||
|
||||
//these 2 only apply for entities that this simulator will not take ownership of
|
||||
void StartCloningEntityAcrossPortals( CBaseEntity *pEntity );
|
||||
void StopCloningEntityAcrossPortals( CBaseEntity *pEntity );
|
||||
|
||||
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
|
||||
|
||||
//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
|
||||
|
||||
bool OwnsEntity( const CBaseEntity *pEntity ) const;
|
||||
|
||||
static CPortalSimulator *GetSimulatorThatOwnsEntity( const CBaseEntity *pEntity ); //fairly cheap to call
|
||||
|
||||
|
||||
bool IsEntityCarvedByPortal( int iEntIndex ) const;
|
||||
inline bool IsEntityCarvedByPortal( CBaseEntity *pEntity ) const { return IsEntityCarvedByPortal( pEntity->entindex() ); };
|
||||
CPhysCollide * GetCollideForCarvedEntity( CBaseEntity *pEntity ) const;
|
||||
|
||||
#ifdef PORTAL_SIMULATORS_EMBED_GUID
|
||||
int GetPortalSimulatorGUID( void ) const { return m_iPortalSimulatorGUID; };
|
||||
#endif
|
||||
|
||||
void SetCarvedParent( CBaseEntity *pPortalPlacementParent ); //sometimes you have to carve up a func brush the portal was placed on
|
||||
|
||||
void DebugCollisionOverlay( bool noDepthTest, float flDuration ) const;
|
||||
|
||||
protected:
|
||||
void MovedOrResized( const Vector &ptCenter, const QAngle &qAngles, float fHalfWidth, float fHalfHeight ); //MoveTo() and SetSize() funnel here to create geometry
|
||||
void AddCarvedEntity( CBaseEntity *pEntity ); //finds/adds an entity that we should carve with the portal hole
|
||||
void ReleaseCarvedEntity( CBaseEntity *pEntity ); //finds and removes a carved entity
|
||||
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 CreateTubePolyhedrons( void ); //Sometimes we have to shift the portal tube helper collideable around a bit
|
||||
|
||||
void UpdateLinkMatrix( void );
|
||||
|
||||
void MarkAsOwned( CBaseEntity *pEntity );
|
||||
void MarkAsReleased( CBaseEntity *pEntity );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CNetworkVarEmbedded( PS_InternalData_t, m_InternalData );
|
||||
#else
|
||||
PS_InternalData_t m_InternalData;
|
||||
#endif
|
||||
|
||||
public:
|
||||
inline const PS_InternalData_t &GetInternalData() const;
|
||||
PS_DebuggingData_t &EditDebuggingData();
|
||||
|
||||
friend class CPS_AutoGameSys_EntityListener;
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
EXTERN_SEND_TABLE( DT_PortalSimulator );
|
||||
#else
|
||||
EXTERN_RECV_TABLE( DT_PortalSimulator );
|
||||
#endif
|
||||
|
||||
|
||||
extern CUtlVector<CPortalSimulator *> const &g_PortalSimulators;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline bool CPortalSimulator::OwnsEntity( const CBaseEntity *pEntity ) const
|
||||
{
|
||||
return ((m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_OWNS_ENTITY) != 0);
|
||||
}
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
inline bool CPortalSimulator::IsSimulatingVPhysics( void ) const
|
||||
{
|
||||
return m_bSimulateVPhysics && m_bGenerateCollision;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool CPortalSimulator::IsCollisionGenerationEnabled( void ) const
|
||||
{
|
||||
return m_bGenerateCollision;
|
||||
}
|
||||
|
||||
inline CPortalSimulator *CPortalSimulator::GetLinkedPortalSimulator( void ) const
|
||||
{
|
||||
return m_pLinkedPortal;
|
||||
}
|
||||
|
||||
inline const PS_InternalData_t &CPortalSimulator::GetInternalData() const
|
||||
{
|
||||
return m_InternalData;
|
||||
}
|
||||
|
||||
inline PS_DebuggingData_t &CPortalSimulator::EditDebuggingData()
|
||||
{
|
||||
return m_InternalData.Debugging;
|
||||
}
|
||||
#if defined ( GAME_DLL )
|
||||
struct VPhysicsClipEntry_t
|
||||
{
|
||||
EHANDLE hEnt;
|
||||
Vector vAABBMins;
|
||||
Vector vAABBMaxs;
|
||||
};
|
||||
CUtlVector<VPhysicsClipEntry_t>& GetVPhysicsClipList ( void );
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
#endif //#ifndef PORTALSIMULATION_H
|
||||
|
||||
@@ -0,0 +1,577 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
//===========================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include "paint_power_user.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_projectedwallentity.h"
|
||||
#include "c_portal_player.h"
|
||||
#include "c_physicsprop.h"
|
||||
#define CPhysicsProp C_PhysicsProp
|
||||
#else
|
||||
#include "projectedwallentity.h"
|
||||
#include "portal_player.h"
|
||||
#include "props.h" // for CPhysicsProp def
|
||||
|
||||
extern void WallPainted( int colorIndex, int nSegment, CBaseEntity *pWall );
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
ConVar wall_debug_time("wall_debug_time", "5.f");
|
||||
ConVar wall_debug("wall_debug", "0");
|
||||
#endif
|
||||
|
||||
ConVar debug_paintable_projected_wall("debug_paintable_projected_wall", "0", FCVAR_REPLICATED);
|
||||
ConVar sv_thinnerprojectedwalls( "sv_thinnerprojectedwalls", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
|
||||
void CProjectedWallEntity::Touch( CBaseEntity* pOther )
|
||||
{
|
||||
//Check if the touched entity is a paint power user
|
||||
IPaintPowerUser* pPowerUser = dynamic_cast< IPaintPowerUser* >( pOther );
|
||||
if( engine->HasPaintmap() && pPowerUser )
|
||||
{
|
||||
//Get the up vector of the wall
|
||||
Vector vecWallUp;
|
||||
#if defined( GAME_DLL )
|
||||
AngleVectors( GetLocalAngles(), NULL, NULL, &vecWallUp );
|
||||
#else
|
||||
AngleVectors( GetNetworkAngles(), NULL, NULL, &vecWallUp );
|
||||
#endif
|
||||
|
||||
const trace_t& trace = BaseClass::GetTouchTrace();
|
||||
float flDot = DotProduct( vecWallUp, trace.plane.normal );
|
||||
|
||||
//Get the segment of the wall that the power user touched
|
||||
Vector vecWorldSpaceCenter = pOther->WorldSpaceCenter();
|
||||
|
||||
Vector vecTouchPoint = UTIL_ProjectPointOntoPlane( vecWorldSpaceCenter, trace.plane );
|
||||
|
||||
const int nSegment = ComputeSegmentIndex( vecTouchPoint );
|
||||
|
||||
if( nSegment >= m_nNumSegments )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the paint power at the current segment
|
||||
PaintPowerType power = m_PaintPowers[nSegment];
|
||||
|
||||
if( debug_paintable_projected_wall.GetBool() )
|
||||
{
|
||||
DevMsg( "Segment: %d, Power: %d\n", nSegment, power );
|
||||
}
|
||||
|
||||
// We dont want to give power to the user if they're touching the side of a projected wall
|
||||
if( !CloseEnough( flDot, 0.0f ) )
|
||||
{
|
||||
pPowerUser->AddSurfacePaintPowerInfo( PaintPowerInfo_t( trace.plane.normal,
|
||||
trace.endpos,
|
||||
this,
|
||||
power ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CProjectedWallEntity::ComputeSegmentIndex( const Vector& vWorldPositionOnWall ) const
|
||||
{
|
||||
const Vector& startPoint = m_vecStartPoint;
|
||||
const Vector& endPoint = m_vecEndPoint;
|
||||
|
||||
const Vector wallVector = endPoint - startPoint;
|
||||
const Vector contactOffset = vWorldPositionOnWall - startPoint;
|
||||
|
||||
const float distance = DotProduct( wallVector, contactOffset ) / m_flLength;
|
||||
Assert( distance + 0.5f > 0.0f && distance < m_flLength + 0.5f );
|
||||
return clamp( distance / m_flSegmentLength, 0, m_nNumSegments - 1 );
|
||||
}
|
||||
|
||||
|
||||
PaintPowerType CProjectedWallEntity::GetPaintPowerAtPoint( const Vector& worldContactPt ) const
|
||||
{
|
||||
return m_PaintPowers[ComputeSegmentIndex(worldContactPt)];
|
||||
}
|
||||
|
||||
|
||||
void CProjectedWallEntity::Paint( PaintPowerType type, const Vector& worldContactPt )
|
||||
{
|
||||
const int nSegment = ComputeSegmentIndex( worldContactPt );
|
||||
if( nSegment < m_PaintPowers.Count() )
|
||||
{
|
||||
m_PaintPowers[nSegment] = type;
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
//Send the event to the client
|
||||
WallPainted( type, nSegment, this );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CProjectedWallEntity::CleansePaint()
|
||||
{
|
||||
for( int i = 0; i < m_nNumSegments; ++i )
|
||||
{
|
||||
// come back to this - MTW
|
||||
/*
|
||||
#if defined( CLIENT_DLL )
|
||||
if ( m_PaintPowers[i] != NO_POWER && m_PaintParticles[i] && m_PaintParticles[i]->IsValid() )
|
||||
{
|
||||
ParticleProp()->StopEmissionAndDestroyImmediately( m_PaintParticles[i] );
|
||||
}
|
||||
m_PaintParticles[i] = NULL;
|
||||
#endif
|
||||
*/
|
||||
|
||||
m_PaintPowers[i] = NO_POWER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CProjectorCollideList : public IEntityEnumerator
|
||||
{
|
||||
public:
|
||||
CProjectorCollideList( Ray_t *pRay, CProjectedWallEntity* pIgnoreEntity, int nContentsMask ) :
|
||||
m_Entities( 0, 32 ), m_pIgnoreEntity( pIgnoreEntity ),
|
||||
m_nContentsMask( nContentsMask ), m_pRay(pRay) {}
|
||||
|
||||
virtual bool EnumEntity( IHandleEntity *pHandleEntity )
|
||||
{
|
||||
// Don't bother with the ignore entity.
|
||||
if ( pHandleEntity == m_pIgnoreEntity )
|
||||
return true;
|
||||
|
||||
Assert( pHandleEntity );
|
||||
if ( !pHandleEntity )
|
||||
return true;
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
|
||||
#else
|
||||
CBaseEntity *pEntity = C_BaseEntity::Instance( pHandleEntity->GetRefEHandle() );
|
||||
#endif
|
||||
|
||||
Assert( pEntity );
|
||||
if ( !pEntity )
|
||||
return true;
|
||||
|
||||
// Only interested in physics objects, the player and turret npcs
|
||||
if ( pEntity->IsPlayer() ||
|
||||
dynamic_cast<CPhysicsProp*>( pEntity ) )
|
||||
{
|
||||
Ray_t ray;
|
||||
ray.Init( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs() ) ;
|
||||
trace_t tr;
|
||||
m_pIgnoreEntity->TestCollision( ray, MASK_SHOT, tr );
|
||||
|
||||
// add if their AABB is overlapping the collideable
|
||||
if ( tr.DidHit() )
|
||||
{
|
||||
m_Entities.AddToTail( pEntity );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CUtlVector<CBaseEntity*> m_Entities;
|
||||
|
||||
private:
|
||||
CProjectedWallEntity *m_pIgnoreEntity;
|
||||
int m_nContentsMask;
|
||||
Ray_t *m_pRay;
|
||||
};
|
||||
|
||||
void CProjectedWallEntity::DisplaceObstructingEntity( CBaseEntity *pEntity, bool bIgnoreStuck )
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
Vector vOrigin = GetLocalOrigin();
|
||||
#else
|
||||
Vector vOrigin = GetNetworkOrigin();
|
||||
#endif
|
||||
|
||||
Vector vWallForward, vWallRight, vWallUp;
|
||||
GetVectors( &vWallForward, &vWallRight, &vWallUp );
|
||||
|
||||
Ray_t ray;
|
||||
Vector vLength = GetLengthVector();
|
||||
Vector vWallSweptBoxMins, vWallSweptBoxMaxs;
|
||||
GetExtents( vWallSweptBoxMins, vWallSweptBoxMaxs );
|
||||
ray.Init( vOrigin, vOrigin + vWallForward*vLength.Length(), vWallSweptBoxMins, vWallSweptBoxMaxs );
|
||||
|
||||
CTraceFilterOnlyHitThis filter( pEntity );
|
||||
trace_t tr;
|
||||
UTIL_TraceRay( ray, MASK_ALL, &filter, &tr );
|
||||
|
||||
if ( tr.DidHit() )
|
||||
{
|
||||
DisplaceObstructingEntity( pEntity, vOrigin, vWallUp, vWallRight, bIgnoreStuck );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Attempts to smooth over the frequent physics-stuck properties of the wall.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProjectedWallEntity::DisplaceObstructingEntities( void )
|
||||
{
|
||||
// Walls size needs to be set up before we test for obstructing objects
|
||||
Assert ( !VectorsAreEqual( m_vWorldSpace_WallMins, m_vWorldSpace_WallMaxs ) );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
Vector vOrigin = GetLocalOrigin();
|
||||
#else
|
||||
Vector vOrigin = GetNetworkOrigin();
|
||||
#endif
|
||||
|
||||
Vector vWallForward, vWallRight, vWallUp;
|
||||
GetVectors( &vWallForward, &vWallRight, &vWallUp );
|
||||
|
||||
Ray_t ray;
|
||||
Vector vLength = GetLengthVector();
|
||||
Vector vWallSweptBoxMins, vWallSweptBoxMaxs;
|
||||
GetExtents( vWallSweptBoxMins, vWallSweptBoxMaxs );
|
||||
ray.Init( vOrigin, vOrigin + vWallForward*vLength.Length(), vWallSweptBoxMins, vWallSweptBoxMaxs );
|
||||
|
||||
CProjectorCollideList enumerator( &ray, this, MASK_SHOT );
|
||||
enginetrace->EnumerateEntities( ray, false, &enumerator );
|
||||
|
||||
for( int iEntity = enumerator.m_Entities.Count(); --iEntity >= 0; )
|
||||
{
|
||||
CBaseEntity *pEntity = enumerator.m_Entities[iEntity];
|
||||
|
||||
DisplaceObstructingEntity( pEntity, vOrigin, vWallUp, vWallRight, false );
|
||||
}
|
||||
}
|
||||
|
||||
CEG_NOINLINE void CProjectedWallEntity::DisplaceObstructingEntity( CBaseEntity *pEntity, const Vector &vOrigin, const Vector &vWallUp, const Vector &vWallRight, bool bIgnoreStuck )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( !pEntity->GetPredictable() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
Vector vObstructionMaxs = pEntity->CollisionProp()->OBBMins();
|
||||
Vector vObstructionMins = pEntity->CollisionProp()->OBBMaxs();
|
||||
|
||||
Vector vNewPos = pEntity->GetAbsOrigin();
|
||||
QAngle vNewAngles = pEntity->GetAbsAngles();
|
||||
Vector vNewVel = pEntity->GetAbsVelocity();
|
||||
|
||||
// TODO:
|
||||
// - get 8 corner points out of the OBB
|
||||
// - find max distances PointVSPlane for both sides of the plane
|
||||
// - use the least distance of the two maxs as distance to push off the entity in the direction of normal of the greater max
|
||||
Vector vEntForward, vEntRight, vEntUp;
|
||||
AngleVectors( pEntity->CollisionProp()->GetCollisionAngles(), &vEntForward, &vEntRight, &vEntUp );
|
||||
|
||||
Vector ptOBBCenter = pEntity->CollisionProp()->GetCollisionOrigin() + pEntity->CollisionProp()->OBBCenter();
|
||||
Vector vExtents = ( vObstructionMaxs - vObstructionMins ) * 0.5f;
|
||||
vEntForward *= vExtents.x;
|
||||
vEntRight *= vExtents.y;
|
||||
vEntUp *= vExtents.z;
|
||||
|
||||
Vector ptOBB[8];
|
||||
ptOBB[0] = ptOBBCenter - vEntForward - vEntRight - vEntUp;
|
||||
ptOBB[1] = ptOBBCenter - vEntForward - vEntRight + vEntUp;
|
||||
ptOBB[2] = ptOBBCenter - vEntForward + vEntRight + vEntUp;
|
||||
ptOBB[3] = ptOBBCenter - vEntForward + vEntRight - vEntUp;
|
||||
ptOBB[4] = ptOBBCenter + vEntForward - vEntRight - vEntUp;
|
||||
ptOBB[5] = ptOBBCenter + vEntForward - vEntRight + vEntUp;
|
||||
ptOBB[6] = ptOBBCenter + vEntForward + vEntRight + vEntUp;
|
||||
ptOBB[7] = ptOBBCenter + vEntForward + vEntRight - vEntUp;
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::Sphere( ptOBBCenter, 5, 255, 0, 0, true, wall_debug_time.GetFloat() );
|
||||
for ( int i=0; i<8; ++i )
|
||||
{
|
||||
NDebugOverlay::Sphere( ptOBB[i], 2, 255, 0, 0, true, wall_debug_time.GetFloat() );
|
||||
}
|
||||
|
||||
NDebugOverlay::VertArrow( GetAbsOrigin(), GetAbsOrigin() + 50.f*vWallUp, 2, 255, 0, 0, 128, true, wall_debug_time.GetFloat() );
|
||||
}
|
||||
CEG_PROTECT_MEMBER_FUNCTION( CProjectedWallEntity_DisplaceObstructingEntity );
|
||||
#endif
|
||||
|
||||
VPlane plWallPlane( vWallUp, DotProduct( vWallUp, vOrigin ) );
|
||||
float flFrontMax = 0.f;
|
||||
float flBackMax = 0.f;
|
||||
Vector vFrontMaxPos, vBackMaxPos;
|
||||
for ( int i=0; i<8; ++i )
|
||||
{
|
||||
float flDistToPlane = fabsf( plWallPlane.DistTo( ptOBB[i] ) );
|
||||
if ( plWallPlane.GetPointSide( ptOBB[i] ) == SIDE_FRONT )
|
||||
{
|
||||
if ( flDistToPlane > flFrontMax )
|
||||
{
|
||||
flFrontMax = flDistToPlane;
|
||||
vFrontMaxPos = ptOBB[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( flDistToPlane > flBackMax )
|
||||
{
|
||||
flBackMax = flDistToPlane;
|
||||
vBackMaxPos = ptOBB[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// always try to push the entity up or down along Z-axis if the wall is horizontal (walkable)
|
||||
// else push the entity to the side of the bridge in the direction of bridge UP vector projected onto XY-plane
|
||||
float flHalfWallWidth = m_flWidth / 2.f;
|
||||
Vector side1 = vOrigin + flHalfWallWidth * vWallRight;
|
||||
Vector side2 = vOrigin - flHalfWallWidth * vWallRight;
|
||||
|
||||
Vector vBumpAxis;
|
||||
float flBumpAmount;
|
||||
float flInvBumpAmount;
|
||||
if ( m_bIsHorizontal )
|
||||
{
|
||||
vBumpAxis = Vector( 0, 0, 1 );
|
||||
|
||||
// compute the bump amount
|
||||
float flDot = fabs( clamp( DotProduct( vWallUp, vBumpAxis ), -1.f, 1.f ) );
|
||||
Assert( flDot != 0.0f );
|
||||
flBumpAmount = MIN( flBackMax / flDot, MAX( fabs( DotProduct( side1 - vBackMaxPos, vBumpAxis ) ), fabs( DotProduct( side2 - vBackMaxPos, vBumpAxis ) ) ) );
|
||||
flInvBumpAmount = MIN( flFrontMax / flDot, MAX( fabs( DotProduct( side1 - vFrontMaxPos, vBumpAxis ) ), fabs( DotProduct( side2 - vFrontMaxPos, vBumpAxis ) ) ) );
|
||||
|
||||
if ( vWallUp.z < 0.f )
|
||||
{
|
||||
V_swap( flBumpAmount, flInvBumpAmount );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vBumpAxis = Vector( vWallUp.x, vWallUp.y, 0.f );
|
||||
VectorNormalize( vBumpAxis );
|
||||
|
||||
// compute the bump amount
|
||||
float flDot = fabs( clamp( DotProduct( vWallUp, vBumpAxis ), -1.f, 1.f ) );
|
||||
Assert( flDot != 0.0f );
|
||||
flBumpAmount = MIN( flBackMax / flDot, MAX( fabs( DotProduct( side1 - vBackMaxPos, vBumpAxis ) ), fabs( DotProduct( side2 - vBackMaxPos, vBumpAxis ) ) ) );
|
||||
flInvBumpAmount = MIN( flFrontMax / flDot, MAX( fabs( DotProduct( side1 - vFrontMaxPos, vBumpAxis ) ), fabs( DotProduct( side2 - vFrontMaxPos, vBumpAxis ) ) ) );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
// front side push
|
||||
NDebugOverlay::VertArrow( vFrontMaxPos - flFrontMax * vWallUp, vFrontMaxPos, 2, 255, 0, 0, 255, true, wall_debug_time.GetFloat() );
|
||||
NDebugOverlay::VertArrow( vFrontMaxPos, vFrontMaxPos - flInvBumpAmount * vBumpAxis, 2, 255, 0, 0, 255, true, wall_debug_time.GetFloat() );
|
||||
|
||||
// back side push
|
||||
NDebugOverlay::VertArrow( vBackMaxPos + flBackMax * vWallUp, vBackMaxPos, 2, 0, 0, 255, 255, true, wall_debug_time.GetFloat() );
|
||||
NDebugOverlay::VertArrow( vBackMaxPos, vBackMaxPos + flBumpAmount * vBumpAxis, 2, 0, 0, 255, 255, true, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
// push in the negative direction of the projected normal
|
||||
if ( flFrontMax < flBackMax )
|
||||
{
|
||||
VectorNegate( vBumpAxis );
|
||||
V_swap( flBumpAmount, flInvBumpAmount );
|
||||
}
|
||||
}
|
||||
|
||||
// add epsilon
|
||||
flBumpAmount += 0.1f;
|
||||
flInvBumpAmount += 0.1f;
|
||||
|
||||
vNewPos += flBumpAmount * vBumpAxis;
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
Vector vPosOffset = vNewPos - pEntity->GetAbsOrigin();
|
||||
NDebugOverlay::Sphere( ptOBBCenter + vPosOffset, 5, 0, 0, 255, true, wall_debug_time.GetFloat() );
|
||||
for ( int i=0; i<8; ++i )
|
||||
{
|
||||
NDebugOverlay::Sphere( ptOBB[i] + vPosOffset, 2, 0, 0, 255, true, wall_debug_time.GetFloat() );
|
||||
}
|
||||
|
||||
NDebugOverlay::BoxAngles( pEntity->GetAbsOrigin() , vObstructionMins, vObstructionMaxs, pEntity->CollisionProp()->GetCollisionAngles(), 0, 255, 255, 64, wall_debug_time.GetFloat() );
|
||||
NDebugOverlay::BoxAngles( vNewPos , vObstructionMins, vObstructionMaxs, pEntity->CollisionProp()->GetCollisionAngles(), 255, 255, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
// check if the entity gets stuck at the new pos
|
||||
CTraceFilterSimple filter( pEntity, COLLISION_GROUP_NONE );
|
||||
trace_t stuckTrace;
|
||||
enginetrace->SweepCollideable( pEntity->GetCollideable(), vNewPos, vNewPos, vNewAngles, MASK_SOLID, &filter, &stuckTrace );
|
||||
|
||||
// If safe, teleport. Otherwise, we're better off being stuck by the wall.
|
||||
if ( !stuckTrace.startsolid || bIgnoreStuck )
|
||||
{
|
||||
//EASY_DIFFPRINT( this, "CProjectedWallEntity::DisplaceObstructingEntities() teleport up" );
|
||||
// TODO: Some smoothing of the player's view or effect when this happens?
|
||||
|
||||
pEntity->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
return;
|
||||
}
|
||||
// the entity got stuck with horizontal bridge
|
||||
else if ( pEntity->IsPlayer() && m_bIsHorizontal )
|
||||
{
|
||||
// if success, move on
|
||||
CPortal_Player* pPlayer = ToPortalPlayer( pEntity );
|
||||
Assert ( pPlayer );
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
// 1. If player wasn't crouching already, try to crouch and move player up
|
||||
if ( !pPlayer->m_Local.m_bDucked )
|
||||
{
|
||||
// If ducking stops the intersection, force them to duck
|
||||
TracePlayerBoxAgainstCollidables( stuckTrace, pPlayer, vNewPos, vNewPos, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
|
||||
if ( !stuckTrace.startsolid )
|
||||
{
|
||||
//EASY_DIFFPRINT( this, "CProjectedWallEntity::DisplaceObstructingEntities() force duck up" );
|
||||
pPlayer->ForceDuckThisFrame();
|
||||
pPlayer->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If pushing up failed, try to move the player to the opposite side
|
||||
vNewPos = pEntity->GetAbsOrigin() - flInvBumpAmount * vBumpAxis;
|
||||
if ( !pPlayer->m_Local.m_bDucked )
|
||||
{
|
||||
TracePlayerBoxAgainstCollidables( stuckTrace, pPlayer, vNewPos, vNewPos, VEC_HULL_MIN, VEC_HULL_MAX );
|
||||
if ( !stuckTrace.startsolid )
|
||||
{
|
||||
//EASY_DIFFPRINT( this, "CProjectedWallEntity::DisplaceObstructingEntities() teleport down" );
|
||||
pPlayer->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , VEC_HULL_MIN, VEC_HULL_MAX, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check duck
|
||||
vNewPos += 36.f * Vector( 0, 0, 1 );
|
||||
TracePlayerBoxAgainstCollidables( stuckTrace, pPlayer, vNewPos, vNewPos, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
|
||||
if ( !stuckTrace.startsolid )
|
||||
{
|
||||
//EASY_DIFFPRINT( this, "CProjectedWallEntity::DisplaceObstructingEntities() force duck down" );
|
||||
pPlayer->ForceDuckThisFrame();
|
||||
pPlayer->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TracePlayerBoxAgainstCollidables( stuckTrace, pPlayer, vNewPos, vNewPos, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
|
||||
if ( !stuckTrace.startsolid )
|
||||
{
|
||||
//EASY_DIFFPRINT( this, "CProjectedWallEntity::DisplaceObstructingEntities() double duck down" );
|
||||
pPlayer->ForceDuckThisFrame();
|
||||
pPlayer->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , VEC_HULL_MIN, VEC_HULL_MAX, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// player stuck in not-horizontal bridge OR entity is stuck in a bridge
|
||||
else
|
||||
{
|
||||
vNewPos = pEntity->GetAbsOrigin() - flInvBumpAmount * vBumpAxis;
|
||||
UTIL_ClearTrace( stuckTrace );
|
||||
enginetrace->SweepCollideable( pEntity->GetCollideable(), vNewPos, vNewPos, vNewAngles, MASK_SOLID, &filter, &stuckTrace );
|
||||
|
||||
if ( !stuckTrace.startsolid || bIgnoreStuck )
|
||||
{
|
||||
pEntity->Teleport( &vNewPos, &vNewAngles, &vNewVel );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , vObstructionMins, vObstructionMaxs, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
|
||||
STEAMWORKS_SELFCHECK();
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
if ( wall_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::BoxAngles( vNewPos , vObstructionMins, vObstructionMaxs, pEntity->CollisionProp()->GetCollisionAngles(), 255, 0, 0, 64, wall_debug_time.GetFloat() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// The entity is stuck at super rare case if we get here.
|
||||
{
|
||||
AssertMsg( 0, "Rare case for the entity getting stuck with projected bridge. Investigate at CProjectedWallEntity::DisplaceObstructingEntities()." );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CProjectedWallEntity::GetExtents( Vector &outMins, Vector &outMaxs, float flWidthScale )
|
||||
{
|
||||
// Get current orientation
|
||||
Vector vecForward, vecRight, vecUp;
|
||||
#if defined( GAME_DLL )
|
||||
QAngle qAngles = GetLocalAngles();
|
||||
#else
|
||||
QAngle qAngles = GetNetworkAngles();
|
||||
#endif
|
||||
|
||||
AngleVectors( qAngles, &vecForward, &vecRight, &vecUp );
|
||||
|
||||
#if defined( GAME_DLL ) && !defined( _PS3 )
|
||||
// we're assuming it's oblong, and that height is the larger
|
||||
COMPILE_TIME_ASSERT( WALL_PROJECTOR_THICKNESS > WALL_PROJECTOR_HEIGHT );
|
||||
#endif
|
||||
|
||||
// Set up mins/maxes to trace along
|
||||
float flHalfHeight = m_flHeight / 2.f;
|
||||
float flHalfWidth = ( m_flWidth * flWidthScale ) / 2.f;
|
||||
Vector vTmpExtent1 = ( -vecForward * FLT_EPSILON ) - ( vecUp * flHalfHeight ) - ( vecRight * flHalfWidth );
|
||||
Vector vTmpExtent2 = ( vecForward * FLT_EPSILON ) + ( vecUp * flHalfHeight ) + ( vecRight * flHalfWidth );
|
||||
|
||||
// align the mins and maxs
|
||||
Vector vWallSweptBoxMins, vWallSweptBoxMaxs;
|
||||
VectorMin( vTmpExtent1, vTmpExtent2, vWallSweptBoxMins );
|
||||
VectorMax( vTmpExtent1, vTmpExtent2, vWallSweptBoxMaxs );
|
||||
|
||||
outMins = vWallSweptBoxMins;
|
||||
outMaxs = vWallSweptBoxMaxs;
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: Declares the base class for paint power users that are props.
|
||||
//
|
||||
//===========================================================================//
|
||||
#ifndef PROP_PAINT_POWER_USER_H
|
||||
#define PROP_PAINT_POWER_USER_H
|
||||
|
||||
#include "vphysics/friction.h"
|
||||
#include "vphysics/constraints.h"
|
||||
#include "player_pickup.h"
|
||||
#include "paintable_entity.h"
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
#include "portal/weapon_physcannon.h"
|
||||
#endif
|
||||
|
||||
#include "portal_util_shared.h"
|
||||
#include "portal_base2d_shared.h"
|
||||
|
||||
#include "paint_power_user.h"
|
||||
#include "stick_partner.h"
|
||||
|
||||
#include "material_index_data_ops_proxy.h"
|
||||
|
||||
char const* const PROP_PAINT_POWER_USER_DATA_CLASS_NAME = "PropPaintPowerUser";
|
||||
|
||||
char const* const UPDATE_PAINT_POWER_CONTEXT = "UpdatePaintPowers";
|
||||
|
||||
const float PROP_PAINT_POWER_USER_PICKUP_DROP_TIME = 0.5f;
|
||||
|
||||
//=============================================================================
|
||||
// class PropPaintPowerUser
|
||||
// Purpose: Base class for props which use paint powers.
|
||||
//=============================================================================
|
||||
template< typename BasePropType >
|
||||
class PropPaintPowerUser : public PaintPowerUser< CPaintableEntity< BasePropType > > // Derive from PaintPowerUser but add CPaintableEntity.
|
||||
{
|
||||
DECLARE_CLASS( PropPaintPowerUser< BasePropType >, PaintPowerUser< CPaintableEntity< BasePropType > > );
|
||||
DECLARE_DATADESC();
|
||||
static const datamap_t DataMapInit();
|
||||
|
||||
public:
|
||||
//-------------------------------------------------------------------------
|
||||
// Constructor/Virtual Destructor
|
||||
//-------------------------------------------------------------------------
|
||||
PropPaintPowerUser();
|
||||
virtual ~PropPaintPowerUser();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Prop Overrides
|
||||
//-------------------------------------------------------------------------
|
||||
virtual void Spawn();
|
||||
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
|
||||
virtual void VPhysicsUpdate( IPhysicsObject *pPhysics );
|
||||
virtual void UpdatePaintPowersFromContacts();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paintable Entity Overrides
|
||||
//-------------------------------------------------------------------------
|
||||
virtual void Paint( PaintPowerType type, const Vector& worldContactPt );
|
||||
|
||||
protected:
|
||||
int m_nOriginalMaterialIndex; // Cached physics material index (it changes)
|
||||
int m_PrePaintedPower; // Power to start with on load
|
||||
|
||||
typedef typename BaseClass::PaintPowerInfoVector BaseClass_PaintPowerInfoVector;
|
||||
|
||||
virtual void ChooseActivePaintPowers( BaseClass_PaintPowerInfoVector& activePowers );
|
||||
|
||||
static int GetSpeedMaterialIndex();
|
||||
|
||||
private:
|
||||
//-------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//-------------------------------------------------------------------------
|
||||
bool m_bHeldByPlayer;
|
||||
float m_flPickedUpTime;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Paint Power Effects
|
||||
//-------------------------------------------------------------------------
|
||||
virtual PaintPowerState ActivateSpeedPower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState UseSpeedPower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState DeactivateSpeedPower( PaintPowerInfo_t& powerInfo );
|
||||
|
||||
virtual PaintPowerState ActivateBouncePower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState UseBouncePower( PaintPowerInfo_t& powerInfo );
|
||||
virtual PaintPowerState DeactivateBouncePower( PaintPowerInfo_t& powerInfo );
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// PropPaintPowerUser Implementation
|
||||
//=============================================================================
|
||||
|
||||
// OMFG HACK: Define the data description table. The current macros don't work with templatized classes.
|
||||
// OMFG TODO: Write a generic macro to work with templatized classes.
|
||||
template< typename BasePropType >
|
||||
datamap_t PropPaintPowerUser<BasePropType>::m_DataMap = PropPaintPowerUser<BasePropType>::DataMapInit();
|
||||
|
||||
template< typename BasePropType >
|
||||
datamap_t* PropPaintPowerUser<BasePropType>::GetDataDescMap()
|
||||
{
|
||||
return &m_DataMap;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
datamap_t* PropPaintPowerUser<BasePropType>::GetBaseMap()
|
||||
{
|
||||
datamap_t *pResult;
|
||||
DataMapAccess((BaseClass *)NULL, &pResult);
|
||||
return pResult;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
const datamap_t PropPaintPowerUser<BasePropType>::DataMapInit()
|
||||
{
|
||||
typedef PropPaintPowerUser<BasePropType> classNameTypedef;
|
||||
static CDatadescGeneratedNameHolder nameHolder(PROP_PAINT_POWER_USER_DATA_CLASS_NAME);
|
||||
static typedescription_t dataDesc[] =
|
||||
{
|
||||
DEFINE_KEYFIELD( m_PrePaintedPower, FIELD_INTEGER, "PaintPower" ),
|
||||
DEFINE_CUSTOM_FIELD( m_nOriginalMaterialIndex, &GetMaterialIndexDataOpsProxy() )
|
||||
};
|
||||
|
||||
datamap_t dataMap = { dataDesc, SIZE_OF_ARRAY(dataDesc), PROP_PAINT_POWER_USER_DATA_CLASS_NAME, PropPaintPowerUser<BasePropType>::GetBaseMap() };
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PropPaintPowerUser<BasePropType>::PropPaintPowerUser()
|
||||
: m_PrePaintedPower(NO_POWER),
|
||||
m_flPickedUpTime( 0.0f ),
|
||||
m_bHeldByPlayer( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PropPaintPowerUser<BasePropType>::~PropPaintPowerUser()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
// Store our material index
|
||||
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
m_nOriginalMaterialIndex = pPhysObject->GetMaterialIndex();
|
||||
}
|
||||
|
||||
this->AddFlag( FL_AFFECTED_BY_PAINT );
|
||||
if( m_PrePaintedPower != NO_POWER )
|
||||
{
|
||||
this->Paint( (PaintPowerType)m_PrePaintedPower, vec3_origin );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
|
||||
{
|
||||
if( engine->HasPaintmap() )
|
||||
{
|
||||
CBaseEntity* pOther = pEvent->pEntities[!index];
|
||||
|
||||
PaintPowerInfo_t contact;
|
||||
|
||||
// Get data out of the event
|
||||
Vector vNormal, vPoint;
|
||||
pEvent->pInternalData->GetSurfaceNormal( vNormal );
|
||||
pEvent->pInternalData->GetContactPoint( vPoint );
|
||||
|
||||
// Fill out contact info
|
||||
contact.m_SurfaceNormal = -vNormal;
|
||||
contact.m_ContactPoint = vPoint;
|
||||
contact.m_HandleToOther.Set( pOther );
|
||||
|
||||
// Add info to paint power info
|
||||
this->AddSurfacePaintPowerInfo( contact, 0 );
|
||||
}
|
||||
|
||||
BaseClass::VPhysicsCollision( index, pEvent );
|
||||
}
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::VPhysicsUpdate( IPhysicsObject *pPhysics )
|
||||
{
|
||||
if( engine->HasPaintmap() )
|
||||
{
|
||||
UpdatePaintPowersFromContacts();
|
||||
}
|
||||
|
||||
BaseClass::VPhysicsUpdate( pPhysics );
|
||||
}
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::UpdatePaintPowersFromContacts()
|
||||
{
|
||||
//If the prop is held by a player
|
||||
if( GetPlayerHoldingEntity( this ) )
|
||||
{
|
||||
//If the prop was not already held by a player
|
||||
if( !m_bHeldByPlayer )
|
||||
{
|
||||
m_bHeldByPlayer = true;
|
||||
|
||||
//Set the timer
|
||||
m_flPickedUpTime = gpGlobals->curtime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bHeldByPlayer = false;
|
||||
m_flPickedUpTime = 0.0f;
|
||||
}
|
||||
|
||||
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
IPhysicsFrictionSnapshot* pSnapShot = pPhysObject->CreateFrictionSnapshot();
|
||||
while( pSnapShot->IsValid() )
|
||||
{
|
||||
PaintPowerInfo_t contact;
|
||||
|
||||
IPhysicsObject *pOther = pSnapShot->GetObject(1);
|
||||
CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
|
||||
Assert(pOtherEntity);
|
||||
|
||||
if( pOtherEntity != NULL )
|
||||
{
|
||||
// Get data out of the event
|
||||
Vector vNormal, vPoint;
|
||||
pSnapShot->GetSurfaceNormal( vNormal );
|
||||
pSnapShot->GetContactPoint( vPoint );
|
||||
|
||||
// Fill out contact info
|
||||
contact.m_SurfaceNormal = -vNormal;
|
||||
contact.m_ContactPoint = vPoint;
|
||||
contact.m_HandleToOther.Set( pOtherEntity );
|
||||
|
||||
// Add info to paint power info
|
||||
this->AddSurfacePaintPowerInfo( contact, 0 );
|
||||
}
|
||||
|
||||
pSnapShot->NextFrictionData();
|
||||
}
|
||||
|
||||
pPhysObject->DestroyFrictionSnapshot( pSnapShot );
|
||||
|
||||
// Figure out paint powers
|
||||
this->UpdatePaintPowers();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear all current data
|
||||
this->ClearSurfacePaintPowerInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::ChooseActivePaintPowers( BaseClass_PaintPowerInfoVector& activePowers )
|
||||
{
|
||||
this->MapSurfacesToPowers();
|
||||
|
||||
// Get the contacts
|
||||
PaintPowerConstRange powerRange = this->GetSurfacePaintPowerInfo();
|
||||
size_t count = powerRange.second - powerRange.first;
|
||||
|
||||
// Set our desired paint power to be our current painted color
|
||||
PaintPowerInfo_t desiredPower;
|
||||
desiredPower.m_PaintPowerType = NO_POWER;
|
||||
|
||||
// Get the first active power, since props can only have one at a time
|
||||
const PaintPowerInfo_t* pHighestPriorityActivePower = this->FindHighestPriorityActivePaintPower();
|
||||
PaintPowerInfo_t currentPower = pHighestPriorityActivePower ? *pHighestPriorityActivePower : PaintPowerInfo_t( Vector(0, 0, 1), this->GetAbsOrigin(), 0 );
|
||||
|
||||
PaintPowerType paintedPower = this->GetPaintedPower();
|
||||
|
||||
// If we're touching something
|
||||
if( count != 0 )
|
||||
{
|
||||
this->PrioritySortSurfacePaintPowerInfo( &DescendingPaintPriorityCompare );
|
||||
|
||||
// Default our desired color to be our current painted color so this will default
|
||||
// as our power if all else fails
|
||||
if( paintedPower != NO_POWER )
|
||||
{
|
||||
for( PaintPowerConstIter i = powerRange.first; i != powerRange.second; ++i )
|
||||
{
|
||||
if( i->m_PaintPowerType != INVALID_PAINT_POWER )
|
||||
{
|
||||
desiredPower = *i;
|
||||
desiredPower.m_PaintPowerType = paintedPower;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through all the surfaces and try to find a power to use
|
||||
for( PaintPowerConstIter i = powerRange.first; i != powerRange.second; ++i )
|
||||
{
|
||||
const PaintPowerInfo_t& powerInfo = *i;
|
||||
|
||||
if( currentPower.m_PaintPowerType == SPEED_POWER )
|
||||
{
|
||||
// Always take bounce when currently using speed
|
||||
if( powerInfo.m_PaintPowerType == BOUNCE_POWER )
|
||||
{
|
||||
desiredPower = powerInfo;
|
||||
}
|
||||
// Take speed if others are not present
|
||||
else if( desiredPower.m_PaintPowerType == NO_POWER &&
|
||||
powerInfo.m_PaintPowerType == SPEED_POWER )
|
||||
{
|
||||
desiredPower = powerInfo;
|
||||
}
|
||||
}
|
||||
else if( powerInfo.m_PaintPowerType != NO_POWER &&
|
||||
powerInfo.m_PaintPowerType < desiredPower.m_PaintPowerType )
|
||||
{ // Accept whatever it was if it's of higher priority
|
||||
desiredPower = powerInfo;
|
||||
}
|
||||
}//for
|
||||
}//if count
|
||||
|
||||
// Add the power to the active list
|
||||
activePowers.AddToTail( desiredPower );
|
||||
}
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::ActivateSpeedPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
pPhysObject->SetMaterialIndex( ThisClass::GetSpeedMaterialIndex() );
|
||||
}
|
||||
|
||||
return ACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::UseSpeedPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return ACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::DeactivateSpeedPower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
pPhysObject->SetMaterialIndex( m_nOriginalMaterialIndex );
|
||||
}
|
||||
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
extern ConVar sv_wall_bounce_trade;
|
||||
extern ConVar bounce_paint_wall_jump_upward_speed;
|
||||
extern ConVar bounce_paint_min_speed;
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::ActivateBouncePower( PaintPowerInfo_t& info )
|
||||
{
|
||||
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
float flTrade = sv_wall_bounce_trade.GetFloat(); // We trade some outward velocity for upward velocity
|
||||
const Vector vUp = Vector(0,0,1);
|
||||
Vector vBounceVel(0,0,0);
|
||||
|
||||
// Cancel out velocity going into the surface
|
||||
Vector velocity;
|
||||
AngularImpulse angularVel;
|
||||
pPhysObject->GetVelocity( &velocity, &angularVel);
|
||||
velocity -= info.m_SurfaceNormal * DotProduct( velocity, info.m_SurfaceNormal );
|
||||
|
||||
// Cancel out downward velocity (allows for going up parallel walls)
|
||||
velocity -= vUp * DotProduct( velocity, vUp );
|
||||
|
||||
// Store this for later
|
||||
Vector velNorm = velocity;
|
||||
velNorm.NormalizeInPlace();
|
||||
|
||||
float flNormDot = DotProduct( vUp, info.m_SurfaceNormal );
|
||||
|
||||
float flBounceScale = 0.f;
|
||||
// Add upward velocity if surface normal is facing up, relative to the player
|
||||
if( flNormDot > -0.1f )
|
||||
{
|
||||
// Extra upward wall bounce velocity
|
||||
flBounceScale = (1.f - DotProduct( vUp, info.m_SurfaceNormal ));
|
||||
vBounceVel += vUp * bounce_paint_wall_jump_upward_speed.GetFloat() * flBounceScale;
|
||||
}
|
||||
else // Downward facing wall. Add velocity in the XY plane
|
||||
{
|
||||
// Vector pointing out of the surface in the XY plane
|
||||
Vector vOut = ( info.m_SurfaceNormal - (vUp * DotProduct( vUp, info.m_SurfaceNormal )) ).Normalized();
|
||||
|
||||
// Extra lateral velocity off the wall
|
||||
flBounceScale = DotProduct( vOut, info.m_SurfaceNormal );
|
||||
vBounceVel += vOut * bounce_paint_wall_jump_upward_speed.GetFloat() * flBounceScale;
|
||||
}
|
||||
|
||||
// Calculate how much bounce velocity is left to spend after the lateral
|
||||
float fWallBounceScale = flTrade + ( (1.f - flBounceScale) * (1.0 - flTrade) );
|
||||
// Velocity off of the surface
|
||||
vBounceVel += info.m_SurfaceNormal * bounce_paint_min_speed.GetFloat() * fWallBounceScale;
|
||||
|
||||
// If we're going to bounce straight up, add some random XY velocity. Bouncing straight up
|
||||
// doesn't look natural.
|
||||
if( vBounceVel.x == 0.f && vBounceVel.y == 0.f )
|
||||
{
|
||||
vBounceVel += Vector( RandomFloat(-80.f, 80.f), RandomFloat(-80.f, 80.f), 0.f );
|
||||
}
|
||||
|
||||
// Dont let our velocity fight the new bounce velocity
|
||||
velocity -= vBounceVel.Normalized() * DotProduct( velocity, vBounceVel.Normalized() );
|
||||
|
||||
velocity += vBounceVel;
|
||||
|
||||
// Add velocity to physics object
|
||||
pPhysObject->SetVelocity( &velocity, &angularVel );
|
||||
}
|
||||
|
||||
return DEACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::UseBouncePower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return DEACTIVATING_PAINT_POWER;
|
||||
}
|
||||
|
||||
|
||||
template< typename BasePropType >
|
||||
PaintPowerState PropPaintPowerUser<BasePropType>::DeactivateBouncePower( PaintPowerInfo_t& powerInfo )
|
||||
{
|
||||
return INACTIVE_PAINT_POWER;
|
||||
}
|
||||
|
||||
template< typename BasePropType >
|
||||
int PropPaintPowerUser<BasePropType>::GetSpeedMaterialIndex()
|
||||
{
|
||||
static int s_SpeedMaterialIndex = physprops->GetSurfaceIndex( "ice" );
|
||||
return s_SpeedMaterialIndex;
|
||||
}
|
||||
|
||||
template< typename BasePropType >
|
||||
void PropPaintPowerUser<BasePropType>::Paint( PaintPowerType type, const Vector& worldContactPt )
|
||||
{
|
||||
BaseClass::Paint( type, worldContactPt );
|
||||
|
||||
IPhysicsObject* pPhysicsObject = this->VPhysicsGetObject();
|
||||
if( pPhysicsObject != NULL && pPhysicsObject->IsAsleep() )
|
||||
{
|
||||
pPhysicsObject->Wake();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ifndef PROP_PAINT_POWER_USER_H
|
||||
@@ -0,0 +1,271 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "prop_portal_shared.h"
|
||||
#include "portal_shareddefs.h"
|
||||
#include "portal_placement.h"
|
||||
#include "weapon_portalgun_shared.h"
|
||||
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
#include "baseprojector.h"
|
||||
#else
|
||||
#include "c_baseprojectedentity.h"
|
||||
typedef C_BaseProjectedEntity CBaseProjectedEntity;
|
||||
#endif
|
||||
|
||||
CUtlVector<CProp_Portal *> CProp_Portal_Shared::AllPortals;
|
||||
|
||||
extern ConVar sv_portal_placement_never_fail;
|
||||
|
||||
void CProp_Portal::PlacePortal( const Vector &vOrigin, const QAngle &qAngles, PortalPlacementResult_t eResult, bool bDelay /*= false*/ )
|
||||
{
|
||||
Vector vOldOrigin = GetLocalOrigin();
|
||||
QAngle qOldAngles = GetLocalAngles();
|
||||
|
||||
Vector vNewOrigin = vOrigin;
|
||||
QAngle qNewAngles = qAngles;
|
||||
|
||||
#if !defined( PORTAL2 )
|
||||
UTIL_TestForOrientationVolumes( qNewAngles, vNewOrigin, this );
|
||||
#endif // PORTAL2
|
||||
|
||||
if ( PortalPlacementSucceeded( eResult ) == false && sv_portal_placement_never_fail.GetBool() == false )
|
||||
{
|
||||
// Prepare fizzle
|
||||
m_vDelayedPosition = vOrigin;
|
||||
m_qDelayedAngles = qAngles;
|
||||
|
||||
// Translate the fizzle type
|
||||
// FIXME: This can go away, we don't care about the fizzle type anymore -- jdw
|
||||
switch( eResult )
|
||||
{
|
||||
case PORTAL_PLACEMENT_CANT_FIT:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_CANT_FIT;
|
||||
break;
|
||||
|
||||
case PORTAL_PLACEMENT_OVERLAP_LINKED:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_OVERLAPPED_LINKED;
|
||||
break;
|
||||
|
||||
case PORTAL_PLACEMENT_INVALID_VOLUME:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_BAD_VOLUME;
|
||||
break;
|
||||
|
||||
case PORTAL_PLACEMENT_INVALID_SURFACE:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_BAD_SURFACE;
|
||||
break;
|
||||
|
||||
case PORTAL_PLACEMENT_CLEANSER:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_CLEANSER;
|
||||
break;
|
||||
|
||||
default:
|
||||
case PORTAL_PLACEMENT_PASSTHROUGH_SURFACE:
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_vDelayedPosition = vNewOrigin;
|
||||
m_qDelayedAngles = qNewAngles;
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_SUCCESS;
|
||||
|
||||
if ( bDelay == false )
|
||||
{
|
||||
NewLocation( vNewOrigin, qNewAngles );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Runs when a fired portal shot reaches it's destination wall. Detects current placement valididty state.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::DelayedPlacementThink( void )
|
||||
{
|
||||
Vector vOldOrigin = m_ptOrigin; //GetLocalOrigin();
|
||||
QAngle qOldAngles = m_qAbsAngle; //GetLocalAngles();
|
||||
|
||||
Vector vForward;
|
||||
AngleVectors( m_qDelayedAngles, &vForward );
|
||||
|
||||
// Check if something made the spot invalid mid flight
|
||||
// Bad surface and near fizzle effects take priority
|
||||
if ( m_iDelayedFailure != PORTAL_FIZZLE_BAD_SURFACE && m_iDelayedFailure != PORTAL_FIZZLE_NEAR_BLUE && m_iDelayedFailure != PORTAL_FIZZLE_NEAR_RED )
|
||||
{
|
||||
if ( IsPortalOverlappingOtherPortals( this, m_vDelayedPosition, m_qDelayedAngles, GetHalfWidth(), GetHalfHeight() ) )
|
||||
{
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_OVERLAPPED_LINKED;
|
||||
}
|
||||
else if ( IsPortalIntersectingNoPortalVolume( m_vDelayedPosition, m_qDelayedAngles, vForward, GetHalfWidth(), GetHalfHeight() ) )
|
||||
{
|
||||
#if defined GAME_DLL
|
||||
RANDOM_CEG_TEST_SECRET_PERIOD( 29, 83 )
|
||||
#endif
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_BAD_VOLUME;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sv_portal_placement_never_fail.GetBool() )
|
||||
{
|
||||
m_iDelayedFailure = PORTAL_FIZZLE_SUCCESS;
|
||||
}
|
||||
|
||||
DoFizzleEffect( m_iDelayedFailure );
|
||||
|
||||
if ( m_iDelayedFailure != PORTAL_FIZZLE_SUCCESS )
|
||||
{
|
||||
// It didn't successfully place
|
||||
return;
|
||||
}
|
||||
|
||||
// Do effects at old location if it was active
|
||||
if ( GetOldActiveState() )
|
||||
{
|
||||
DoFizzleEffect( PORTAL_FIZZLE_CLOSE, false );
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( m_hPlacedBy.Get() );
|
||||
|
||||
if( pPortalGun )
|
||||
{
|
||||
CPortal_Player *pFiringPlayer = dynamic_cast<CPortal_Player *>( pPortalGun->GetOwner() );
|
||||
if( pFiringPlayer )
|
||||
{
|
||||
pFiringPlayer->IncrementPortalsPlaced( IsPortal2() );
|
||||
|
||||
// Placement successful, fire the output
|
||||
m_OnPlacedSuccessfully.FireOutput( pPortalGun, this );
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Move to new location
|
||||
NewLocation( m_vDelayedPosition, m_qDelayedAngles );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
// Test for our surface moving out from behind us
|
||||
SetContextThink( &CProp_Portal::TestRestingSurfaceThink, gpGlobals->curtime + 0.1f, s_szTestRestingSurfaceThinkContext );
|
||||
|
||||
CBaseProjector::TestAllForProjectionChanges();
|
||||
#else
|
||||
CBaseProjectedEntity::TestAllForProjectionChanges();
|
||||
#endif
|
||||
}
|
||||
|
||||
// default to sane-looking but incorrect portal height for CEG - Updated in constructor
|
||||
bool CProp_Portal::ms_DefaultPortalSizeInitialized = false; // for CEG protection
|
||||
float CProp_Portal::ms_DefaultPortalHalfWidth = DEFAULT_PORTAL_HALF_WIDTH;
|
||||
float CProp_Portal::ms_DefaultPortalHalfHeight = 0.25 * DEFAULT_PORTAL_HALF_HEIGHT;
|
||||
|
||||
//NULL portal will return default width/height
|
||||
void CProp_Portal::GetPortalSize( float &fHalfWidth, float &fHalfHeight, CProp_Portal *pPortal )
|
||||
{
|
||||
if( pPortal )
|
||||
{
|
||||
fHalfWidth = pPortal->GetHalfWidth();
|
||||
fHalfHeight = pPortal->GetHalfHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
fHalfWidth = ms_DefaultPortalHalfWidth;
|
||||
fHalfHeight = ms_DefaultPortalHalfHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CProp_Portal::SetFiredByPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
m_hFiredByPlayer = pPlayer;
|
||||
if( pPlayer )
|
||||
{
|
||||
SetPlayerSimulated( pPlayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
UnsetPlayerSimulated();
|
||||
}
|
||||
}
|
||||
|
||||
extern ConVar sv_gravity;
|
||||
|
||||
float CProp_Portal::GetMinimumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity )
|
||||
{
|
||||
if( bExitOnFloor )
|
||||
{
|
||||
if( bPlayer )
|
||||
{
|
||||
return 300.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bEntranceOnFloor ? 225.0f : 50.0f;
|
||||
}
|
||||
}
|
||||
else if( bPlayer )
|
||||
{
|
||||
//bExitOnFloor means the portal is facing almost entirely up, just because it's false doesn't mean the portal isn't facing significantly up
|
||||
//We also need to solve the case where the player's AABB rotates in such a way that we pull the ground out from under them
|
||||
if( m_vForward.z > 0.5f ) //forward facing up by at least 30 degrees
|
||||
{
|
||||
float fGravity = GetGravity();
|
||||
if ( fGravity != 0.0f )
|
||||
{
|
||||
fGravity *= sv_gravity.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
fGravity = sv_gravity.GetFloat();
|
||||
}
|
||||
|
||||
if( fGravity != 0.0f )
|
||||
{
|
||||
//Assuming our current velocity is zero. What's the minimum portal-forward velocity to perch the player on the bottom edge of the portal?
|
||||
Vector vPerchPoint = m_ptOrigin - (m_vUp * GetHalfHeight()); //a point along the bottom edge of the portal, horizontally centered
|
||||
Vector vPlayerExtents = (((CPortal_Player *)pEntity)->GetHullMaxs() - ((CPortal_Player *)pEntity)->GetHullMins()) * 0.5f;
|
||||
//Vector vPlayerCenterToPerch = vPerchPoint - vEntityCenterAtExit;
|
||||
Vector vTestBBoxPoint = vEntityCenterAtExit;
|
||||
//vTestBBoxPoint.x += Sign( vPlayerCenterToPerch.x ) * vPlayerExtents.x;
|
||||
//vTestBBoxPoint.y += Sign( vPlayerCenterToPerch.y ) * vPlayerExtents.y;
|
||||
vTestBBoxPoint.z -= vPlayerExtents.z;
|
||||
|
||||
|
||||
Vector vTestToPerch = vPerchPoint - vTestBBoxPoint;
|
||||
vTestToPerch -= vTestToPerch.Dot( m_vRight ) * m_vRight; //Project test vector onto horizontal center, so all x/y dist to perch point is actually distance to perch line
|
||||
float fHorzTestToPerch = vTestToPerch.Length2D();
|
||||
float fHorzVelocityComponent = m_vForward.Length2D(); //the portion of our velocity axis that will move us horizontally toward the perch
|
||||
|
||||
|
||||
float fRoot1, fRoot2;
|
||||
if( SolveQuadratic( (m_vForward.z * (-2.0f)) * ((fHorzTestToPerch * fHorzVelocityComponent) - (vTestToPerch.z*m_vForward.z)), 0, fHorzTestToPerch * fHorzTestToPerch * fGravity, fRoot1, fRoot2 ) )
|
||||
{
|
||||
float fMax = MAX( fRoot1, fRoot2 );
|
||||
if( fMax > 0.0f )
|
||||
{
|
||||
if( fMax > 300.0f ) //cap out at floor/floor minimum
|
||||
return 300.0f;
|
||||
else
|
||||
return fMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::GetMinimumExitSpeed( bPlayer, bEntranceOnFloor, bExitOnFloor, vEntityCenterAtExit, pEntity );
|
||||
}
|
||||
|
||||
float CProp_Portal::GetMaximumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity )
|
||||
{
|
||||
return 1000.0f;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
#include "portal_base2d_shared.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "C_Prop_Portal.h"
|
||||
#else
|
||||
#include "prop_portal.h"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_PORTAL_HALF_WIDTH 32.0f
|
||||
#define DEFAULT_PORTAL_HALF_HEIGHT 56.0f
|
||||
|
||||
class CProp_Portal_Shared //defined as a class to make intellisense more intelligent
|
||||
{
|
||||
public:
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,586 @@
|
||||
//========= Copyright © 1996-2006, 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 ) { Assert( false ); };
|
||||
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 void *s_BrushPolyhedronMemory = NULL;
|
||||
static void *s_StaticPropPolyhedronMemory = NULL;
|
||||
|
||||
CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache;
|
||||
|
||||
void sv_portal_staticcollisioncache_cache_ChangeFN( IConVar *var, const char *pOldValue, float flOldValue )
|
||||
{
|
||||
g_StaticCollisionPolyhedronCache.ForceRefreshOnMapLoad(); //force a reload on restart/mapchange
|
||||
}
|
||||
|
||||
ConVar sv_portal_staticcollisioncache_cachebrushes( "sv_portal_staticcollisioncache_cachebrushes", IsPS3() ? "0" : "1", FCVAR_REPLICATED, "Cache all solid brushes as polyhedrons on level load", sv_portal_staticcollisioncache_cache_ChangeFN );
|
||||
ConVar sv_portal_staticcollisioncache_cachestaticprops( "sv_portal_staticcollisioncache_cachestaticprops", IsPS3() ? "0" : "1", FCVAR_REPLICATED, "Cache all solid static props' vcollides as polyhedrons on level load", sv_portal_staticcollisioncache_cache_ChangeFN );
|
||||
|
||||
|
||||
typedef vcollide_t *VCollidePtr; //needed for key comparison function syntax
|
||||
static bool CollideablePtr_KeyCompareFunc( const VCollidePtr &a, const VCollidePtr &b )
|
||||
{
|
||||
return a < b;
|
||||
};
|
||||
|
||||
CStaticCollisionPolyhedronCache::CStaticCollisionPolyhedronCache( void )
|
||||
: m_CollideableIndicesMap( CollideablePtr_KeyCompareFunc )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CStaticCollisionPolyhedronCache::~CStaticCollisionPolyhedronCache( void )
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CStaticCollisionPolyhedronCache::LevelInitPreEntity( void )
|
||||
{
|
||||
//keep the cached data if the source hasn't changed.
|
||||
if(
|
||||
#if defined( GAME_DLL )
|
||||
(gpGlobals->eLoadType != MapLoad_LoadGame) || //always reload on new game, only in case the map file contents changed (level designers using "restart")
|
||||
#endif
|
||||
(Q_stricmp( m_CachedMap, MapName() ) != 0) ) //different map than we have cached.
|
||||
{
|
||||
// New map or the last load was a transition, fully update the cache
|
||||
m_CachedMap.Set( MapName() );
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CPolyhedron *ConvertBrushToPolyhedron( int iBrushNumber, int iContentsMask, bool bTempPolyhedron )
|
||||
{
|
||||
int iBrushContents = 0;
|
||||
int iPlanesNeeded = -enginetrace->GetBrushInfo( iBrushNumber, iBrushContents, NULL, 0 );
|
||||
if( (iPlanesNeeded == 0) || ((iContentsMask & iBrushContents) == 0) )
|
||||
return NULL;
|
||||
|
||||
uint8 *pMemory;
|
||||
void *pDeleteMemory;
|
||||
float *fStackPlanes;
|
||||
BrushSideInfo_t *brushSides;
|
||||
size_t iMemoryNeeded = iPlanesNeeded * ((sizeof( float ) * 4) + sizeof( BrushSideInfo_t ));
|
||||
|
||||
if( iMemoryNeeded < (64 * 1024) )
|
||||
{
|
||||
//use stack memory
|
||||
pMemory = (uint8 *)stackalloc( iMemoryNeeded );
|
||||
pDeleteMemory = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pMemory = new uint8 [iMemoryNeeded];
|
||||
pDeleteMemory = pMemory;
|
||||
}
|
||||
|
||||
|
||||
fStackPlanes = (float *)pMemory;
|
||||
brushSides = (BrushSideInfo_t *)(pMemory + (iPlanesNeeded * (sizeof( float ) * 4)));
|
||||
int iPlaneCount = enginetrace->GetBrushInfo( iBrushNumber, iBrushContents, brushSides, iPlanesNeeded );
|
||||
|
||||
CPolyhedron *pRetVal = NULL;
|
||||
Assert( iPlaneCount == iPlanesNeeded );
|
||||
if( iPlaneCount == iPlanesNeeded )
|
||||
{
|
||||
for( int i = 0; i != iPlaneCount; ++i )
|
||||
{
|
||||
fStackPlanes[(i * 4) + 0] = brushSides[i].plane.normal.x;
|
||||
fStackPlanes[(i * 4) + 1] = brushSides[i].plane.normal.y;
|
||||
fStackPlanes[(i * 4) + 2] = brushSides[i].plane.normal.z;
|
||||
fStackPlanes[(i * 4) + 3] = brushSides[i].plane.dist;
|
||||
}
|
||||
|
||||
pRetVal = GeneratePolyhedronFromPlanes( fStackPlanes, iPlaneCount, (1.0f/16.0f), bTempPolyhedron );
|
||||
}
|
||||
|
||||
if( pDeleteMemory )
|
||||
{
|
||||
delete []pDeleteMemory;
|
||||
}
|
||||
|
||||
return pRetVal;
|
||||
}
|
||||
|
||||
void CStaticCollisionPolyhedronCache::Update( void )
|
||||
{
|
||||
Clear();
|
||||
|
||||
if( gpGlobals->IsClient() && (g_pGameRules->IsMultiplayer() == false) ) //not going to need this data on the client
|
||||
return;
|
||||
|
||||
if( !sv_portal_staticcollisioncache_cachebrushes.GetBool() && !sv_portal_staticcollisioncache_cachestaticprops.GetBool() )
|
||||
return;
|
||||
|
||||
//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
|
||||
if( sv_portal_staticcollisioncache_cachebrushes.GetBool() )
|
||||
{
|
||||
int iBrush = 0;
|
||||
int iBrushContents;
|
||||
|
||||
CPolyhedron *pTempPolyhedron = ConvertBrushToPolyhedron( iBrush, MASK_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, true );
|
||||
|
||||
while( (pTempPolyhedron != NULL) || (enginetrace->GetBrushInfo( iBrush, iBrushContents, NULL, 0 ) != 0) )
|
||||
{
|
||||
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;
|
||||
pTempPolyhedron = ConvertBrushToPolyhedron( iBrush, MASK_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, true );
|
||||
}
|
||||
|
||||
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
|
||||
if( sv_portal_staticcollisioncache_cachestaticprops.GetBool() )
|
||||
{
|
||||
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;
|
||||
|
||||
if( (pProp->GetCollisionModel() != NULL) && (pProp->GetSolid() != SOLID_NONE) && ((pProp->GetSolidFlags() & FSOLID_NOT_SOLID) == 0) )
|
||||
{
|
||||
vcollide_t *pCollide = modelinfo->GetVCollide( pProp->GetCollisionModel() );
|
||||
if( (pCollide != NULL) && (m_CollideableIndicesMap.IsValidIndex( m_CollideableIndicesMap.Find( pCollide ) ) == false) )
|
||||
{
|
||||
StaticPropPolyhedronCacheInfo_t cacheInfo;
|
||||
cacheInfo.iStartIndex = m_StaticPropPolyhedrons.Count();
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
|
||||
#if defined( DBGFLAG_ASSERT ) && 0
|
||||
CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pTempPolyhedron );
|
||||
AssertMsg( pConvex != NULL, "Conversion from Convex to Polyhedron was irreversable" );
|
||||
if( pConvex )
|
||||
{
|
||||
physcollision->ConvexFree( pConvex );
|
||||
}
|
||||
#endif
|
||||
|
||||
pTempPolyhedron->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cacheInfo.iNumPolyhedrons = m_StaticPropPolyhedrons.Count() - cacheInfo.iStartIndex;
|
||||
Assert( m_CollideableIndicesMap.IsValidIndex( m_CollideableIndicesMap.Find( pCollide ) ) == false );
|
||||
m_CollideableIndicesMap.Insert( pCollide, 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()) || ((sv_portal_staticcollisioncache_cachebrushes.GetBool() == false) && (m_CachedMap.IsEmpty() == false)) );
|
||||
|
||||
if( iBrushNumber < 0 )
|
||||
return NULL;
|
||||
|
||||
if( (iBrushNumber >= m_BrushPolyhedrons.Count()) || (m_BrushPolyhedrons[iBrushNumber] == NULL) )
|
||||
{
|
||||
return ConvertBrushToPolyhedron( iBrushNumber, MASK_ALL, false );
|
||||
}
|
||||
|
||||
return m_BrushPolyhedrons[iBrushNumber];
|
||||
}
|
||||
|
||||
void CStaticCollisionPolyhedronCache::ReleaseBrushPolyhedron( int iBrushNumber, const CPolyhedron *pPolyhedron )
|
||||
{
|
||||
//we only actually do any work here if there was a polyhedron and it's not in our cache.
|
||||
if( pPolyhedron )
|
||||
{
|
||||
Assert( iBrushNumber >= 0 );
|
||||
if( (iBrushNumber >= m_BrushPolyhedrons.Count()) || (pPolyhedron != m_BrushPolyhedrons[iBrushNumber]) )
|
||||
{
|
||||
//not a cached version. Must have generated it on the fly, release it
|
||||
((CPolyhedron *)pPolyhedron)->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CStaticCollisionPolyhedronCache::GetStaticPropPolyhedrons( ICollideable *pStaticProp, const CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize )
|
||||
{
|
||||
if( pStaticProp->GetCollisionModel() == NULL )
|
||||
return 0;
|
||||
|
||||
vcollide_t *pCollide = modelinfo->GetVCollide( pStaticProp->GetCollisionModel() );
|
||||
if( pCollide == NULL )
|
||||
return 0;
|
||||
|
||||
unsigned short iPropIndex = m_CollideableIndicesMap.Find( pCollide );
|
||||
|
||||
int iWrotePolyhedrons = 0;
|
||||
|
||||
if( m_CollideableIndicesMap.IsValidIndex( iPropIndex ) )
|
||||
{
|
||||
StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element( iPropIndex );
|
||||
|
||||
if( cacheInfo.iNumPolyhedrons < iOutputArraySize )
|
||||
{
|
||||
iOutputArraySize = cacheInfo.iNumPolyhedrons;
|
||||
}
|
||||
|
||||
for( int i = cacheInfo.iStartIndex; iWrotePolyhedrons != iOutputArraySize; ++i, ++iWrotePolyhedrons )
|
||||
{
|
||||
pOutputPolyhedronArray[iWrotePolyhedrons] = m_StaticPropPolyhedrons[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (pStaticProp->GetSolid() == SOLID_NONE) || ((pStaticProp->GetSolidFlags() & FSOLID_NOT_SOLID) != 0) )
|
||||
return 0;
|
||||
|
||||
for( int i = 0; i != pCollide->solidCount; ++i )
|
||||
{
|
||||
CPhysConvex *ConvexesArray[1024];
|
||||
int iConvexes = physcollision->GetConvexesUsedInCollideable( pCollide->solids[i], ConvexesArray, 1024 );
|
||||
|
||||
if( iConvexes > iOutputArraySize )
|
||||
{
|
||||
iConvexes = iOutputArraySize;
|
||||
}
|
||||
|
||||
for( int j = 0; j != iConvexes; ++j )
|
||||
{
|
||||
pOutputPolyhedronArray[iWrotePolyhedrons] = physcollision->PolyhedronFromConvex( ConvexesArray[j], false );
|
||||
if( pOutputPolyhedronArray[iWrotePolyhedrons] != NULL )
|
||||
{
|
||||
++iWrotePolyhedrons;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iWrotePolyhedrons;
|
||||
}
|
||||
|
||||
void CStaticCollisionPolyhedronCache::ReleaseStaticPropPolyhedrons( ICollideable *pStaticProp, const CPolyhedron **pPolyhedrons, int iPolyhedronCount )
|
||||
{
|
||||
if( pStaticProp->GetCollisionModel() != NULL )
|
||||
{
|
||||
vcollide_t *pCollide = modelinfo->GetVCollide( pStaticProp->GetCollisionModel() );
|
||||
if( pCollide != NULL )
|
||||
{
|
||||
if( m_CollideableIndicesMap.IsValidIndex( m_CollideableIndicesMap.Find( pCollide ) ) )
|
||||
{
|
||||
//these polyhedrons came from the cache, do nothing.
|
||||
#if defined( DBGFLAG_ASSERT )
|
||||
for( int i = 0; i < iPolyhedronCount; ++i )
|
||||
{
|
||||
Assert( m_StaticPropPolyhedrons.IsValidIndex( m_StaticPropPolyhedrons.Find((CPolyhedron *)pPolyhedrons[i]) ) );
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( DBGFLAG_ASSERT )
|
||||
for( int i = 0; i < iPolyhedronCount; ++i )
|
||||
{
|
||||
Assert( m_StaticPropPolyhedrons.IsValidIndex( m_StaticPropPolyhedrons.Find((CPolyhedron *)pPolyhedrons[i]) ) == false );
|
||||
}
|
||||
#endif
|
||||
|
||||
//if we're down here, the polyhedrons were not in the cache. Release them
|
||||
for( int i = 0; i != iPolyhedronCount; ++i )
|
||||
{
|
||||
((CPolyhedron *)pPolyhedrons[i])->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright © 1996-2006, 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 );
|
||||
void ReleaseBrushPolyhedron( int iBrushNumber, const CPolyhedron *pPolyhedron );
|
||||
|
||||
int GetStaticPropPolyhedrons( ICollideable *pStaticProp, const CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize );
|
||||
void ReleaseStaticPropPolyhedrons( ICollideable *pStaticProp, const CPolyhedron **pPolyhedrons, int iPolyhedronCount );
|
||||
|
||||
void ForceRefreshOnMapLoad( void ) { m_CachedMap.Clear(); };
|
||||
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;
|
||||
};
|
||||
|
||||
CUtlVector<CPolyhedron *> m_StaticPropPolyhedrons;
|
||||
CUtlMap<vcollide_t *, StaticPropPolyhedronCacheInfo_t> m_CollideableIndicesMap;
|
||||
|
||||
|
||||
void Clear( void );
|
||||
void Update( void );
|
||||
};
|
||||
|
||||
extern CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache;
|
||||
@@ -0,0 +1,25 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the stick partner struct props use to keep track of
|
||||
// stick constraints.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "stick_partner.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
StickPartner_t::StickPartner_t()
|
||||
: m_pConstraint( NULL ),
|
||||
m_nLastContactCount( 0 )
|
||||
{}
|
||||
|
||||
StickPartner_t::StickPartner_t( const StickPartner_t& rhs )
|
||||
: m_other( rhs.m_other ),
|
||||
m_pConstraint( rhs.m_pConstraint )
|
||||
{
|
||||
m_contacts = rhs.m_contacts;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Declares the stick partner struct props use to keep track of
|
||||
// stick constraints.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef STICK_PARTNER_H
|
||||
#define STICK_PARTNER_H
|
||||
|
||||
struct StickPartner_t
|
||||
{
|
||||
StickPartner_t();
|
||||
StickPartner_t( const StickPartner_t& );
|
||||
|
||||
CBaseHandle m_other;
|
||||
CUtlVector< Vector > m_contacts; // Where we are touching the other entity
|
||||
IPhysicsConstraint* m_pConstraint; // The constraint holding this object to the other object
|
||||
int m_nLastContactCount; // Number of contacts we had last time we checked
|
||||
};
|
||||
|
||||
#endif // ifndef STICK_PARTNER_H
|
||||
@@ -0,0 +1,669 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "trigger_tractorbeam_shared.h"
|
||||
#include "in_buttons.h"
|
||||
#include "portal_util_shared.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#include "c_portal_player.h"
|
||||
#include "c_paintblob.h"
|
||||
#include "portal_mp_gamerules.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "portal_player.h"
|
||||
#include "npc_portal_turret_floor.h"
|
||||
#include "prop_weightedcube.h"
|
||||
#include "cpaintblob.h"
|
||||
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
extern ConVar sv_gravity;
|
||||
|
||||
ConVar tbeam_air_ctrl_threshold( "tbeam_air_ctrl_threshold", "20", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
|
||||
CTractorBeam_Manager g_TractorBeamManager;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTrigger_TractorBeam::EndTouch( CBaseEntity *pOther )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
if ( !PassesTriggerFilters( pOther ) )
|
||||
return;
|
||||
#endif
|
||||
|
||||
BaseClass::EndTouch( pOther );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
if ( FClassnameIs( pOther, "npc_portal_turret_floor" ) )
|
||||
{
|
||||
CNPC_Portal_FloorTurret *pTurret = assert_cast< CNPC_Portal_FloorTurret* >( pOther );
|
||||
if ( pTurret )
|
||||
{
|
||||
pTurret->OnExitedTractorBeam();
|
||||
}
|
||||
}
|
||||
else if ( UTIL_IsReflectiveCube( pOther ) )
|
||||
{
|
||||
CPropWeightedCube *pCube = assert_cast< CPropWeightedCube* >( pOther );
|
||||
if ( pCube )
|
||||
{
|
||||
pCube->OnExitedTractorBeam();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bTravelingToLinkedTBeam = false;
|
||||
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
CPortal_Player *pPlayer = static_cast< CPortal_Player* >( pOther );
|
||||
if ( pPlayer )
|
||||
{
|
||||
if ( m_hProxyEntity.Get() )
|
||||
{
|
||||
// Lets see if the player is going through a portal
|
||||
Vector vExtents = pPlayer->GetPlayerMaxs() - pPlayer->GetPlayerMins();
|
||||
for ( int i=0; i<CPortal_Base2D_Shared::AllPortals.Count(); ++i )
|
||||
{
|
||||
CPortal_Base2D *pPortal = CPortal_Base2D_Shared::AllPortals[i];
|
||||
if ( pPortal && UTIL_IsBoxIntersectingPortal( pPlayer->WorldSpaceCenter(), vExtents, pPortal ) )
|
||||
{
|
||||
// Is the tbeam is pushing us into the portal that we're traveling through?
|
||||
CPortal_Base2D *pPortal2 = IsReversed() ? m_hProxyEntity->GetSourcePortal() : m_hProxyEntity->GetHitPortal();
|
||||
if ( pPortal == pPortal2 || pPortal->GetLinkedPortal() == pPortal2 )
|
||||
{
|
||||
bTravelingToLinkedTBeam = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pPlayer->SetLeaveTractorBeam( this, bTravelingToLinkedTBeam );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
if ( m_sndPlayerInBeam )
|
||||
{
|
||||
CSoundEnvelopeController::GetController().SoundFadeOut( m_sndPlayerInBeam, 0.5f );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
triggerevent_t event;
|
||||
if ( PhysGetTriggerEvent( &event, this ) && event.pObject && m_pController )
|
||||
{
|
||||
event.pObject->Wake();
|
||||
m_pController->DetachObject( event.pObject );
|
||||
}
|
||||
#else
|
||||
//Warning( "client EndTouch %f %s %s\n", gpGlobals->curtime, prediction->InPrediction() ? "true" : "false", prediction->IsFirstTimePredicted() ? "First" : "Repredict" );
|
||||
|
||||
if( m_pController )
|
||||
{
|
||||
IPhysicsObject *pPhysObject = pOther->VPhysicsGetObject();
|
||||
if( pPhysObject )
|
||||
{
|
||||
m_pController->DetachObject( pPhysObject );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
EntityBeamHistory_t& beamHistory = g_TractorBeamManager.GetHistoryFromEnt( pOther );
|
||||
beamHistory.LeaveBeam( this );
|
||||
#ifdef GAME_DLL
|
||||
if ( beamHistory.m_beams.Count() && beamHistory.m_beams.Head().m_hBeamHandle != NULL )
|
||||
{
|
||||
static_cast< CTrigger_TractorBeam* >( beamHistory.m_beams.Head().m_hBeamHandle.Get() )->ForceAttachEntity( pOther );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CTrigger_TractorBeam::UpdateBeam( const Vector& vStartPoint, const Vector& vEndPoint, float flLinearForce )
|
||||
{
|
||||
CBaseEntity *pOwner = GetOwnerEntity();
|
||||
if ( !pOwner )
|
||||
return;
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
// We want to touch everything...
|
||||
AddSpawnFlags( SF_TRIGGER_ALLOW_ALL );
|
||||
#endif
|
||||
|
||||
if ( flLinearForce < 0 )
|
||||
{
|
||||
flLinearForce = fabs(flLinearForce);
|
||||
#if defined( GAME_DLL )
|
||||
SetAsReversed( true );
|
||||
#endif
|
||||
SetDirection( vEndPoint, vStartPoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined( GAME_DLL )
|
||||
SetAsReversed( false );
|
||||
#endif
|
||||
SetDirection( vStartPoint, vEndPoint );
|
||||
}
|
||||
|
||||
Vector vStart = GetStartPoint();
|
||||
Vector vEnd = GetEndPoint();
|
||||
|
||||
// Get our local vectors
|
||||
Vector vDir = ( vEnd - vStart );
|
||||
float flLength = vDir.NormalizeInPlace();
|
||||
|
||||
QAngle qBeamAngles;
|
||||
VectorAngles( vDir, qBeamAngles );
|
||||
|
||||
// Setup our base (unrotated) bounding box
|
||||
const float flHalfWidth = GetBeamRadius();
|
||||
Vector vMins( 0, -flHalfWidth, -flHalfWidth );
|
||||
Vector vMaxs( flLength, flHalfWidth, flHalfWidth );
|
||||
|
||||
// Clear any current physobject we have assigned to us
|
||||
IPhysicsObject *pObject = VPhysicsGetObject();
|
||||
if ( pObject )
|
||||
{
|
||||
// Untouch everything
|
||||
pObject->RemoveTrigger();
|
||||
|
||||
// Recreate static physics box
|
||||
VPhysicsDestroyObject();
|
||||
}
|
||||
|
||||
// Create our object
|
||||
if ( physenv )
|
||||
{
|
||||
IPhysicsObject *pPhysicsObject = PhysModelCreateOBB( this, vMins, vMaxs, m_vStart, qBeamAngles, true );
|
||||
VPhysicsSetObject( pPhysicsObject );
|
||||
pPhysicsObject->BecomeTrigger();
|
||||
|
||||
// make sure origin and angle are todate to compute world surrounding bounds
|
||||
SetAbsOrigin( m_vStart );
|
||||
SetAbsAngles( qBeamAngles );
|
||||
|
||||
CollisionProp()->SetCollisionBounds( vMins, vMaxs );
|
||||
CollisionProp()->SetSurroundingBoundsType( USE_OBB_COLLISION_BOUNDS );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// need to add the entity to partition tree so we can trace against this on the client
|
||||
Vector vWorldMins, vWorldMaxs;
|
||||
CollisionProp()->WorldSpaceSurroundingBounds( &vWorldMins, &vWorldMaxs );
|
||||
::partition->ElementMoved( CollisionProp()->GetPartitionHandle(), vWorldMins, vWorldMaxs );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
// Setup our direction and force data
|
||||
SetLinearForce( vDir, flLinearForce );
|
||||
SetLinearLimit( flLinearForce * 0.5f );
|
||||
#endif
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
// Now wake everything up
|
||||
WakeTouchingObjects();
|
||||
#endif
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
// Deal with particle changes
|
||||
CreateParticles();
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
RemoveAllBlobsFromBeam();
|
||||
|
||||
m_nLastUpdateFrame = gpGlobals->framecount;
|
||||
}
|
||||
|
||||
|
||||
int CTrigger_TractorBeam::GetLastUpdateFrame() const
|
||||
{
|
||||
return m_nLastUpdateFrame;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CTrigger_TractorBeam::SetDirection( const Vector &vStart, const Vector &vEnd )
|
||||
{
|
||||
// Cache these points for the server / client
|
||||
m_vStart = vStart;
|
||||
m_vEnd = vEnd;
|
||||
}
|
||||
|
||||
Vector CTrigger_TractorBeam::GetForceDirection() const
|
||||
{
|
||||
Vector vecForceDir = GetEndPoint() - GetStartPoint();
|
||||
return vecForceDir.Normalized();
|
||||
}
|
||||
|
||||
ConVar tbeam_allow_player_struggle( "tbeam_allow_player_struggle", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
ConVar tbeam_prevent_players_from_colliding( "tbeam_prevent_players_from_colliding", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Apply the forces to the entity
|
||||
//------------------------------------------------------------------------------
|
||||
IMotionEvent::simresult_e CTrigger_TractorBeam::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
|
||||
{
|
||||
if ( m_bDisabled )
|
||||
return SIM_NOTHING;
|
||||
|
||||
linear.Init();
|
||||
angular.Init();
|
||||
|
||||
// Don't affect things held by the player
|
||||
if ( !pObject || (pObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
|
||||
return SIM_NOTHING;
|
||||
|
||||
// Get our actual game entity
|
||||
CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
|
||||
|
||||
CalculateFrameMovement( pObject, pEntity, deltaTime, linear, angular );
|
||||
|
||||
return SIM_GLOBAL_ACCELERATION;
|
||||
}
|
||||
|
||||
void CTrigger_TractorBeam::CalculateFrameMovement( IPhysicsObject *pObject, CBaseEntity *pEntity, float deltaTime, Vector &linear, AngularImpulse &angular )
|
||||
{
|
||||
// We want to influence entities to rest at our "middles" so that they pass through portals and are predictable.
|
||||
// Here we perturb the position of the entity in question to move it towards the "middle line" of our beam.
|
||||
Vector vWorldPos;
|
||||
Vector vel;
|
||||
AngularImpulse angVel;
|
||||
if ( pObject )
|
||||
{
|
||||
pObject->GetPosition( &vWorldPos, NULL );
|
||||
pObject->GetVelocity( &vel, &angVel );
|
||||
}
|
||||
else
|
||||
{
|
||||
vWorldPos = pEntity->WorldSpaceCenter();
|
||||
vel = pEntity->GetAbsVelocity();
|
||||
angVel.Init();
|
||||
}
|
||||
|
||||
const bool bIsPlayer = pEntity->IsPlayer();
|
||||
|
||||
// Find the direction of travel
|
||||
Vector vecForceDir = GetForceDirection();
|
||||
|
||||
float flLinearScale = 1.0f;
|
||||
Vector vCenteringPos = vWorldPos + vel * deltaTime * 5.0f; // Use the position that we're heading toward to dampen oscelation as we approach the center
|
||||
Vector vMidPoint;
|
||||
float flPathPerc; // How far along the beam we are
|
||||
CalcClosestPointOnLineSegment( vCenteringPos, m_vStart, m_vEnd, vMidPoint, &flPathPerc );
|
||||
|
||||
CBasePlayer *pPlayer = NULL;
|
||||
if ( bIsPlayer )
|
||||
{
|
||||
pPlayer = ToBasePlayer( pEntity );
|
||||
|
||||
// Players need to decelerate when they reach the end of the trigger (unless they're going to go through a portal!)
|
||||
bool bSourcePortal = m_hProxyEntity.Get() ? m_hProxyEntity->GetSourcePortal() != NULL : false;
|
||||
bool bHitPortal = m_hProxyEntity.Get() ? m_hProxyEntity->GetHitPortal() != NULL : false;
|
||||
bool bHeadingTowardsPortal = ( m_bReversed ) ? (bool) bSourcePortal : (bool) bHitPortal;
|
||||
|
||||
// Players also need to decelerate if they'll bump into each other
|
||||
bool bMayBumpIntoOtherPlayerInTbeam = false;
|
||||
float flPlayerBlockingPathPerc = 0.0f;
|
||||
float flBlockedBeamLength = 0.0f;
|
||||
if( tbeam_prevent_players_from_colliding.GetBool() && GameRules()->IsMultiplayer() )
|
||||
{
|
||||
CPortal_Player const* pOtherPlayer = assert_cast<CPortal_Player*>( UTIL_OtherPlayer( pPlayer ) );
|
||||
|
||||
if( pOtherPlayer && pOtherPlayer->GetTractorBeam() == this )
|
||||
{
|
||||
// Compute the percentage along the beam of the other player
|
||||
Vector const vOtherCenteringPos = pOtherPlayer->WorldSpaceCenter() + pOtherPlayer->GetAbsVelocity() * deltaTime * 5.0f;
|
||||
Vector vOtherClosest;
|
||||
float flOtherPathPerc;
|
||||
CalcClosestPointOnLineSegment( vOtherCenteringPos, m_vStart, m_vEnd, vOtherClosest, &flOtherPathPerc );
|
||||
|
||||
// This player might bump into the other if she is behind the other player.
|
||||
// If they're both at the same point in the beam, slow one down temporarily.
|
||||
bMayBumpIntoOtherPlayerInTbeam = flPathPerc == flOtherPathPerc ? pPlayer->entindex() > pOtherPlayer->entindex() : flPathPerc < flOtherPathPerc;
|
||||
|
||||
// Reuse the code below to slow down the player. Essentially, if both players are in
|
||||
// the same beam, we're treating the player that's in front as the end point of the
|
||||
// beam for the player that's behind.
|
||||
if( bMayBumpIntoOtherPlayerInTbeam )
|
||||
{
|
||||
float const PLAYER_DISTANCE_BUFFER = 18.0f; // Approximately how far the gun can extend outside the AABB
|
||||
Vector temp;
|
||||
Vector const end = vOtherClosest - PLAYER_DISTANCE_BUFFER * vecForceDir;
|
||||
CalcClosestPointOnLineSegment( vCenteringPos, m_vStart, end, temp, &flPlayerBlockingPathPerc );
|
||||
flBlockedBeamLength = (end - m_vStart).Length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (bHeadingTowardsPortal == false && !m_bDisablePlayerMove) || bMayBumpIntoOtherPlayerInTbeam )
|
||||
{
|
||||
// Find the player's "width" in the direction travel (different when moving up/down to left/right)
|
||||
Vector mins, maxs;
|
||||
pEntity->CollisionProp()->NormalizedToCollisionSpace( vec3_origin, &mins );
|
||||
pEntity->CollisionProp()->NormalizedToCollisionSpace( vecForceDir, &maxs );
|
||||
|
||||
float flRadius = ( maxs - mins ).Length();
|
||||
|
||||
// Find where we are along the length of the beam
|
||||
float flBeamLength = bMayBumpIntoOtherPlayerInTbeam ? flBlockedBeamLength : VectorLength( (Vector)m_vEnd - (Vector)m_vStart );
|
||||
float flEndPerc = ( flRadius / flBeamLength );
|
||||
|
||||
float flRemapPathPerc = bMayBumpIntoOtherPlayerInTbeam ? flPlayerBlockingPathPerc : flPathPerc;
|
||||
|
||||
// If we're within that last period, we need to scale down the linear force
|
||||
if ( flRemapPathPerc >= flEndPerc )
|
||||
{
|
||||
flLinearScale = RemapValClamped( flRemapPathPerc, (1.0f-(flEndPerc*2.0f)), (1.0f-flEndPerc), 1.0f, 0.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply our constant directional force
|
||||
VectorMA( linear, ((m_linearForce*2.0f) * flLinearScale), vecForceDir, linear );
|
||||
|
||||
// Deal with limiting velocity
|
||||
if ( HasAirDensity() || HasLinearLimit() || HasLinearScale() || HasAngularLimit() || HasAngularScale() )
|
||||
{
|
||||
vel += linear * deltaTime; // account for gravity scale
|
||||
|
||||
Vector unitVel = vel;
|
||||
Vector unitAngVel = angVel;
|
||||
|
||||
float speed = VectorNormalize( unitVel );
|
||||
float angSpeed = VectorNormalize( unitAngVel );
|
||||
|
||||
float speedScale = 0.0;
|
||||
float angSpeedScale = 0.0;
|
||||
|
||||
if ( HasAirDensity() && pObject )
|
||||
{
|
||||
float linearDrag = -0.5 * m_addAirDensity * pObject->CalculateLinearDrag( unitVel ) * deltaTime;
|
||||
if ( linearDrag < -1 )
|
||||
{
|
||||
linearDrag = -1;
|
||||
}
|
||||
speedScale += linearDrag / deltaTime;
|
||||
|
||||
float angDrag = -0.5 * m_addAirDensity * pObject->CalculateAngularDrag( unitAngVel ) * deltaTime;
|
||||
if ( angDrag < -1 )
|
||||
{
|
||||
angDrag = -1;
|
||||
}
|
||||
angSpeedScale += angDrag / deltaTime;
|
||||
}
|
||||
if ( HasLinearLimit() && speed > (m_linearLimit*flLinearScale) )
|
||||
{
|
||||
float flDeltaVel = ((GetLinearLimit()*flLinearScale)- speed) / deltaTime;
|
||||
VectorMA( linear, flDeltaVel, unitVel, linear );
|
||||
}
|
||||
if ( HasAngularLimit() && angSpeed > m_angularLimit )
|
||||
{
|
||||
angular += ((m_angularLimit - angSpeed)/deltaTime) * unitAngVel;
|
||||
}
|
||||
if ( HasLinearScale() )
|
||||
{
|
||||
speedScale = ( (speedScale+1) * m_linearScale ) - 1;
|
||||
}
|
||||
if ( HasAngularScale() )
|
||||
{
|
||||
angSpeedScale = ( (angSpeedScale+1) * m_angularScale ) - 1;
|
||||
}
|
||||
linear += vel * speedScale;
|
||||
angular += angVel * angSpeedScale;
|
||||
}
|
||||
|
||||
// Push us slowly towards the center and keep us there
|
||||
Vector vShoveDir = ( vMidPoint - vCenteringPos );
|
||||
float flDistFromCenter = VectorNormalize( vShoveDir );
|
||||
float flPerc = RemapValClamped( flDistFromCenter, 0, 32.0f, 0.0f, 1.0f );
|
||||
|
||||
if ( pEntity && bIsPlayer )
|
||||
{
|
||||
bool bPlayerAirControlling = false;
|
||||
Vector vForward, vRight, vUp;
|
||||
AngleVectors( pEntity->EyeAngles(), &vForward, &vRight, &vUp );
|
||||
|
||||
Vector vSubDir = vec3_origin;
|
||||
|
||||
float flAirControlMod = 1.0f;
|
||||
|
||||
const CUserCmd *ucmd = pPlayer->GetLastUserCommand();
|
||||
if ( ucmd && !m_bDisablePlayerMove )
|
||||
{
|
||||
vSubDir = ( vForward * ucmd->forwardmove ) + ( vRight * ucmd->sidemove ) + ( vUp * ucmd->upmove );
|
||||
bPlayerAirControlling = ( VectorNormalize( vSubDir ) > tbeam_air_ctrl_threshold.GetFloat() );
|
||||
|
||||
// Don't let them swim upstream
|
||||
float flTravelDot = DotProduct( vSubDir, vecForceDir );
|
||||
|
||||
// This is an old deprecated way of letting the player move through tractor beams
|
||||
if ( tbeam_allow_player_struggle.GetBool() )
|
||||
{
|
||||
if ( flTravelDot < 0.0f )
|
||||
{
|
||||
vSubDir -= flTravelDot * vecForceDir;
|
||||
}
|
||||
|
||||
// If the beam is vertical, don't let players' velocities get limited by looking down
|
||||
if ( fabs( vecForceDir.z ) > DOT_45DEGREE )
|
||||
{
|
||||
VectorNormalize( vSubDir );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clip the movement so they can't swim up or down the stream
|
||||
vSubDir -= flTravelDot * vecForceDir;
|
||||
|
||||
if ( vSubDir.z > 0.0f && vCenteringPos.z - vMidPoint.z > 30.0f )
|
||||
{
|
||||
// Don't let them climb up above the tbeam
|
||||
vSubDir.z = 0.0f;
|
||||
}
|
||||
|
||||
// Find out how much we're going to throttle their movement speed based on their view direction
|
||||
flAirControlMod = RemapValClamped( fabs(flTravelDot), 1.0f, DOT_30DEGREE, 0.0f, 1.0f );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_bDisablePlayerMove )
|
||||
{
|
||||
pPlayer->ForceButtons( IN_DUCK );
|
||||
pPlayer->SetGroundEntity( NULL );
|
||||
}
|
||||
|
||||
if ( bPlayerAirControlling )
|
||||
{
|
||||
if ( tbeam_allow_player_struggle.GetBool() )
|
||||
{
|
||||
linear += vSubDir * 64.0f / deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vShoveAdd = vShoveDir * ( ( 24.0f * flPerc ) / deltaTime ) * flLinearScale;
|
||||
|
||||
// If we don't allow them to struggle against the beam, then limit them
|
||||
if ( tbeam_allow_player_struggle.GetBool() == false )
|
||||
{
|
||||
vShoveAdd += vSubDir * ( ( 84.0f / deltaTime ) * flAirControlMod );
|
||||
}
|
||||
|
||||
linear += vShoveAdd;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
linear += vShoveDir * ( ( 24.0f * flPerc ) / deltaTime ) * flLinearScale;
|
||||
}
|
||||
|
||||
// Set the player up to move this way
|
||||
if ( pEntity->GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
pEntity->SetGroundEntity( NULL );
|
||||
Vector origin = pEntity->GetAbsOrigin();
|
||||
origin.z += 1.0f;
|
||||
pEntity->SetAbsOrigin( origin );
|
||||
pEntity->AddFlag( FL_BASEVELOCITY );
|
||||
pEntity->SetBaseVelocity( Vector( 0, 0, 1000 ) );
|
||||
linear.z += 5000.0f;
|
||||
}
|
||||
|
||||
#if defined( DEBUG_MOTION_CONTROLLERS )
|
||||
((CBasePlayer *)pEntity)->m_Debug_LinearAccel = linear;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
linear += vShoveDir * ( ( 16.0f * flPerc ) / deltaTime );
|
||||
linear.z -= (m_gravityScale-1) * sv_gravity.GetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float CTrigger_TractorBeam::GetLinearLimit()
|
||||
{
|
||||
if ( m_linearLimitTime == 0.0f )
|
||||
return m_linearLimit;
|
||||
|
||||
float dt = gpGlobals->curtime - m_linearLimitStartTime;
|
||||
if ( dt >= m_linearLimitTime )
|
||||
{
|
||||
m_linearLimitTime = 0.0;
|
||||
return m_linearLimit;
|
||||
}
|
||||
|
||||
dt /= m_linearLimitTime;
|
||||
float flLimit = RemapVal( dt, 0.0f, 1.0f, m_linearLimitStart, m_linearLimit );
|
||||
return flLimit;
|
||||
}
|
||||
|
||||
|
||||
struct ShouldDeleteBlob_t : std::unary_function< CPaintBlob*, bool >
|
||||
{
|
||||
inline bool operator()( const CPaintBlob* pBlob ) const
|
||||
{
|
||||
return pBlob->ShouldDeleteThis();
|
||||
}
|
||||
};
|
||||
|
||||
// Remove dead blobs from the list
|
||||
void CTrigger_TractorBeam::RemoveDeadBlobs()
|
||||
{
|
||||
if ( m_blobs.Count() == 0 )
|
||||
return;
|
||||
|
||||
CPaintBlob** begin = GetBegin( m_blobs );
|
||||
CPaintBlob** end = GetEnd( m_blobs );
|
||||
CPaintBlob** middle = std::partition( begin, end, ShouldDeleteBlob_t() );
|
||||
|
||||
int numRemoved = middle - begin;
|
||||
m_blobs.RemoveMultipleFromHead( numRemoved );
|
||||
}
|
||||
|
||||
|
||||
struct IsBlobInSameBeam_t : std::unary_function< CPaintBlob*, bool >
|
||||
{
|
||||
IsBlobInSameBeam_t( CTrigger_TractorBeam* pBeam ) : m_pBeam( pBeam )
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator()( const CPaintBlob* pBlob ) const
|
||||
{
|
||||
return ( m_pBeam == pBlob->GetCurrentBeam() );
|
||||
}
|
||||
|
||||
CTrigger_TractorBeam *m_pBeam;
|
||||
};
|
||||
|
||||
|
||||
// remove blobs that change to different beams
|
||||
void CTrigger_TractorBeam::RemoveChangedBeamBlobs()
|
||||
{
|
||||
if ( m_blobs.Count() == 0 )
|
||||
return;
|
||||
|
||||
CPaintBlob** begin = GetBegin( m_blobs );
|
||||
CPaintBlob** end = GetEnd( m_blobs );
|
||||
CPaintBlob** middle = std::partition( begin, end, IsBlobInSameBeam_t( this ) );
|
||||
|
||||
int numRemoved = end - middle;
|
||||
m_blobs.RemoveMultipleFromTail( numRemoved );
|
||||
}
|
||||
|
||||
|
||||
void CTrigger_TractorBeam::RemoveAllBlobsFromBeam()
|
||||
{
|
||||
for ( int i=0; i<m_blobs.Count(); ++i )
|
||||
{
|
||||
m_blobs[i]->SetTractorBeam( NULL );
|
||||
}
|
||||
|
||||
m_blobs.Purge();
|
||||
}
|
||||
|
||||
|
||||
void CTrigger_TractorBeam_Shared::RemoveDeadBlobsFromBeams()
|
||||
{
|
||||
for ( int i = 0; i < ITriggerTractorBeamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
static_cast< CTrigger_TractorBeam* >( ITriggerTractorBeamAutoList::AutoList()[i] )->RemoveDeadBlobs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CTrigger_TractorBeam_Shared::RemoveBlobsFromPreviousBeams()
|
||||
{
|
||||
for ( int i = 0; i < ITriggerTractorBeamAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
static_cast< CTrigger_TractorBeam* >( ITriggerTractorBeamAutoList::AutoList()[i] )->RemoveChangedBeamBlobs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EntityBeamHistory_t::IsDifferentBeam( CTrigger_TractorBeam* pNewBeam )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
// run the old behavior if PC client is playing against PS3 server
|
||||
if ( PortalMPGameRules() && !PortalMPGameRules()->IsClientCrossplayingPCvsPC() )
|
||||
{
|
||||
if ( m_beams.Count() == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return m_beams.Head().m_hBeamHandle != pNewBeam;
|
||||
}
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
int nLastUpdateFrame = pNewBeam->GetLastUpdateFrame();
|
||||
for ( int i=0; i<m_beams.Count(); ++i )
|
||||
{
|
||||
if ( m_beams[i].m_hBeamHandle == pNewBeam )
|
||||
{
|
||||
return m_beams[i].m_nLastFrameUpdate != nLastUpdateFrame;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef TRIGGER_TRACTORBEAM_SHARED_H
|
||||
#define TRIGGER_TRACTORBEAM_SHARED_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_trigger_tractorbeam.h"
|
||||
#else
|
||||
#include "trigger_tractorbeam.h"
|
||||
#endif
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "igamesystem.h"
|
||||
|
||||
class CTrigger_TractorBeam_Shared //defined as a class to make intellisense more intelligent
|
||||
{
|
||||
public:
|
||||
|
||||
static void RemoveDeadBlobsFromBeams();
|
||||
static void RemoveBlobsFromPreviousBeams();
|
||||
};
|
||||
|
||||
|
||||
struct BeamInfo_t
|
||||
{
|
||||
BeamInfo_t() : m_hBeamHandle( NULL ), m_nLastFrameUpdate( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
BeamInfo_t( const BeamInfo_t& copy )
|
||||
{
|
||||
m_hBeamHandle = copy.m_hBeamHandle;
|
||||
m_nLastFrameUpdate = copy.m_nLastFrameUpdate;
|
||||
}
|
||||
|
||||
EHANDLE m_hBeamHandle;
|
||||
int m_nLastFrameUpdate;
|
||||
};
|
||||
|
||||
|
||||
struct EntityBeamHistory_t
|
||||
{
|
||||
EntityBeamHistory_t()
|
||||
{
|
||||
m_beams.Purge();
|
||||
}
|
||||
|
||||
EntityBeamHistory_t( const EntityBeamHistory_t& copy )
|
||||
{
|
||||
m_beams.CopyArray( copy.m_beams.Base(), copy.m_beams.Count() );
|
||||
}
|
||||
|
||||
~EntityBeamHistory_t()
|
||||
{
|
||||
m_beams.Purge();
|
||||
}
|
||||
|
||||
bool IsDifferentBeam( CTrigger_TractorBeam* pNewBeam );
|
||||
|
||||
void UpdateBeam( CTrigger_TractorBeam *pBeam )
|
||||
{
|
||||
// remove the existing beam in the list
|
||||
LeaveBeam( pBeam );
|
||||
|
||||
// if we have full list, remove the last one before we add the new one.
|
||||
if ( m_beams.Count() == 6 )
|
||||
{
|
||||
m_beams.Remove( 5 );
|
||||
}
|
||||
|
||||
// add new beam to head to be the current
|
||||
m_beams.AddToHead();
|
||||
m_beams.Head().m_hBeamHandle = pBeam;
|
||||
m_beams.Head().m_nLastFrameUpdate = pBeam->GetLastUpdateFrame();
|
||||
}
|
||||
|
||||
void LeaveBeam( CTrigger_TractorBeam *pBeam )
|
||||
{
|
||||
// remove the existing beam in the list
|
||||
for ( int i=0; i<m_beams.Count(); ++i )
|
||||
{
|
||||
if ( m_beams[i].m_hBeamHandle == pBeam )
|
||||
{
|
||||
m_beams.Remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearAllBeams()
|
||||
{
|
||||
m_beams.Purge();
|
||||
}
|
||||
|
||||
CUtlVectorFixed< BeamInfo_t, 6 > m_beams;
|
||||
};
|
||||
|
||||
class CTractorBeam_Manager : public CAutoGameSystemPerFrame
|
||||
{
|
||||
public:
|
||||
CTractorBeam_Manager() : m_flLastUpdateTime(0.0)
|
||||
{
|
||||
m_entityBeamHistories.SetLessFunc( DefLessFunc( EHANDLE ) );
|
||||
}
|
||||
|
||||
~CTractorBeam_Manager()
|
||||
{
|
||||
m_entityBeamHistories.Purge();
|
||||
}
|
||||
|
||||
virtual void LevelShutdownPostEntity()
|
||||
{
|
||||
m_entityBeamHistories.Purge();
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void PreClientUpdate()
|
||||
#else
|
||||
virtual void Update( float frametime )
|
||||
#endif
|
||||
{
|
||||
if ( gpGlobals->curtime - m_flLastUpdateTime > 1.f )
|
||||
{
|
||||
m_flLastUpdateTime = gpGlobals->curtime;
|
||||
|
||||
// remove NULL entities
|
||||
CUtlVector< EHANDLE > removingHandles;
|
||||
for ( unsigned int i=0; i<m_entityBeamHistories.Count(); ++i )
|
||||
{
|
||||
const EHANDLE& hHandle = m_entityBeamHistories.Key( i );
|
||||
if ( hHandle == NULL )
|
||||
{
|
||||
removingHandles.AddToTail( hHandle );
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i=0; i<removingHandles.Count(); ++i )
|
||||
{
|
||||
m_entityBeamHistories.Remove( removingHandles[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityBeamHistory_t& GetHistoryFromEnt( const EHANDLE& hEntHandle )
|
||||
{
|
||||
unsigned short index = m_entityBeamHistories.Find( hEntHandle );
|
||||
if ( index == m_entityBeamHistories.InvalidIndex() )
|
||||
{
|
||||
index = m_entityBeamHistories.Insert( hEntHandle );
|
||||
}
|
||||
|
||||
return m_entityBeamHistories.Element( index );
|
||||
}
|
||||
|
||||
void RemoveHistoryFromEnt( const EHANDLE& hEntHandle )
|
||||
{
|
||||
m_entityBeamHistories.Remove( hEntHandle );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CUtlMap< EHANDLE, EntityBeamHistory_t > m_entityBeamHistories;
|
||||
double m_flLastUpdateTime;
|
||||
};
|
||||
|
||||
extern CTractorBeam_Manager g_TractorBeamManager;
|
||||
|
||||
#endif //TRIGGRE_TRACTORBEAM_SHARED_H
|
||||
@@ -0,0 +1,487 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Shared variables, etc. for the paint gun.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "paint_color_manager.h"
|
||||
#include "shot_manipulator.h"
|
||||
#include "in_buttons.h"
|
||||
#include "debugoverlay_shared.h"
|
||||
#include "weapon_paintgun_shared.h"
|
||||
#include "paint_sprayer_shared.h"
|
||||
#include "paint_stream_shared.h"
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_weapon_paintgun.h"
|
||||
#include "c_portal_player.h"
|
||||
#include "igameevents.h"
|
||||
#else
|
||||
#include "weapon_paintgun.h"
|
||||
#include "portal_player.h"
|
||||
#include "paint_database.h"
|
||||
#include "portal_base2d.h"
|
||||
#include "prop_portal_shared.h"
|
||||
#include "env_speaker.h"
|
||||
#include "rumble_shared.h"
|
||||
#include "paint_database.h"
|
||||
|
||||
//ConVar sv_paint_erase_range("sv_paint_erase_range", "2000", FCVAR_CHEAT);
|
||||
//ConVar sv_num_erase_ray("sv_num_erase_ray", "10", FCVAR_CHEAT, "number of ray that shoots out per shot");
|
||||
//ConVar sv_debug_suck_erase("sv_debug_suck_erase", "0", FCVAR_CHEAT);
|
||||
|
||||
extern void Paint( CBaseEntity* pEntity, const Vector& pos, uint8 colorIndex, int nPainted );
|
||||
extern CPaintDatabase PaintDatabase;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define paintgun_blobs_spread_radius 0.f //ConVar paintgun_blobs_spread_radius( "paintgun_blobs_spread_radius", "0.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The starting radius of the spread of the paint blobs from the gun" );
|
||||
#define paintgun_blobs_spread_angle 10.f //ConVar paintgun_blobs_spread_angle( "paintgun_blobs_spread_angle", "10.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The spread (in degrees) of the paint blobs from the gun" );
|
||||
#define paintgun_blobs_per_second 40.f //ConVar paintgun_blobs_per_second( "paintgun_blobs_per_second", "40.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "Number of blobs shot out of the paint gun per second" );
|
||||
#define paintgun_blobs_min_speed 950.f //ConVar paintgun_blobs_min_speed( "paintgun_blobs_min_speed", "950.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The min speed of the blobs shot out of the paint gun" );
|
||||
#define paintgun_blobs_max_speed 1050.f //ConVar paintgun_blobs_max_speed( "paintgun_blobs_max_speed", "1050.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The max speed of the blobs shot out of the paint gun" );
|
||||
#define paintgun_shoot_position_trace_for_wall 1 //ConVar paintgun_shoot_position_trace_for_wall( "paintgun_shoot_position_trace_for_wall", "1", FCVAR_REPLICATED, "If the paint gun shooting position should test if it is inside a wall" );
|
||||
|
||||
#define paintgun_blobs_streak_percent 10.f //ConVar paintgun_blobs_streak_percent( "paintgun_blobs_streak_percent", "10.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
#define paintgun_blobs_min_streak_time 0.1f //ConVar paintgun_blobs_min_streak_time( "paintgun_blobs_min_streak_time", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
#define paintgun_blobs_max_streak_time 0.5f //ConVar paintgun_blobs_max_streak_time( "paintgun_blobs_max_streak_time", "0.5f", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
#define paintgun_blobs_min_streak_speed_dampen 4500.f //ConVar paintgun_blobs_min_streak_speed_dampen( "paintgun_blobs_min_streak_speed_dampen", "4500.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
#define paintgun_blobs_max_streak_speed_dampen 5500.f //ConVar paintgun_blobs_max_streak_speed_dampen( "paintgun_blobs_max_streak_speed_dampen", "5500.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||
|
||||
#define paintgun_max_ammo 60 //ConVar paintgun_max_ammo( "paintgun_max_ammo", "60", FCVAR_REPLICATED, "The maximum amount of paint ammo allowed." );
|
||||
#define paintgun_ammo_type 0 //ConVar paintgun_ammo_type( "paintgun_ammo_type", "0", FCVAR_REPLICATED, "Type of paint ammo. 0: No ammo, 1: Global ammo per-gun, 2: Ammo per-paint type" );
|
||||
|
||||
|
||||
|
||||
acttable_t CWeaponPaintGun::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 },
|
||||
{ ACT_MP_RUN_SPEEDPAINT, ACT_MP_RUN_SPEEDPAINT_PRIMARY, false },
|
||||
{ ACT_MP_DROWNING_PRIMARY, ACT_MP_DROWNING_PRIMARY, false },
|
||||
{ ACT_MP_LONG_FALL, ACT_MP_LONG_FALL_PRIMARY, false },
|
||||
{ ACT_MP_TRACTORBEAM_FLOAT, ACT_MP_TRACTORBEAM_FLOAT_PRIMARY, false },
|
||||
{ ACT_MP_DEATH_CRUSH, ACT_MP_DEATH_CRUSH_PRIMARY, false },
|
||||
};
|
||||
|
||||
IMPLEMENT_ACTTABLE(CWeaponPaintGun);
|
||||
|
||||
void CWeaponPaintGun::ItemPostFrame()
|
||||
{
|
||||
bool bWasFiringPaint = m_bFiringPaint;
|
||||
bool bWasFiringErase = m_bFiringErase;
|
||||
|
||||
// Only the player fires this way so we can cast
|
||||
CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
|
||||
if ( pPlayer == NULL )
|
||||
return;
|
||||
|
||||
// The paint clearing secondary function can always be used
|
||||
if( paintgun_ammo_type != PAINT_AMMO_NONE &&
|
||||
(pPlayer->m_nButtons & IN_ATTACK2) != 0 )
|
||||
{
|
||||
// Attack!
|
||||
SecondaryAttack();
|
||||
}
|
||||
else if( pPlayer->GetUseEntity() == NULL )
|
||||
{
|
||||
BaseClass::ItemPostFrame();
|
||||
}
|
||||
|
||||
// Was shooting neither and is now shooting either
|
||||
if( !bWasFiringPaint && !bWasFiringErase &&
|
||||
( m_bFiringPaint || m_bFiringErase ) )
|
||||
{
|
||||
#if !defined (CLIENT_DLL)
|
||||
StartShootingSound();
|
||||
#else
|
||||
|
||||
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
#endif
|
||||
}
|
||||
// Was shooting either and now is shooting neither
|
||||
else if( ( bWasFiringPaint || bWasFiringErase ) &&
|
||||
( !m_bFiringPaint && !m_bFiringErase ) )
|
||||
{
|
||||
#if !defined (CLIENT_DLL)
|
||||
StopShootingSound();
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CWeaponPaintGun::PrimaryAttack()
|
||||
{
|
||||
bool bHasSelectedColor = false;
|
||||
#if !defined (CLIENT_DLL)
|
||||
bHasSelectedColor = HasPaintPower( (PaintPowerType)m_nCurrentColor.Get() );
|
||||
#else // CLIENT_DLL
|
||||
bHasSelectedColor = HasPaintPower( (PaintPowerType)m_nCurrentColor );
|
||||
#endif
|
||||
|
||||
// Don't shoot if we dont have the selected color or any color at all
|
||||
if( !HasAnyPaintPower() || !bHasSelectedColor || !HasPaintAmmo( m_nCurrentColor ) )
|
||||
{
|
||||
m_bFiringPaint = m_bFiringErase = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if !defined (CLIENT_DLL)
|
||||
SprayPaint( gpGlobals->frametime, m_nCurrentColor );
|
||||
if( !m_bFiringPaint )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "player_painted" );
|
||||
if ( event )
|
||||
{
|
||||
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
assert( pPlayer );
|
||||
|
||||
event->SetInt("userid", pPlayer->GetUserID() );
|
||||
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
#else // CLIENT_DLL
|
||||
StartHoseEffect();
|
||||
//SprayPaint( gpGlobals->frametime, static_cast<PaintPowerType>( m_nCurrentColor ) );
|
||||
#endif //CLIENT_DLL
|
||||
|
||||
|
||||
m_bFiringPaint = true;
|
||||
m_bFiringErase = false;
|
||||
}
|
||||
|
||||
|
||||
void CWeaponPaintGun::SecondaryAttack()
|
||||
{
|
||||
if( paintgun_ammo_type == PAINT_AMMO_NONE )
|
||||
{
|
||||
# ifdef CLIENT_DLL
|
||||
StartHoseEffect();
|
||||
# else
|
||||
if( !m_bFiringErase )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "player_erased" );
|
||||
if ( event )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
assert( pPlayer );
|
||||
|
||||
event->SetInt("userid", pPlayer->GetUserID() );
|
||||
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
m_bFiringPaint = false;
|
||||
m_bFiringErase = true;
|
||||
|
||||
SprayPaint( gpGlobals->frametime, NO_POWER );
|
||||
# endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetAmmo();
|
||||
#ifdef GAME_DLL
|
||||
PaintDatabase.RemoveAllPaint();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CWeaponPaintGun::HasPaintPower( PaintPowerType nIndex )
|
||||
{
|
||||
return m_bHasPaint[nIndex];
|
||||
}
|
||||
|
||||
|
||||
bool CWeaponPaintGun::HasAnyPaintPower()
|
||||
{
|
||||
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
|
||||
{
|
||||
if( HasPaintPower( (PaintPowerType)i ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CWeaponPaintGun::WeaponIdle()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
StopHoseEffect();
|
||||
#else
|
||||
if( m_bFiringPaint || m_bFiringErase )
|
||||
{
|
||||
StopShootingSound();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_bFiringPaint = m_bFiringErase = false;
|
||||
m_flAccumulatedTime = 1.0f/paintgun_blobs_per_second;
|
||||
#ifdef CLIENT_DLL
|
||||
#endif
|
||||
|
||||
m_nBlobRandomSeed = 0;
|
||||
|
||||
BaseClass::WeaponIdle();
|
||||
}
|
||||
|
||||
|
||||
bool CWeaponPaintGun::Holster( CBaseCombatWeapon *pSwitchingTo )
|
||||
{
|
||||
m_bFiringPaint = m_bFiringErase = false;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
ChangeRenderColor();
|
||||
StopHoseEffect();
|
||||
#else
|
||||
StopShootingSound();
|
||||
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "holstered_paintgun" );
|
||||
if ( event )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if( pPlayer )
|
||||
{
|
||||
event->SetInt("userid", pPlayer->GetUserID() );
|
||||
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return BaseClass::Holster( pSwitchingTo );
|
||||
}
|
||||
|
||||
|
||||
void CWeaponPaintGun::Drop( const Vector &vecVelocity )
|
||||
{
|
||||
m_bFiringPaint = m_bFiringErase = false;
|
||||
|
||||
Color color = MapPowerToVisualColor( m_nCurrentColor );
|
||||
if ( !HasAnyPaintPower() )
|
||||
color = MapPowerToVisualColor( NO_POWER );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
StopHoseEffect();
|
||||
#else
|
||||
StopShootingSound();
|
||||
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "dropped_paintgun" );
|
||||
if ( event )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if( pPlayer )
|
||||
{
|
||||
event->SetInt("userid", pPlayer->GetUserID() );
|
||||
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SetRenderColor( color.r(), color.g(), color.b() );
|
||||
|
||||
BaseClass::Drop( vecVelocity );
|
||||
}
|
||||
|
||||
|
||||
bool CWeaponPaintGun::Deploy()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
ChangeRenderColor();
|
||||
#endif
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "deployed_paintgun" );
|
||||
if ( event )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if( pPlayer )
|
||||
{
|
||||
event->SetInt("userid", pPlayer->GetUserID() );
|
||||
event->SetInt("paintcount", GetPaintCount() );
|
||||
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
#endif //Only on server
|
||||
|
||||
return BaseClass::Deploy();
|
||||
}
|
||||
|
||||
|
||||
void CWeaponPaintGun::SetSubType( int iType )
|
||||
{
|
||||
m_iSubType = iType;
|
||||
m_nCurrentColor = iType;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
ChangeRenderColor();
|
||||
#else
|
||||
EmitSound( "Player.WeaponSelected" );
|
||||
#endif
|
||||
|
||||
BaseClass::SetSubType( iType );
|
||||
}
|
||||
|
||||
|
||||
PaintPowerType CWeaponPaintGun::GetCurrentPaint()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
return (PaintPowerType)( m_nCurrentColor );
|
||||
#else //!CLIENT_DLL
|
||||
return (PaintPowerType)( m_nCurrentColor.Get() );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//Paint Ammo!
|
||||
bool CWeaponPaintGun::HasPaintAmmo( unsigned paintType ) const
|
||||
{
|
||||
switch( paintgun_ammo_type )
|
||||
{
|
||||
case PAINT_AMMO_NONE:
|
||||
return true;
|
||||
|
||||
case PAINT_AMMO_GLOBAL:
|
||||
return m_nPaintAmmo > 0;
|
||||
|
||||
case PAINT_AMMO_PER_TYPE:
|
||||
Assert( paintType < PAINT_POWER_TYPE_COUNT );
|
||||
return m_PaintAmmoPerType[MIN( paintType, PAINT_POWER_TYPE_COUNT )] > 0;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CWeaponPaintGun::DecrementPaintAmmo( unsigned paintType )
|
||||
{
|
||||
switch( paintgun_ammo_type )
|
||||
{
|
||||
case PAINT_AMMO_GLOBAL:
|
||||
--m_nPaintAmmo;
|
||||
break;
|
||||
|
||||
case PAINT_AMMO_PER_TYPE:
|
||||
const int index = MIN( paintType, PAINT_POWER_TYPE_COUNT );
|
||||
m_PaintAmmoPerType.Set( index, m_PaintAmmoPerType[index] - 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CWeaponPaintGun::ResetAmmo()
|
||||
{
|
||||
m_nPaintAmmo = paintgun_max_ammo;
|
||||
|
||||
const int maxAmmo = paintgun_max_ammo;
|
||||
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
|
||||
{
|
||||
m_PaintAmmoPerType.Set( i, maxAmmo );
|
||||
}
|
||||
}
|
||||
|
||||
void CWeaponPaintGun::SprayPaint( float flDeltaTime, int paintType )
|
||||
{
|
||||
if( flDeltaTime <= 0.0f )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CPortal_Player *pOwner = ToPortalPlayer( GetOwner() );
|
||||
if ( pOwner == NULL )
|
||||
return;
|
||||
|
||||
CPaintStream *pPaintStream = assert_cast< CPaintStream* >( m_hPaintStream.Get( paintType ).Get() );
|
||||
if ( !pPaintStream )
|
||||
return;
|
||||
|
||||
m_flAccumulatedTime += flDeltaTime;
|
||||
|
||||
Vector vecEyePosition = pOwner->EyePosition();
|
||||
Vector vecVelocity = pOwner->GetAbsVelocity();
|
||||
Vector vecAimDir = pOwner->GetAutoaimVector( 0 );
|
||||
Vector vecForwardVelocity = vecVelocity.Normalized() * DotProduct( vecVelocity, vecAimDir );
|
||||
Vector vecBlobFirePos = pOwner->GetPaintGunShootPosition();
|
||||
|
||||
if( paintgun_shoot_position_trace_for_wall )
|
||||
{
|
||||
// Because the muzzle is so long, it can stick through a wall if the player is right up against it.
|
||||
// Make sure to adjust the shoot position in this condition by tracing a line between the eye point and the end of the muzzle.
|
||||
trace_t trace;
|
||||
Ray_t muzzleRay;
|
||||
muzzleRay.Init( vecEyePosition, vecBlobFirePos );
|
||||
CTraceFilterSimple traceFilter( pOwner, COLLISION_GROUP_NONE );
|
||||
UTIL_TraceRay( muzzleRay, MASK_SOLID, &traceFilter, &trace );
|
||||
|
||||
//Check if there is a portal between the player's eye and the muzzle of the paint gun
|
||||
CPortal_Base2D *pInPortal = NULL;
|
||||
CPortal_Base2D *pOutPortal = NULL;
|
||||
if( UTIL_DidTraceTouchPortals( muzzleRay, trace, &pInPortal, &pOutPortal ) )
|
||||
{
|
||||
Vector vecPortalForward;
|
||||
AngleVectors( pInPortal->GetAbsAngles(), &vecPortalForward );
|
||||
Vector vecTraceDir = vecBlobFirePos - vecEyePosition;
|
||||
vecTraceDir.NormalizeInPlace();
|
||||
|
||||
if( DotProduct( vecPortalForward, vecTraceDir ) < 0 )
|
||||
{
|
||||
UTIL_Portal_PointTransform( pInPortal->MatrixThisToLinked(), trace.endpos, vecBlobFirePos );
|
||||
UTIL_Portal_VectorTransform( pInPortal->MatrixThisToLinked(), vecAimDir, vecAimDir );
|
||||
}
|
||||
}
|
||||
else if ( trace.fraction < 1.0 && ( !trace.m_pEnt || trace.m_pEnt->m_takedamage == DAMAGE_NO ) )
|
||||
{
|
||||
// there is something between the eye and the end of the muzzle, most likely a wall
|
||||
// Move the muzzle position to the end position of the trace so that the wall gets painted
|
||||
vecBlobFirePos = trace.endpos;
|
||||
}
|
||||
//vecBlobFirePos = trace.endpos;
|
||||
}
|
||||
|
||||
const float flBlobPerSecond = 1.0f/paintgun_blobs_per_second;
|
||||
while ( m_flAccumulatedTime >= flBlobPerSecond && HasPaintAmmo( paintType ) )
|
||||
{
|
||||
m_flAccumulatedTime -= flBlobPerSecond;
|
||||
CPaintBlob *pBlob = FirePaintBlob( vecBlobFirePos,
|
||||
vecBlobFirePos,
|
||||
vecForwardVelocity,
|
||||
vecAimDir,
|
||||
paintType,
|
||||
paintgun_blobs_spread_radius,
|
||||
paintgun_blobs_spread_angle,
|
||||
paintgun_blobs_min_speed,
|
||||
paintgun_blobs_max_speed,
|
||||
paintgun_blobs_streak_percent,
|
||||
paintgun_blobs_min_streak_time,
|
||||
paintgun_blobs_max_streak_time,
|
||||
paintgun_blobs_min_streak_speed_dampen,
|
||||
paintgun_blobs_max_streak_speed_dampen,
|
||||
false,
|
||||
false,
|
||||
pPaintStream,
|
||||
m_nBlobRandomSeed );
|
||||
|
||||
pPaintStream->AddPaintBlob( pBlob );
|
||||
|
||||
++m_nBlobRandomSeed;
|
||||
DecrementPaintAmmo( paintType );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Shared variables, etc. for the paint gun.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef WEAPON_PAINTGUN_SHARED_H
|
||||
#define WEAPON_PAINTGUN_SHARED_H
|
||||
|
||||
enum PaintAmmoType
|
||||
{
|
||||
PAINT_AMMO_NONE,
|
||||
PAINT_AMMO_GLOBAL,
|
||||
PAINT_AMMO_PER_TYPE
|
||||
};
|
||||
|
||||
#endif // ifndef WEAPON_PAINTGUN_SHARED_H
|
||||
@@ -0,0 +1,461 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
#include "vgui_int.h"
|
||||
#include "model_types.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_ALIASED( weapon_portal_base, WeaponPortalBase );
|
||||
|
||||
|
||||
#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 g_pGameRules->IsMultiplayer();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// opt out of the model fast path for now. Since this model is "drawn" when in first person
|
||||
// and not looking through a portal and drawing is aborted in DrawModel() the fast path can
|
||||
// skip this and cause an extra copy of this entity to be visible
|
||||
IClientModelRenderable* CWeaponPortalBase::GetClientModelRenderable()
|
||||
{
|
||||
// NOTE: This should work but doesn't. It makes the object invisible in the portal pass
|
||||
// I suspect the IsRenderingPortal() test isn't getting re-entered while building the list for the frame
|
||||
// but I haven't tracked it down. For now I'll just have weapons opt out of the fast path and render
|
||||
// correctly at lower performance.
|
||||
#if 0
|
||||
C_BasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( pOwner && C_BasePlayer::IsLocalPlayer( pOwner ) )
|
||||
{
|
||||
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pOwner );
|
||||
if ( !g_pPortalRender->IsRenderingPortal() && !pOwner->ShouldDrawLocalPlayer() )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int CWeaponPortalBase::DrawModel( int flags, const RenderableInstance_t &instance )
|
||||
{
|
||||
if ( !m_bReadyToDraw )
|
||||
return 0;
|
||||
|
||||
C_BasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( pOwner && C_BasePlayer::IsLocalPlayer( pOwner ) )
|
||||
{
|
||||
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pOwner );
|
||||
if ( !g_pPortalRender->IsRenderingPortal() && !pOwner->ShouldDrawLocalPlayer() && !VGui_IsSplitScreen() )
|
||||
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, instance );
|
||||
|
||||
if( bChangeModelBack )
|
||||
SetModelIndex( iOriginalIndex );
|
||||
|
||||
return iRetVal;
|
||||
}
|
||||
|
||||
bool CWeaponPortalBase::ShouldPredict()
|
||||
{
|
||||
if ( C_BasePlayer::IsLocalPlayer( GetOwner() ) )
|
||||
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 = GetHud().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 = HudIcons().GetIcon( "arrow" );
|
||||
|
||||
crosshair->SetCrosshair( pArrow, GetHud().m_clrNormal );
|
||||
return;
|
||||
}
|
||||
|
||||
// Find out if this weapon's auto-aimed onto a target
|
||||
bool bOnTarget = ( m_iState == WEAPON_IS_ACTIVE ) && player->m_fOnTarget;
|
||||
|
||||
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_flPlayerDamage = 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
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
|
||||
#include "weapons_portal.h"
|
||||
|
||||
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 int 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 );
|
||||
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_FORCE_TRANSITION; }
|
||||
|
||||
public:
|
||||
#if defined( CLIENT_DLL )
|
||||
|
||||
virtual IClientModelRenderable* GetClientModelRenderable();
|
||||
virtual int DrawModel( int flags, const RenderableInstance_t &instance );
|
||||
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
|
||||
@@ -0,0 +1,509 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "weapon_portalbasecombatweapon.h"
|
||||
#include "in_buttons.h"
|
||||
|
||||
#include "portal_player_shared.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#include "c_portal_player.h"
|
||||
#else
|
||||
#include "vphysics/constraints.h"
|
||||
#include "gameweaponmanager.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( BasePortalCombatWeapon , DT_BasePortalCombatWeapon )
|
||||
LINK_ENTITY_TO_CLASS_ALIASED( baseportalcombatweapon, 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 ),
|
||||
DEFINE_FIELD( m_flNextRepeatPrimaryAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flNextRepeatSecondaryAttack, FIELD_TIME ),
|
||||
END_DATADESC()
|
||||
|
||||
#else
|
||||
|
||||
BEGIN_PREDICTION_DATA( CBasePortalCombatWeapon )
|
||||
DEFINE_FIELD( m_flNextRepeatPrimaryAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flNextRepeatSecondaryAttack, FIELD_TIME ),
|
||||
END_PREDICTION_DATA()
|
||||
|
||||
#endif
|
||||
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
}
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
|
||||
extern float g_lateralBob;
|
||||
extern float g_verticalBob;
|
||||
|
||||
#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
|
||||
|
||||
#if !defined( PORTAL2 )
|
||||
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_CHEAT );
|
||||
static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_CHEAT );
|
||||
static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_CHEAT );
|
||||
static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_CHEAT );
|
||||
static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_CHEAT );
|
||||
static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_CHEAT );
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
}
|
||||
|
||||
// Note: we use paint code for this so when player move on speed paint, gun bob faster (Bank)
|
||||
//Find the speed of the player
|
||||
float speed = player->GetLocalVelocity().Length();
|
||||
|
||||
speed = clamp( speed, -player->MaxSpeed(), player->MaxSpeed() );
|
||||
|
||||
float bob_offset = RemapVal( speed, 0, player->MaxSpeed(), 0.0f, 1.0f );
|
||||
|
||||
////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, up;
|
||||
AngleVectors( angles, &forward, &right, &up );
|
||||
|
||||
CalcViewmodelBob();
|
||||
|
||||
|
||||
// Note: we need to use paint code for gun bob so the gun bobs correctly when player sticks on walls (Bank)
|
||||
C_Portal_Player *pPortalPlayer = ToPortalPlayer( GetOwner() );
|
||||
if ( !pPortalPlayer )
|
||||
return;
|
||||
|
||||
// Apply bob, but scaled down to 40%
|
||||
VectorMA( origin, g_verticalBob * 0.1f, forward, origin );
|
||||
|
||||
// Z bob a bit more
|
||||
origin += g_verticalBob * 0.1f * pPortalPlayer->GetPortalPlayerLocalData().m_Up;
|
||||
|
||||
//move left and right
|
||||
VectorMA( origin, g_lateralBob * 0.8f, right, origin );
|
||||
|
||||
//roll, pitch, yaw
|
||||
float rollAngle = g_verticalBob * 0.5f;
|
||||
VMatrix rotMatrix;
|
||||
Vector rotAxis = CrossProduct( right, up ).Normalized();
|
||||
|
||||
MatrixBuildRotationAboutAxis( rotMatrix, rotAxis, rollAngle );
|
||||
up = rotMatrix * up;
|
||||
forward = rotMatrix * forward;
|
||||
right = rotMatrix * right;
|
||||
|
||||
float pitchAngle = -g_verticalBob * 0.4f;
|
||||
rotAxis = right;
|
||||
MatrixBuildRotationAboutAxis( rotMatrix, rotAxis, pitchAngle );
|
||||
up = rotMatrix * up;
|
||||
forward = rotMatrix * forward;
|
||||
|
||||
float yawAngle = -g_lateralBob * 0.3f;
|
||||
rotAxis = up;
|
||||
MatrixBuildRotationAboutAxis( rotMatrix, rotAxis, yawAngle );
|
||||
forward = rotMatrix * forward;
|
||||
|
||||
VectorAngles( forward, up, angles );
|
||||
|
||||
//// 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
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handle firing
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBasePortalCombatWeapon::ItemPostFrame( void )
|
||||
{
|
||||
CPortal_Player *pOwner = ToPortalPlayer( GetOwner() );
|
||||
if ( pOwner == NULL )
|
||||
return;
|
||||
|
||||
// Primary attack
|
||||
if ( pOwner->m_nButtons & IN_ATTACK && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
|
||||
{
|
||||
if ( pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false )
|
||||
{
|
||||
// This weapon doesn't fire underwater
|
||||
WeaponSound(EMPTY);
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.2;
|
||||
return;
|
||||
}
|
||||
|
||||
// If they're still holding down the button, wait for the next fire time
|
||||
if ( ( pOwner->m_afButtonLast & IN_ATTACK ) && ( m_flNextRepeatPrimaryAttack > gpGlobals->curtime ) )
|
||||
return;
|
||||
|
||||
PrimaryAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
// Secondary attack
|
||||
if ( pOwner->m_nButtons & IN_ATTACK2 && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
|
||||
{
|
||||
if ( pOwner->GetWaterLevel() == 3 )
|
||||
{
|
||||
// This weapon doesn't fire underwater
|
||||
WeaponSound( EMPTY );
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.2;
|
||||
return;
|
||||
}
|
||||
|
||||
// If they're still holding down the button, wait for the next fire time
|
||||
if ( ( pOwner->m_afButtonLast & IN_ATTACK2 ) && ( m_flNextRepeatSecondaryAttack > gpGlobals->curtime ) )
|
||||
return;
|
||||
|
||||
// Attack!
|
||||
SecondaryAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
WeaponIdle();
|
||||
}
|
||||
|
||||
|
||||
ConVar sv_weapon_pickup_time_delay("sv_weapon_pickup_time_delay", "0.2f", FCVAR_REPLICATED | FCVAR_CHEAT);
|
||||
|
||||
bool CBasePortalCombatWeapon::EnoughTimeSinceThrown()
|
||||
{
|
||||
return gpGlobals->curtime - m_flThrowTime > sv_weapon_pickup_time_delay.GetFloat();
|
||||
}
|
||||
|
||||
float CBasePortalCombatWeapon::GetThrowTime()
|
||||
{
|
||||
return m_flThrowTime;
|
||||
}
|
||||
|
||||
void CBasePortalCombatWeapon::Drop( const Vector& vecVelocity )
|
||||
{
|
||||
// Store time when we threw the gun so we dont go and pick up the gun too soon
|
||||
m_flThrowTime = gpGlobals->curtime;
|
||||
m_pLastOwner = GetOwner();
|
||||
|
||||
BaseClass::Drop( vecVelocity );
|
||||
}
|
||||
|
||||
CBaseEntity* CBasePortalCombatWeapon::GetLastOwner()
|
||||
{
|
||||
return m_pLastOwner;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
virtual void ItemPostFrame( 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
|
||||
|
||||
float m_flNextRepeatPrimaryAttack;
|
||||
float m_flNextRepeatSecondaryAttack;
|
||||
private:
|
||||
|
||||
CBasePortalCombatWeapon( const CBasePortalCombatWeapon & );
|
||||
|
||||
// throwing guns
|
||||
public:
|
||||
float GetThrowTime();
|
||||
bool EnoughTimeSinceThrown();
|
||||
virtual void Drop( const Vector &vecVelocity );
|
||||
CBaseEntity* GetLastOwner();
|
||||
private:
|
||||
float m_flThrowTime;
|
||||
CBaseEntity* m_pLastOwner;
|
||||
};
|
||||
|
||||
#endif // WEAPON_BASEPORTALCOMBATWEAPON_SHARED_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
#include "portal_shareddefs.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
class C_InfoPlacementHelper;
|
||||
#define CInfoPlacementHelper C_InfoPlacementHelper
|
||||
#else
|
||||
class CInfoPlacementHelper;
|
||||
#endif
|
||||
|
||||
struct TracePortalPlacementInfo_t
|
||||
{
|
||||
// Default initialization
|
||||
TracePortalPlacementInfo_t( void ) :
|
||||
ePlacementResult( PORTAL_PLACEMENT_SUCCESS ),
|
||||
vecFinalPosition( vec3_invalid ),
|
||||
angFinalAngles( vec3_angle ),
|
||||
pPlacementHelper( NULL )
|
||||
{
|
||||
UTIL_ClearTrace( tr );
|
||||
}
|
||||
|
||||
PortalPlacementResult_t ePlacementResult; // The final indicator of if the portal succeeded in placement
|
||||
Vector vecFinalPosition; // Where the shot ended up
|
||||
QAngle angFinalAngles; // How the shot ended up oriented
|
||||
CInfoPlacementHelper *pPlacementHelper; // A placement helper (if we hit it)
|
||||
trace_t tr; // Resultant trace
|
||||
};
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_weapon_portalgun.h"
|
||||
#else
|
||||
#include "weapon_portalgun.h"
|
||||
#endif
|
||||
|
||||
//
|
||||
// NOTE: IF you change these, you *MUST* ensure the Precache blocks in CWeaponPortalgun::Precache()
|
||||
// are maintained. Lack of precaching causes VERY BAD run time I/O hitches which cause the audio
|
||||
// to stutter and pollute Perf analysis.
|
||||
//
|
||||
#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_TINTED_GLOW "sprites/whitelight"
|
||||
// #define PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE "sprites/portalgun_effects"
|
||||
#define PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE "particle/particle_glow_05"
|
||||
#define PORTALGUN_PORTAL_TUBE_BEAM_SPRITE "particle/particle_glow_05"
|
||||
|
||||
enum
|
||||
{
|
||||
EFFECT_NONE,
|
||||
EFFECT_READY,
|
||||
EFFECT_HOLDING,
|
||||
};
|
||||
|
||||
extern ConVar sk_auto_reload_time;
|
||||
|
||||
#endif // WEAPON_PORTALGUN_SHARED_H
|
||||
@@ -0,0 +1,48 @@
|
||||
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef WEAPONS_PORTAL_H
|
||||
#define WEAPONS_PORTAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
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,
|
||||
|
||||
//Hat
|
||||
WEAPON_WEARABLE,
|
||||
|
||||
WEAPON_MAX, // number of weapons weapon index
|
||||
|
||||
} PortalWeaponID;
|
||||
|
||||
#endif // WEAPONS_PORTAL_H
|
||||
Reference in New Issue
Block a user