//===== 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( 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 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( 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( 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 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 );