cstrike15_src/game/client/portal/c_prop_portal.cpp
2025-06-04 03:22:50 +02:00

1373 lines
42 KiB
C++

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "c_prop_portal.h"
#include "portal_shareddefs.h"
#include "clientsideeffects.h"
#include "tier0/vprof.h"
#include "materialsystem/ITexture.h"
#include "hud_macros.h"
#include "IGameSystem.h"
#include "game_timescale_shared.h"
#include "view.h" // For MainViewOrigin()
#include "clientleafsystem.h" // For finding the leaves our portals are in
#include "portal_render_targets.h" // Access to static references to Portal-specific render textures
#include "toolframework/itoolframework.h"
#include "toolframework_client.h"
#include "tier1/keyvalues.h"
#include "rendertexture.h"
#include "prop_portal_shared.h"
#include "particles_new.h"
#include "materialsystem/imaterialvar.h"
#include "portal_mp_gamerules.h"
#include "c_weapon_portalgun.h"
#include "prediction.h"
#include "particle_parse.h"
#include "c_user_message_register.h"
#include "c_world.h"
#include "C_Portal_Player.h"
#include "c_pixel_visibility.h"
#include "glow_overlay.h"
#include "dlight.h"
#include "iefx.h"
#include "simple_keys.h"
#include "c_baseprojectedentity.h"
#include "view_scene.h"
#ifdef _DEBUG
#include "filesystem.h"
#endif
#include "debugoverlay_shared.h"
IMPLEMENT_CLIENTCLASS_DT( C_Prop_Portal, DT_Prop_Portal, CProp_Portal )
RecvPropEHandle( RECVINFO( m_hFiredByPlayer ) ),
RecvPropInt( RECVINFO( m_nPlacementAttemptParity ) ),
END_RECV_TABLE()
LINK_ENTITY_TO_CLASS( prop_portal, C_Prop_Portal );
BEGIN_PREDICTION_DATA( C_Prop_Portal )
DEFINE_PRED_FIELD( m_nPlacementAttemptParity, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
bool g_bShowGhostedPortals = false;
PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheBasicPropPortalDrawingMaterials )
#if !defined( _GAMECONSOLE ) //XBox 360 is guaranteed to use stencil mode, and therefore doesn't need texture mode materials
PRECACHE( MATERIAL, "models/portals/portal_1_dynamicmesh" )
PRECACHE( MATERIAL, "models/portals/portal_2_dynamicmesh" )
PRECACHE( MATERIAL, "models/portals/portal_1_renderfix_dynamicmesh" )
PRECACHE( MATERIAL, "models/portals/portal_2_renderfix_dynamicmesh" )
#endif
PRECACHE( MATERIAL, "models/portals/portal_depthdoubler" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_1" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_2" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_tinted" )
PRECACHE( MATERIAL, "models/portals/portal_stencil_hole" )
PRECACHE( MATERIAL, "models/portals/portal_refract_1" )
//PRECACHE( MATERIAL, "models/portals/portal_refract_2" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_1_noz" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_2_noz" )
PRECACHE( MATERIAL, "models/portals/portalstaticoverlay_noz" )
//PRECACHE( MATERIAL, "effects/flashlight001" ) //light transfers disabled indefinitely
PRECACHE_REGISTER_END()
class CAutoInitBasicPropPortalDrawingMaterials : public CAutoGameSystem
{
public:
PropPortalRenderingMaterials_t m_Materials;
void LevelInitPreEntity()
{
m_Materials.m_PortalMaterials[0].Init( "models/portals/portal_1_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalMaterials[1].Init( "models/portals/portal_2_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalRenderFixMaterials[0].Init( "models/portals/portal_1_renderfix_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalRenderFixMaterials[1].Init( "models/portals/portal_2_renderfix_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalDepthDoubler.Init( "models/portals/portal_depthdoubler", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticOverlay[0].Init( "models/portals/portalstaticoverlay_1", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticOverlay[1].Init( "models/portals/portalstaticoverlay_2", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticOverlay_Tinted.Init( "models/portals/portalstaticoverlay_tinted", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticGhostedOverlay[0].Init( "models/portals/portalstaticoverlay_1_noz", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticGhostedOverlay[1].Init( "models/portals/portalstaticoverlay_2_noz", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_PortalStaticGhostedOverlay_Tinted.Init( "models/portals/portalstaticoverlay_noz", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_Portal_Stencil_Hole.Init( "models/portals/portal_stencil_hole", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_Portal_Refract.Init( "models/portals/portal_refract_1", TEXTURE_GROUP_CLIENT_EFFECTS );
m_Materials.m_nDepthDoubleViewMatrixVarCache = 0;
m_Materials.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &m_Materials.m_nDepthDoubleViewMatrixVarCache ); // Warm cache
m_Materials.m_nStaticOverlayTintedColorGradientLightVarCache = 0;
m_Materials.m_PortalStaticOverlay_Tinted->FindVarFast( "$PortalColorGradientLight", &m_Materials.m_nStaticOverlayTintedColorGradientLightVarCache ); // Warm cache
IMaterialVar *pPortalCoopColorPlayerOnePortalOne = m_Materials.m_PortalStaticOverlay_Tinted->FindVar( "$PortalCoopColorPlayerOnePortalOne", NULL, false );
if ( pPortalCoopColorPlayerOnePortalOne )
{
pPortalCoopColorPlayerOnePortalOne->GetVecValue( &( m_Materials.m_coopPlayerPortalColors[0][0].x ), 3 );
}
IMaterialVar *pPortalCoopColorPlayerOnePortalTwo = m_Materials.m_PortalStaticOverlay_Tinted->FindVar( "$PortalCoopColorPlayerOnePortalTwo", NULL, false );
if ( pPortalCoopColorPlayerOnePortalTwo )
{
pPortalCoopColorPlayerOnePortalTwo->GetVecValue( &( m_Materials.m_coopPlayerPortalColors[0][1].x ), 3 );
}
IMaterialVar *pPortalCoopColorPlayerTwoPortalOne = m_Materials.m_PortalStaticOverlay_Tinted->FindVar( "$PortalCoopColorPlayerTwoPortalOne", NULL, false );
if ( pPortalCoopColorPlayerTwoPortalOne )
{
pPortalCoopColorPlayerTwoPortalOne->GetVecValue( &( m_Materials.m_coopPlayerPortalColors[1][0].x ), 3 );
}
IMaterialVar *pPortalCoopColorPlayerTwoPortalTwo = m_Materials.m_PortalStaticOverlay_Tinted->FindVar( "$PortalCoopColorPlayerTwoPortalTwo", NULL, false );
if ( pPortalCoopColorPlayerTwoPortalTwo )
{
pPortalCoopColorPlayerTwoPortalTwo->GetVecValue( &( m_Materials.m_coopPlayerPortalColors[1][1].x ), 3 );
}
m_Materials.m_singlePlayerPortalColors[0] = Vector( 64.0f/255.0f, 160.0f/255.0f, 1.0f );
m_Materials.m_singlePlayerPortalColors[1] = Vector( 1.0f, 160.0f/255.0f, 32.0f/255.0f );
}
};
static CAutoInitBasicPropPortalDrawingMaterials s_FlatBasicPortalDrawingMaterials;
PropPortalRenderingMaterials_t& C_Prop_Portal::m_Materials = s_FlatBasicPortalDrawingMaterials.m_Materials;
// Throttle portal "ghosting" rendering
ConVar portal_draw_ghosting( "portal_draw_ghosting", "1", FCVAR_NONE );
// Determines if portals should cast light through themselves when
ConVar portal_transmit_light( "portal_transmit_light", "0", FCVAR_CHEAT );
extern ConVar use_server_portal_particles;
void __MsgFunc_PortalFX_Surface(bf_read &msg)
{
int iPortalEnt = msg.ReadShort();
C_Prop_Portal *pPortal= dynamic_cast<C_Prop_Portal*>( ClientEntityList().GetEnt( iPortalEnt ) );
if( !pPortal )
{
Warning("!!! Failed to find portal %d !!!\n", iPortalEnt );
return;
}
int iOwnerEnt = msg.ReadShort();
C_BaseEntity *pOwner = ClientEntityList().GetEnt( iOwnerEnt );
int nTeam = msg.ReadByte();
int nPortalNum = msg.ReadByte();
int nEffect = msg.ReadByte();
Vector vecOrigin;
msg.ReadBitVec3Coord( vecOrigin );
QAngle qAngle;
msg.ReadBitAngles( qAngle );
pPortal->CreateFizzleEffect( pOwner, nEffect, vecOrigin, qAngle, nTeam, nPortalNum );
}
USER_MESSAGE_REGISTER( PortalFX_Surface );
C_Prop_Portal::C_Prop_Portal( void )
: m_fStaticAmount( 0.0f ),
m_fSecondaryStaticAmount( 0.0f ),
m_fOpenAmount( 0.0f )
{
if( !ms_DefaultPortalSizeInitialized )
{
ms_DefaultPortalSizeInitialized = true; // for CEG protection
CEG_GCV_PRE();
ms_DefaultPortalHalfHeight = CEG_GET_CONSTANT_VALUE( DefaultPortalHalfHeight ); // only protecting one to reduce the cost of first-portal check
CEG_GCV_POST();
}
m_bIsPropPortal = true; // Member of CPortalRenderable
TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
CProp_Portal_Shared::AllPortals.AddToTail( this );
SetPredictionEligible( true );
}
C_Prop_Portal::~C_Prop_Portal( void )
{
// Shut down our effect if we have it
DestroyAttachedParticles();
CProp_Portal_Shared::AllPortals.FindAndRemove( this );
}
void C_Prop_Portal::Spawn( void )
{
SetThink( &C_Prop_Portal::ClientThink );
SetNextClientThink( CLIENT_THINK_ALWAYS );
m_matrixThisToLinked.Identity(); //don't accidentally teleport objects to zero space
m_hEffect = NULL;
BaseClass::Spawn();
}
void C_Prop_Portal::Activate( void )
{
BaseClass::Activate();
}
void C_Prop_Portal::ClientThink( void )
{
bool bDidAnything = false;
if( m_fStaticAmount > 0.0f )
{
m_fStaticAmount -= gpGlobals->frametime;
if( m_fStaticAmount < 0.0f )
m_fStaticAmount = 0.0f;
bDidAnything = true;
}
if( m_fSecondaryStaticAmount > 0.0f )
{
m_fSecondaryStaticAmount -= gpGlobals->frametime;
if( m_fSecondaryStaticAmount < 0.0f )
m_fSecondaryStaticAmount = 0.0f;
bDidAnything = true;
}
if( m_fOpenAmount < 1.0f )
{
float flSlowdown = GameTimescale()->GetCurrentTimescale();
m_fOpenAmount += ( gpGlobals->frametime * ( 2.0f / flSlowdown ) );
if( m_fOpenAmount > 1.0f )
m_fOpenAmount = 1.0f;
bDidAnything = true;
}
// Speculative workaround for multiplayer bug where
// active and linked portals have full static.
// No known repro, so as a hammer just think all the time.
// The above logic is pretty cheap, so this shouldn't be a big deal.
// Also, there really isn't any more than 4 of these entities at one time,
// so the upper bound is pretty small.
#if 0
if( bDidAnything == false )
{
SetNextClientThink( CLIENT_THINK_NEVER );
}
#endif
}
void C_Prop_Portal::UpdateOnRemove( void )
{
/*
if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
{
g_pClientShadowMgr->DestroyFlashlight( TransformedLighting.m_LightShadowHandle );
TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
}
*/
// Kill any dlight we may have
if( TransformedLighting.m_pEntityLight )
{
TransformedLighting.m_pEntityLight->die = gpGlobals->curtime;
TransformedLighting.m_pEntityLight = NULL;
}
// Shut down our effect if we have it
DestroyAttachedParticles();
BaseClass::UpdateOnRemove();
}
void C_Prop_Portal::OnRestore( void )
{
BaseClass::OnRestore();
if ( !m_hEffect && !m_hEffect.IsValid() && IsActive() )
{
CreateAttachedParticles();
}
}
void C_Prop_Portal::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
{
if ( Q_stricmp( pszParticleName, "portal_1_overlap" ) == 0 || Q_stricmp( pszParticleName, "portal_2_overlap" ) == 0 )
{
float fClosestDistanceSqr = -1.0f;
Vector vClosestPosition;
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
if( iPortalCount != 0 )
{
CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
for( int i = 0; i != iPortalCount; ++i )
{
CProp_Portal *pTempPortal = pPortals[i];
if ( pTempPortal != this && pTempPortal->IsActive() )
{
Vector vPosition = pTempPortal->GetAbsOrigin();
float fDistanceSqr = pNewParticleEffect->GetRenderOrigin().DistToSqr( vPosition );
if ( fClosestDistanceSqr == -1.0f || fClosestDistanceSqr > fDistanceSqr )
{
fClosestDistanceSqr = fDistanceSqr;
vClosestPosition = vPosition;
}
}
}
}
if ( fClosestDistanceSqr != -1.0f )
{
pNewParticleEffect->SetControlPoint( 1, vClosestPosition );
}
}
}
void C_Prop_Portal::CreateFizzleEffect( C_BaseEntity *pOwner, int iEffect, Vector vecOrigin, QAngle qAngles, int nTeam, int nPortalNum )
{
Color color = UTIL_Portal_Color_Particles( nPortalNum, nTeam );
Vector vColor;
vColor.x = color.r();
vColor.y = color.g();
vColor.z = color.b();
CUtlReference<CNewParticleEffect> pEffect;
if ( !pOwner )
return;
bool bCreated = false;
switch ( iEffect )
{
case PORTAL_FIZZLE_SUCCESS:
{
pEffect = CNewParticleEffect::CreateOrAggregate( NULL, "portal_success", vecOrigin, NULL );
bCreated = true;
}
break;
case PORTAL_FIZZLE_BAD_SURFACE:
{
pEffect = CNewParticleEffect::CreateOrAggregate( NULL, "portal_badsurface", vecOrigin, NULL );
}
break;
case PORTAL_FIZZLE_CLOSE:
{
pEffect = CNewParticleEffect::CreateOrAggregate( NULL, "portal_close", vecOrigin, NULL );
}
break;
}
if ( pEffect )
{
pEffect->SetControlPoint( 0, vecOrigin );
Vector vecForward, vecRight, vecUp;
AngleVectors( qAngles, &vecForward, &vecRight, &vecUp );
pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp );
pEffect->SetControlPoint( 2, vColor );
}
if ( bCreated )
{
Color rgbaColor = Color( vColor.x, vColor.y, vColor.z, 255 );
CreateAttachedParticles( &rgbaColor );
}
}
void C_Prop_Portal::OnPortalMoved( void )
{
if( IsActive() )
{
if( !IsMobile() )
{
if( !GetPredictable() || (prediction->InPrediction() && prediction->IsFirstTimePredicted()) )
{
m_fOpenAmount = 0.0f;
SetNextClientThink( CLIENT_THINK_ALWAYS ); //we need this to help open up
C_Prop_Portal *pRemote = (C_Prop_Portal *)m_hLinkedPortal.Get();
//add static to the remote
if( pRemote )
{
pRemote->m_fStaticAmount = 1.0f; // This will cause the other portal to show the static effect
pRemote->SetNextClientThink( CLIENT_THINK_ALWAYS );
}
}
}
UpdateTransformedLighting();
C_BaseProjectedEntity::TestAllForProjectionChanges();
}
BaseClass::OnPortalMoved();
}
void C_Prop_Portal::OnActiveStateChanged( void )
{
if( IsActive() )
{
// UpdateTransformedLighting();
if( !GetPredictable() || (prediction->InPrediction() && prediction->IsFirstTimePredicted()) )
{
m_fOpenAmount = 0.0f;
m_fStaticAmount = 1.0f;
SetNextClientThink( CLIENT_THINK_ALWAYS ); //we need this to help open up
C_Prop_Portal *pRemote = (C_Prop_Portal *)m_hLinkedPortal.Get();
//add static to the remote
if( pRemote )
{
pRemote->m_fStaticAmount = 1.0f; // This will cause the other portal to show the static effect
pRemote->SetNextClientThink( CLIENT_THINK_ALWAYS );
}
}
}
else
{
if( TransformedLighting.m_pEntityLight )
{
TransformedLighting.m_pEntityLight->die = gpGlobals->curtime;
TransformedLighting.m_pEntityLight = NULL;
}
// the portal closed
DestroyAttachedParticles();
DoFizzleEffect( PORTAL_FIZZLE_CLOSE, false );
/*
if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
{
g_pClientShadowMgr->DestroyFlashlight( TransformedLighting.m_LightShadowHandle );
TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
}
*/
}
BaseClass::OnActiveStateChanged();
}
void C_Prop_Portal::OnLinkageChanged( C_Portal_Base2D *pOldLinkage )
{
if ( IsActive() )
{
CreateAttachedParticles();
}
if( m_hLinkedPortal.Get() != NULL )
{
UpdateTransformedLighting();
}
/*
else
{
if( TransformedLighting.m_pEntityLight )
{
TransformedLighting.m_pEntityLight->die = gpGlobals->curtime;
TransformedLighting.m_pEntityLight = NULL;
}
if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
{
g_pClientShadowMgr->DestroyFlashlight( TransformedLighting.m_LightShadowHandle );
TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
}
}
*/
}
void C_Prop_Portal::CreateAttachedParticles( Color *pColors /*= NULL*/ )
{
DestroyAttachedParticles();
if ( !GameRules()->IsMultiplayer() && use_server_portal_particles.GetBool() )
return;
C_BaseAnimating::PushAllowBoneAccess( true, false, "portalparticles" );
// create a new effect for this portal
mdlcache->BeginLock();
m_hEffect = ParticleProp()->Create( m_bIsPortal2 ? "portal_edge_reverse" : "portal_edge", PATTACH_POINT_FOLLOW, "particles" );
mdlcache->EndLock();
if ( m_hEffect.IsValid() )
{
Color clrPortal;
if ( !pColors )
{
int nTeam = GetTeamNumber();
C_BasePlayer *pPlayer = GetPredictionOwner();
if ( pPlayer )
{
nTeam = pPlayer->GetTeamNumber();
}
clrPortal = UTIL_Portal_Color_Particles( (m_bIsPortal2)?(2):(1), nTeam );
pColors = &clrPortal;
}
const Vector vecPortalColor( ((float)pColors->r()),
((float)pColors->g()),
((float)pColors->b()) );
m_hEffect->SetControlPoint( 7, vecPortalColor );
}
C_BaseAnimating::PopBoneAccess( "portalparticles" );
}
void C_Prop_Portal::DestroyAttachedParticles( void )
{
// If there's already a different stream particle effect, get rid of it.
// Shut down our effect if we have it
if ( m_hEffect && m_hEffect.IsValid() )
{
ParticleProp()->StopEmission( m_hEffect, false, true, false, true );
m_hEffect = NULL;
}
}
//ConVar r_portal_light_innerangle( "r_portal_light_innerangle", "90.0", FCVAR_CLIENTDLL );
//ConVar r_portal_light_outerangle( "r_portal_light_outerangle", "90.0", FCVAR_CLIENTDLL );
//ConVar r_portal_light_forward( "r_portal_light_forward", "0.0", FCVAR_CLIENTDLL );
ConVar r_portal_use_dlights( "r_portal_use_dlights", "0", FCVAR_CLIENTDLL );
void C_Prop_Portal::UpdateTransformedLighting( void )
{
// C_Prop_Portal *pRemote = (C_Prop_Portal *)m_hLinkedPortal.Get();
Vector ptForwardOrigin = m_ptOrigin + m_vForward;// * 3.0f;
Vector vScaledRight = m_vRight; //* (GetHalfWidth() * 0.95f);
Vector vScaledUp = m_vUp * (GetHalfHeight() * 0.95f);
dlight_t *pFakeLight = NULL;
// ClientShadowHandle_t ShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
/*
if( pRemote )
{
pFakeLight = pRemote->TransformedLighting.m_pEntityLight;
// ShadowHandle = pRemote->TransformedLighting.m_LightShadowHandle;
// AssertMsg( (ShadowHandle == CLIENTSHADOW_INVALID_HANDLE) || (TransformedLighting.m_LightShadowHandle == CLIENTSHADOW_INVALID_HANDLE), "Two shadow handles found, should only have one shared handle" );
AssertMsg( (pFakeLight == NULL) || (TransformedLighting.m_pEntityLight == NULL), "two lights found, should only have one shared light" );
pRemote->TransformedLighting.m_pEntityLight = NULL;
// pRemote->TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
}
*/
if( TransformedLighting.m_pEntityLight )
{
pFakeLight = TransformedLighting.m_pEntityLight;
TransformedLighting.m_pEntityLight = NULL;
}
/*
if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
{
ShadowHandle = TransformedLighting.m_LightShadowHandle;
TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
}
*/
/*
if( pFakeLight != NULL )
{
//turn off the light so it doesn't interfere with absorbed light calculations
pFakeLight->color.r = 0;
pFakeLight->color.g = 0;
pFakeLight->color.b = 0;
pFakeLight->flags = DLIGHT_NO_WORLD_ILLUMINATION | DLIGHT_NO_MODEL_ILLUMINATION;
pFakeLight->radius = 0.0f;
render->TouchLight( pFakeLight );
}
*/
// if ( pRemote /*&& portal_transmit_light.GetBool()*/ ) //now, see if we need to fake light coming through a portal
{
/*
Vector vLightAtRemotePortal( vec3_origin ), vLightAtLocalPortal( vec3_origin );
if( pRemote ) //get lighting at remote portal
{
engine->ComputeLighting( pRemote->m_ptOrigin, NULL, false, vLightAtRemotePortal, NULL );
}
//now get lighting at the local portal
{
engine->ComputeLighting( m_ptOrigin, NULL, false, vLightAtLocalPortal, NULL );
}
*/
//Vector vLightDiff = vLightAtLocalPortal - vLightAtRemotePortal;
//if( vLightDiff.Length() > 0.6f ) //a significant difference in lighting, remember that the light vectors are NOT normalized in length
{
//time to fake some light coming through the greater intensity portal to the lower intensity
//are we transferring light from the local portal to the remote?
Vector ptLightOrigin, vLightForward, vColor, vClampedColor;
float fColorScale;
{
int nTeam = GetTeamNumber();
C_BasePlayer *pPlayer = GetPredictionOwner();
if ( pPlayer )
nTeam = pPlayer->GetTeamNumber();
Color clrPortal = UTIL_Portal_Color( (m_bIsPortal2)?(2):(1), nTeam );
Vector vecPortalColor
(
(float)(clrPortal.r()) / 255.0f,
(float)(clrPortal.g()) / 255.0f,
(float)(clrPortal.b()) / 255.0f
);
/*
if ( bLocalToRemote )
{
vColor = vecPortalColor;
vLightForward = pRemote->m_vForward;
ptLightOrigin = pRemote->m_ptOrigin;
}
else
*/
{
vColor = vecPortalColor;
vLightForward = m_vForward;
ptLightOrigin = m_ptOrigin;
}
//clamp color values
fColorScale = vColor.x;
if( vColor.y > fColorScale )
fColorScale = vColor.y;
if( vColor.z > fColorScale )
fColorScale = vColor.z;
if( fColorScale > 1.0f )
vClampedColor = vColor * (1.0f / fColorScale);
else
vClampedColor = vColor;
/*if( vColor.x < 0.0f )
vColor.x = 0.0f;
if( vColor.x > 1.0f )
vColor.x = 1.0f;
if( vColor.y < 0.0f )
vColor.y = 0.0f;
if( vColor.y > 1.0f )
vColor.y = 1.0f;
if( vColor.z < 0.0f )
vColor.z = 0.0f;
if( vColor.z > 1.0f )
vColor.z = 1.0f;*/
}
// Turn on the dlight
if ( r_portal_use_dlights.GetBool() )
{
if( pFakeLight == NULL )
pFakeLight = effects->CL_AllocDlight( LIGHT_INDEX_TE_DYNAMIC + entindex() ); //is there a difference between DLight and ELight when only lighting ents?
}
if ( pFakeLight != NULL ) //be absolutely sure that light allocation hasn't failed
{
/*
if( bLocalToRemote )
{
//local light is greater, fake at remote portal
pRemote->TransformedLighting.m_pEntityLight = pFakeLight;
pFakeLight->key = pRemote->index;
}
else
*/
{
//remote light is greater, fake at local portal
TransformedLighting.m_pEntityLight = pFakeLight;
pFakeLight->key = index;
}
pFakeLight->die = gpGlobals->curtime + 1e10;
pFakeLight->flags = 0; // DLIGHT_NO_WORLD_ILLUMINATION;
pFakeLight->minlight = 0.0f;
pFakeLight->radius = 128.0f;
pFakeLight->m_InnerAngle = 0.0f; //r_portal_light_innerangle.GetFloat();
pFakeLight->m_OuterAngle = 120.0f; //r_portal_light_outerangle.GetFloat();
pFakeLight->style = 0;
pFakeLight->origin = ptLightOrigin;
pFakeLight->m_Direction = vLightForward;
pFakeLight->color.r = vClampedColor.x * 255;
pFakeLight->color.g = vClampedColor.y * 255;
pFakeLight->color.b = vClampedColor.z * 255;
pFakeLight->color.exponent = 0.0f;
// pFakeLight->color.exponent = ((signed int)(((*((unsigned int *)(&fColorScale))) & 0x7F800000) >> 23)) - 125; //strip the exponent from our maximum color
render->TouchLight( pFakeLight );
}
/*
FlashlightState_t state;
{
state.m_NearZ = 4.0f;
state.m_FarZ = 500.0f;
state.m_nSpotlightTextureFrame = 0;
state.m_pSpotlightTexture = materials->FindTexture( "effects/flashlight001", TEXTURE_GROUP_OTHER, false );
state.m_fConstantAtten = 0.0f;
state.m_fLinearAtten = 500.0f;
state.m_fQuadraticAtten = 0.0f;
state.m_fHorizontalFOVDegrees = 140.0f;
state.m_fVerticalFOVDegrees = 140.0f;
state.m_flShadowSlopeScaleDepthBias = 16.0f;
state.m_flShadowDepthBias = 0.0005f;
state.m_nSpotlightTextureFrame = 0;
state.m_bEnableShadows = true;
state.m_vecLightOrigin = ptLightOrigin;
Vector vLightRight, vLightUp( 0.0f, 0.0f, 1.0f );
if( fabs( DotProduct( vLightUp, vLightForward ) ) > 0.99f )
vLightUp.Init( 0.0f, 1.0f, 0.0f ); // Don't want vLightUp and vLightForward to be parallel
CrossProduct( vLightUp, vLightForward, vLightRight );
VectorNormalize( vLightRight );
CrossProduct( vLightForward, vLightRight, vLightUp );
VectorNormalize( vLightUp );
BasisToQuaternion( vLightForward, vLightRight, vLightUp, state.m_quatOrientation );
state.m_Color[0] = vColor.x;// * 0.35f;
state.m_Color[1] = vColor.y;// * 0.35f;
state.m_Color[2] = vColor.z;// * 0.35f;
state.m_Color[3] = 1.0f;
}
*/
/*
if( ShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
{
g_pClientShadowMgr->UpdateFlashlightState( ShadowHandle, state ); //simpler update for existing handle
g_pClientShadowMgr->UpdateProjectedTexture( ShadowHandle, true );
}
if( ShadowHandle == CLIENTSHADOW_INVALID_HANDLE )
{
ShadowHandle = g_pClientShadowMgr->CreateFlashlight( state );
if( ShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
g_pClientShadowMgr->UpdateProjectedTexture( ShadowHandle, true );
}
if( bLocalToRemote )
pRemote->TransformedLighting.m_LightShadowHandle = ShadowHandle;
else
TransformedLighting.m_LightShadowHandle = ShadowHandle;
*/
}
}
}
void C_Prop_Portal::DrawPreStencilMask( IMatRenderContext *pRenderContext )
{
//draw the warpy effect if we're still opening up
if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
{
DrawSimplePortalMesh( pRenderContext, m_Materials.m_Portal_Refract, 0.25f );
}
}
void C_Prop_Portal::DrawStencilMask( IMatRenderContext *pRenderContext )
{
DrawSimplePortalMesh( pRenderContext, m_Materials.m_Portal_Stencil_Hole, 0.25f );
DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
void C_Prop_Portal::DrawDepthDoublerMesh( IMatRenderContext *pRenderContext, float fForwardOffsetModifier )
{
if( CPortalRender::DepthDoublerPIPDisableCheck() )
return;
if ( m_Materials.m_PortalDepthDoubler.IsValid() )
{
IMaterialVar *pVar = m_Materials.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &m_Materials.m_nDepthDoubleViewMatrixVarCache );
if ( pVar != NULL )
{
pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
}
}
DrawSimplePortalMesh( pRenderContext, m_Materials.m_PortalDepthDoubler, fForwardOffsetModifier, 0.25f );
}
float C_Prop_Portal::GetPortalGhostAlpha( void ) const
{
/*
// THIS CODE HAS MOVED TO THE SHADER (portalstaticoverlay VS)
// If we're facing away, always be full strength
float flDot = DotProduct( m_vForward, m_ptOrigin - CurrentViewOrigin() );
if ( flDot > 0.0f )
return 1.0f;
float flDistSqr = ( CurrentViewOrigin() - m_ptOrigin ).LengthSqr();
float flAlpha = RemapValClamped( flDistSqr, Square(10*12), Square(20*12), 0.0f, 1.0f );
*/
// Factor how "open" this portal is, but don't lerp in the visibility until the portal is 85% open...otherwise
// the colored oval becomes visible before the portal's firey border is fully open.
float flOpenScalar = clamp( ( m_fOpenAmount - 0.85f ) / 0.15f, 0.0f, 1.0f );
return flOpenScalar;
}
void C_Prop_Portal::DrawPortalGhostLocations( IMatRenderContext *pRenderContext )
{
// Allow this to be gated by a convar for recording
if ( portal_draw_ghosting.GetBool() == false )
return;
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
if( iPortalCount == 0 )
return;
CPortalRenderable *pExitView = g_pPortalRender->GetCurrentViewExitPortal();
C_Portal_Player *pPlayer = C_Portal_Player::GetLocalPlayer();
bool bCoop = g_pGameRules->IsMultiplayer() && PortalMPGameRules()->IsCoOp();
for( int i = 0; i != iPortalCount; ++i )
{
C_Prop_Portal *pPortal = CProp_Portal_Shared::AllPortals[i];
if( (pPortal != pExitView) && pPortal->IsActive() )
{
if( (pPortal->m_hFiredByPlayer.Get() != NULL) && (bCoop || (pPortal->m_hFiredByPlayer == pPlayer)) )
{
//portal is a candidate for drawing ghost outlines
if( GameRules()->IsMultiplayer() )
{
Color clrPortal = UTIL_Portal_Color( (pPortal->m_bIsPortal2)?(2):(1), pPortal->GetTeamNumber() );
const Vector vecPortalColor
(
(float)(clrPortal.r()) / 255.0f,
(float)(clrPortal.g()) / 255.0f,
(float)(clrPortal.b()) / 255.0f
);
float flAlpha = pPortal->GetPortalGhostAlpha();
if ( flAlpha > 0.0f )
{
pPortal->DrawSimplePortalMesh( pRenderContext, m_Materials.m_PortalStaticGhostedOverlay_Tinted, 0.0f, flAlpha, &vecPortalColor );
}
}
else
{
float flAlpha = pPortal->GetPortalGhostAlpha();
if ( flAlpha > 0.0f )
{
pPortal->DrawSimplePortalMesh( pRenderContext, m_Materials.m_PortalStaticGhostedOverlay[((pPortal->m_bIsPortal2)?(1):(0))], 0.0f, flAlpha );
}
}
}
}
}
}
void C_Prop_Portal::BuildPortalGhostRenderInfo( const CUtlVector< CPortalRenderable* > &allPortals,
CUtlVector< GhostPortalRenderInfo_t > &ghostPortalRenderInfosOut )
{
Assert( ghostPortalRenderInfosOut.Count() == 0 );
C_Portal_Player *pPlayer = C_Portal_Player::GetLocalPlayer();
bool bIsMultiplayer = g_pGameRules->IsMultiplayer();
bool bCoop = bIsMultiplayer && PortalMPGameRules()->IsCoOp();
for( int i = 0; i < allPortals.Count(); i++ )
{
if ( allPortals[i]->IsPropPortal() )
{
C_Prop_Portal *pPortal = static_cast< C_Prop_Portal* >( allPortals[i] );
if ( pPortal->IsActive() &&
( pPortal->m_hFiredByPlayer.Get() != NULL ) &&
( bCoop || ( pPortal->m_hFiredByPlayer == pPlayer ) ) )
{
ghostPortalRenderInfosOut.AddToTail();
ghostPortalRenderInfosOut.Tail().m_pPortal = pPortal;
ghostPortalRenderInfosOut.Tail().m_nGhostPortalQuadIndex = i;
if ( bIsMultiplayer )
{
ghostPortalRenderInfosOut.Tail().m_pGhostMaterial = m_Materials.m_PortalStaticGhostedOverlay_Tinted;
}
else
{
ghostPortalRenderInfosOut.Tail().m_pGhostMaterial = m_Materials.m_PortalStaticGhostedOverlay[ pPortal->m_bIsPortal2 ? 1 : 0 ];
}
}
} // end if ( is prop_portal )
} // end for ( each portal )
}
void C_Prop_Portal::DrawPortal( IMatRenderContext *pRenderContext )
{
if( (view->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
return;
if( WillUseDepthDoublerThisDraw() )
m_fSecondaryStaticAmount = 0.0f;
bool bUseAlternatePortalColors = GameRules()->IsMultiplayer() && !PortalMPGameRules()->Is2GunsCoOp(); //( m_hFiredByPlayer.Get() && ( m_hFiredByPlayer->GetTeamNumber() == TEAM_BLUE ) );
const IMaterial *pStaticOverlayMaterial = bUseAlternatePortalColors ? m_Materials.m_PortalStaticOverlay_Tinted : m_Materials.m_PortalStaticOverlay[((m_bIsPortal2)?(1):(0))];
if ( bUseAlternatePortalColors )
{
int nTeam = GetTeamNumber();
C_BasePlayer *pPlayer = GetPredictionOwner();
if ( pPlayer )
nTeam = pPlayer->GetTeamNumber();
Color clrPortal = UTIL_Portal_Color( (m_bIsPortal2)?(2):(1), nTeam );
const Vector vecPortalColor
(
(float)(clrPortal.r()) / 255.0f,
(float)(clrPortal.g()) / 255.0f,
(float)(clrPortal.b()) / 255.0f
);
if ( m_Materials.m_PortalStaticOverlay_Tinted.IsValid() )
{
IMaterialVar *pVar = m_Materials.m_PortalStaticOverlay_Tinted->FindVarFast( "$PortalColorGradientLight", &m_Materials.m_nStaticOverlayTintedColorGradientLightVarCache );
if ( pVar != NULL )
{
pVar->SetVecValue( &vecPortalColor.x, 3 );
}
}
}
//stencil-based rendering
if( g_pPortalRender->IsRenderingPortal() == false ) //main view
{
if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
{
if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
{
DrawSimplePortalMesh( pRenderContext, m_Materials.m_Portal_Refract, 0.25f );
}
}
DrawSimplePortalMesh( pRenderContext, pStaticOverlayMaterial, 0.25f );
// NOTE [BRJ 9/20/10]: This is almost certainly not necessary. Investigate if deletion is possible
DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
else if( g_pPortalRender->GetCurrentViewExitPortal() != this )
{
if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
{
if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
{
DrawSimplePortalMesh( pRenderContext, m_Materials.m_Portal_Refract, 0.25f );
}
}
if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
&& (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
&& (g_pPortalRender->GetViewRecursionLevel() > 1)
&& (g_pPortalRender->GetCurrentViewEntryPortal() == this) )
{
DrawDepthDoublerMesh( pRenderContext );
}
else
{
DrawSimplePortalMesh( pRenderContext, pStaticOverlayMaterial, 0.25f );
}
}
}
// TODO: Return the material instead, cache off material pointers?
int C_Prop_Portal::BindPortalMaterial( IMatRenderContext *pRenderContext, int nPassIndex, bool *pAllowRingMeshOptimizationOut )
{
VPROF_BUDGET( __FUNCTION__, "BindPortalMaterial" );
*pAllowRingMeshOptimizationOut = true;
if( (view->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
{
return 0;
}
if( WillUseDepthDoublerThisDraw() )
{
m_fSecondaryStaticAmount = 0.0f;
}
bool bUseAlternatePortalColors = GameRules()->IsMultiplayer() && !PortalMPGameRules()->Is2GunsCoOp(); //( m_hFiredByPlayer.Get() && ( m_hFiredByPlayer->GetTeamNumber() == TEAM_BLUE ) );
IMaterial *pStaticOverlayMaterial = bUseAlternatePortalColors ? m_Materials.m_PortalStaticOverlay_Tinted : m_Materials.m_PortalStaticOverlay[ m_bIsPortal2 ? 1 : 0 ];
if ( bUseAlternatePortalColors )
{
int nTeam = GetTeamNumber();
C_BasePlayer *pPlayer = GetPredictionOwner();
if ( pPlayer )
nTeam = pPlayer->GetTeamNumber();
Color clrPortal = UTIL_Portal_Color( (m_bIsPortal2)?(2):(1), nTeam );
const Vector vecPortalColor
(
(float)(clrPortal.r()) / 255.0f,
(float)(clrPortal.g()) / 255.0f,
(float)(clrPortal.b()) / 255.0f
);
if ( m_Materials.m_PortalStaticOverlay_Tinted.IsValid() )
{
IMaterialVar *pVar = m_Materials.m_PortalStaticOverlay_Tinted->FindVarFast( "$PortalColorGradientLight", &m_Materials.m_nStaticOverlayTintedColorGradientLightVarCache );
if ( pVar != NULL )
{
pVar->SetVecValue( &vecPortalColor.x, 3 );
}
}
}
//stencil-based rendering
if( g_pPortalRender->IsRenderingPortal() == false ) //main view
{
if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
{
if ( IsPortalOpening() && ( nPassIndex == 0 ) )
{
pRenderContext->Bind( m_Materials.m_Portal_Refract, GetClientRenderable() );
UpdateFrontBufferTexturesForMaterial( m_Materials.m_Portal_Refract );
return 2;
}
}
pRenderContext->Bind( pStaticOverlayMaterial, GetClientRenderable() );
return 1;
// NOTE [BRJ 9/20/10]: This is almost certainly not necessary. Investigate if deletion is possible
//DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
else if( g_pPortalRender->GetCurrentViewExitPortal() != this )
{
if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
{
if ( IsPortalOpening() && ( nPassIndex == 0 ) )
{
pRenderContext->Bind( m_Materials.m_Portal_Refract, GetClientRenderable() );
UpdateFrontBufferTexturesForMaterial( m_Materials.m_Portal_Refract );
return 2;
}
}
if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
&& (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
&& (g_pPortalRender->GetViewRecursionLevel() > 1)
&& (g_pPortalRender->GetCurrentViewEntryPortal() == this) )
{
if( CPortalRender::DepthDoublerPIPDisableCheck() )
{
// render a static portal instead of a broken depth doubler
pRenderContext->Bind( pStaticOverlayMaterial, GetClientRenderable() );
return 1;
}
if ( m_Materials.m_PortalDepthDoubler.IsValid() )
{
IMaterialVar *pVar = m_Materials.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &m_Materials.m_nDepthDoubleViewMatrixVarCache );
if ( pVar != NULL )
{
pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
}
}
pRenderContext->Bind( m_Materials.m_PortalDepthDoubler, GetClientRenderable() );
*pAllowRingMeshOptimizationOut = false;
return 1;
}
else
{
pRenderContext->Bind( pStaticOverlayMaterial, GetClientRenderable() );
return 1;
}
}
return 0;
}
void C_Prop_Portal::DoFizzleEffect( int iEffect, bool bDelayedPos /*= true*/ )
{
if( prediction->InPrediction() && !prediction->IsFirstTimePredicted() )
return; //early out if we're repeatedly creating particles. Creates way too many particles.
Vector vecOrigin = ( ( bDelayedPos ) ? ( m_vDelayedPosition ) : ( GetAbsOrigin() ) );
QAngle qAngles = ( ( bDelayedPos ) ? ( m_qDelayedAngles ) : ( GetAbsAngles() ) );
Vector vForward, vUp;
AngleVectors( qAngles, &vForward, &vUp, NULL );
vecOrigin = vecOrigin + vForward * 1.0f;
int nPortalNum = m_bIsPortal2 ? 2 : 1;
int nTeam = GetTeamNumber();
if ( iEffect != PORTAL_FIZZLE_SUCCESS && iEffect != PORTAL_FIZZLE_CLOSE )
iEffect = PORTAL_FIZZLE_BAD_SURFACE;
C_BasePlayer *pPlayer = GetPredictionOwner();
if ( pPlayer )
nTeam = pPlayer->GetTeamNumber();
VectorAngles( vUp, vForward, qAngles );
CreateFizzleEffect( pPlayer, iEffect, vecOrigin, qAngles, nTeam, nPortalNum );
}
void C_Prop_Portal::Fizzle( void )
{
}
float C_Prop_Portal::ComputeStaticAmountForRendering() const
{
float flStaticAmount = m_fStaticAmount;
if ( !GetLinkedPortal() )
{
flStaticAmount = 1.0f;
}
if ( WillUseDepthDoublerThisDraw() )
{
if ( CPortalRender::DepthDoublerPIPDisableCheck() )
{
flStaticAmount = 1.0f;
}
else
{
flStaticAmount = 0.0f;
}
}
else if ( g_pPortalRender->GetRemainingPortalViewDepth() == 0 ) //end of the line, no more views
{
flStaticAmount = 1.0f;
}
else if ( (g_pPortalRender->GetRemainingPortalViewDepth() == 1) && (m_fSecondaryStaticAmount > flStaticAmount) ) //fading in from no views to another view (player just walked through it)
{
flStaticAmount = m_fSecondaryStaticAmount;
}
return flStaticAmount;
}
bool C_Prop_Portal::ShouldPredict( void )
{
FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( hh );
if ( pLocalPlayer )
{
if ( m_hFiredByPlayer == pLocalPlayer )
return true;
CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( pLocalPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
if ( pPortalGun && ((pPortalGun->GetAssociatedPortal( false ) == this) || (pPortalGun->GetAssociatedPortal( true ) == this)) )
return true;
}
}
return BaseClass::ShouldPredict();
}
C_BasePlayer *C_Prop_Portal::GetPredictionOwner( void )
{
if ( m_hFiredByPlayer != NULL )
return (C_BasePlayer *)m_hFiredByPlayer.Get();
FOR_EACH_VALID_SPLITSCREEN_PLAYER( iSplitScreenSlot )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( iSplitScreenSlot );
if ( pLocalPlayer )
{
CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( pLocalPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
if ( pPortalGun && ((pPortalGun->GetAssociatedPortal( false ) == this) || (pPortalGun->GetAssociatedPortal( true ) == this)) )
{
m_hFiredByPlayer = pLocalPlayer; // probably portal_place made this portal don't keep doing this
return pLocalPlayer;
}
}
}
return NULL;
}
void C_Prop_Portal::HandlePredictionError( bool bErrorInThisEntity )
{
BaseClass::HandlePredictionError( bErrorInThisEntity );
if( bErrorInThisEntity )
{
if( IsActive() )
{
if ( !m_hEffect || !m_hEffect.IsValid() )
{
CreateAttachedParticles();
}
}
else
{
DestroyAttachedParticles();
}
}
}
void C_Prop_Portal::GetToolRecordingState( KeyValues *msg )
{
if ( !ToolsEnabled() )
return;
VPROF_BUDGET( "C_Prop_Portal::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
BaseClass::GetToolRecordingState( msg );
{
PortalRecordingState_t dummyState;
PortalRecordingState_t *pState = (PortalRecordingState_t *)msg->GetPtr( "portal", &dummyState );
Assert( pState != &dummyState );
pState->m_fOpenAmount = m_fOpenAmount;
pState->m_fStaticAmount = m_fStaticAmount;
pState->m_portalType = "Prop_Portal";
}
{
KeyValues *pKV = CIFM_EntityKeyValuesHandler_AutoRegister::FindOrCreateNonConformantKeyValues( msg );
pKV->SetString( CIFM_EntityKeyValuesHandler_AutoRegister::GetHandlerIDKeyString(), "C_Prop_Portal" );
pKV->SetInt( "entIndex", index );
pKV->SetInt( "teamNumber", GetTeamNumber() );
}
}
class C_Prop_Portal_EntityKeyValuesHandler : public CIFM_EntityKeyValuesHandler_AutoRegister
{
public:
C_Prop_Portal_EntityKeyValuesHandler( void )
: CIFM_EntityKeyValuesHandler_AutoRegister( "C_Prop_Portal" )
{ }
virtual void HandleData_PreUpdate( void )
{
for( int i = 0; i != m_PlaybackPortals.Count(); ++i )
{
m_PlaybackPortals[i].bTouched = false;
}
}
virtual void HandleData_PostUpdate( void )
{
for( int i = m_PlaybackPortals.Count(); --i >= 0; )
{
if( !m_PlaybackPortals[i].bTouched )
{
m_PlaybackPortals.FastRemove( i );
}
}
}
virtual void HandleData( KeyValues *pKeyValues )
{
int iEntIndex = pKeyValues->GetInt( "entIndex", -1 );
Assert( iEntIndex != -1 );
if( iEntIndex == -1 )
return;
for( int i = 0; i != m_PlaybackPortals.Count(); ++i )
{
if( m_PlaybackPortals[i].iEntIndex == iEntIndex )
{
m_PlaybackPortals[i].iTeamNumber = pKeyValues->GetInt( "teamNumber", 0 );
m_PlaybackPortals[i].bTouched = true;
return;
}
}
//didn't exist, create it.
RecordedPortal_t temp;
temp.iEntIndex = iEntIndex;
temp.bTouched = true;
temp.iTeamNumber = pKeyValues->GetInt( "teamNumber", 0 );
m_PlaybackPortals.AddToTail( temp );
}
virtual void HandleData_RemoveAll( void )
{
m_PlaybackPortals.RemoveAll();
}
struct RecordedPortal_t
{
bool bTouched;
int iEntIndex;
int iTeamNumber;
};
CUtlVector<RecordedPortal_t> m_PlaybackPortals;
};
static C_Prop_Portal_EntityKeyValuesHandler s_ProjectedWallEntityIFMHandler;
void C_Prop_Portal::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
{
BaseClass::HandlePortalPlaybackMessage( pKeyValues );
m_fOpenAmount = pKeyValues->GetFloat( "openAmount" );
m_fStaticAmount = pKeyValues->GetFloat( "staticAmount" );
UpdateTeleportMatrix();
int iEntIndexint = pKeyValues->GetInt( "portalId" );
for( int i = 0; i != s_ProjectedWallEntityIFMHandler.m_PlaybackPortals.Count(); ++i )
{
if( iEntIndexint == s_ProjectedWallEntityIFMHandler.m_PlaybackPortals[i].iEntIndex )
{
m_iTeamNum = s_ProjectedWallEntityIFMHandler.m_PlaybackPortals[i].iTeamNumber;
}
}
}
CPortalRenderable *CreateProp_Portal_Fn( void )
{
return new C_Prop_Portal;
}
static CPortalRenderableCreator_AutoRegister CreateProp_Portal( "Prop_Portal", CreateProp_Portal_Fn );