initial
This commit is contained in:
280
game/server/portal/physicsclonearea.cpp
Normal file
280
game/server/portal/physicsclonearea.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Instead of cloning all physics objects in a level to get proper
|
||||
// near-portal reactions, only clone from a larger area near portals.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "PhysicsCloneArea.h"
|
||||
#include "portal_base2d.h"
|
||||
#include "collisionutils.h"
|
||||
#include "env_debughistory.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( physicsclonearea, CPhysicsCloneArea );
|
||||
|
||||
const float CPhysicsCloneArea::s_fPhysicsCloneAreaScale = 4.0f;
|
||||
//#define PHYSICSCLONEAREASCALE 4.0f
|
||||
|
||||
/*const Vector CPhysicsCloneArea::vLocalMins( 3.0f,
|
||||
-PORTAL_HALF_WIDTH * PHYSICSCLONEAREASCALE,
|
||||
-PORTAL_HALF_HEIGHT * PHYSICSCLONEAREASCALE );
|
||||
const Vector CPhysicsCloneArea::vLocalMaxs( PORTAL_HALF_HEIGHT * PHYSICSCLONEAREASCALE, //x is the forward which is fairly thin for portals, replacing with halfheight
|
||||
PORTAL_HALF_WIDTH * PHYSICSCLONEAREASCALE,
|
||||
PORTAL_HALF_HEIGHT * PHYSICSCLONEAREASCALE );*/
|
||||
|
||||
extern ConVar sv_portal_debug_touch;
|
||||
|
||||
void CPhysicsCloneArea::StartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if( !m_bActive )
|
||||
return;
|
||||
|
||||
if( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
DevMsg( "PortalCloneArea %i Start Touch: %s : %f\n", ((m_pAttachedPortal->m_bIsPortal2)?(2):(1)), pOther->GetClassname(), gpGlobals->curtime );
|
||||
}
|
||||
#if !defined( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "PortalCloneArea %i Start Touch: %s : %f\n", ((m_pAttachedPortal->m_bIsPortal2)?(2):(1)), pOther->GetClassname(), gpGlobals->curtime ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_pAttachedSimulator->StartCloningEntityFromMain( pOther );
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if( !m_bActive )
|
||||
return;
|
||||
|
||||
//TODO: Planar checks to see if it's a better idea to reclone/unclone
|
||||
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::EndTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if( !m_bActive )
|
||||
return;
|
||||
|
||||
if( sv_portal_debug_touch.GetBool() )
|
||||
{
|
||||
DevMsg( "PortalCloneArea %i End Touch: %s : %f\n", ((m_pAttachedPortal->m_bIsPortal2)?(2):(1)), pOther->GetClassname(), gpGlobals->curtime );
|
||||
}
|
||||
#if !defined( DISABLE_DEBUG_HISTORY )
|
||||
if ( !IsMarkedForDeletion() )
|
||||
{
|
||||
ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "PortalCloneArea %i End Touch: %s : %f\n", ((m_pAttachedPortal->m_bIsPortal2)?(2):(1)), pOther->GetClassname(), gpGlobals->curtime ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_pAttachedSimulator->StopCloningEntityFromMain( pOther );
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
Assert( m_pAttachedPortal );
|
||||
|
||||
AddEffects( EF_NORECEIVESHADOW | EF_NOSHADOW | EF_NODRAW );
|
||||
|
||||
SetSolid( SOLID_OBB );
|
||||
SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetCollisionGroup( COLLISION_GROUP_PLAYER );
|
||||
|
||||
m_fHalfWidth = m_pAttachedPortal->GetHalfWidth() * s_fPhysicsCloneAreaScale;
|
||||
m_fHalfHeight = m_pAttachedPortal->GetHalfHeight() * s_fPhysicsCloneAreaScale;
|
||||
m_fHalfDepth = MAX( m_fHalfWidth, m_fHalfHeight );
|
||||
|
||||
SetSize( GetLocalMins(), GetLocalMaxs() );
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::Activate( void )
|
||||
{
|
||||
BaseClass::Activate();
|
||||
}
|
||||
|
||||
int CPhysicsCloneArea::ObjectCaps( void )
|
||||
{
|
||||
return BaseClass::ObjectCaps() | FCAP_DONT_SAVE; //don't save this entity in any way, we naively recreate them
|
||||
}
|
||||
|
||||
|
||||
void CPhysicsCloneArea::UpdatePosition( void )
|
||||
{
|
||||
Assert( m_pAttachedPortal );
|
||||
|
||||
//untouch everything we're touching
|
||||
touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
|
||||
if( root )
|
||||
{
|
||||
//don't want to risk list corruption while untouching
|
||||
CUtlVector<CBaseEntity *> TouchingEnts;
|
||||
for( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
|
||||
TouchingEnts.AddToTail( link->entityTouched );
|
||||
|
||||
|
||||
for( int i = TouchingEnts.Count(); --i >= 0; )
|
||||
{
|
||||
CBaseEntity *pTouch = TouchingEnts[i];
|
||||
|
||||
pTouch->PhysicsNotifyOtherOfUntouch( pTouch, this );
|
||||
PhysicsNotifyOtherOfUntouch( this, pTouch );
|
||||
}
|
||||
}
|
||||
|
||||
//update size as well
|
||||
m_fHalfWidth = m_pAttachedPortal->GetHalfWidth() * s_fPhysicsCloneAreaScale;
|
||||
m_fHalfHeight = m_pAttachedPortal->GetHalfHeight() * s_fPhysicsCloneAreaScale;
|
||||
m_fHalfDepth = MAX( m_fHalfWidth, m_fHalfHeight );
|
||||
SetSize( GetLocalMins(), GetLocalMaxs() );
|
||||
|
||||
SetAbsOrigin( m_pAttachedPortal->GetAbsOrigin() );
|
||||
SetAbsAngles( m_pAttachedPortal->GetAbsAngles() );
|
||||
m_bActive = m_pAttachedPortal->IsActive();
|
||||
|
||||
//NDebugOverlay::EntityBounds( this, 0, 0, 255, 25, 5.0f );
|
||||
|
||||
//RemoveFlag( FL_DONTTOUCH );
|
||||
CloneNearbyEntities(); //wake new objects so they can figure out that they touch
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::CloneNearbyEntities( void )
|
||||
{
|
||||
CBaseEntity* pList[ 1024 ];
|
||||
|
||||
Vector vForward, vUp, vRight;
|
||||
GetVectors( &vForward, &vRight, &vUp );
|
||||
|
||||
Vector ptOrigin = GetAbsOrigin();
|
||||
QAngle qAngles = GetAbsAngles();
|
||||
|
||||
Vector vLocalMins = GetLocalMins();
|
||||
Vector vLocalMaxs = GetLocalMaxs();
|
||||
|
||||
Vector ptOBBStart = ptOrigin;
|
||||
ptOBBStart += vForward * vLocalMins.x;
|
||||
ptOBBStart += vRight * vLocalMins.y;
|
||||
ptOBBStart += vUp * vLocalMins.z;
|
||||
|
||||
|
||||
vForward *= vLocalMaxs.x - vLocalMins.x;
|
||||
vRight *= vLocalMaxs.y - vLocalMins.y;
|
||||
vUp *= vLocalMaxs.z - vLocalMins.z;
|
||||
|
||||
|
||||
Vector vAABBMins, vAABBMaxs;
|
||||
vAABBMins = vAABBMaxs = ptOBBStart;
|
||||
|
||||
for( int i = 1; i != 8; ++i )
|
||||
{
|
||||
Vector ptTest = ptOBBStart;
|
||||
if( i & (1 << 0) ) ptTest += vForward;
|
||||
if( i & (1 << 1) ) ptTest += vRight;
|
||||
if( i & (1 << 2) ) ptTest += vUp;
|
||||
|
||||
if( ptTest.x < vAABBMins.x ) vAABBMins.x = ptTest.x;
|
||||
if( ptTest.y < vAABBMins.y ) vAABBMins.y = ptTest.y;
|
||||
if( ptTest.z < vAABBMins.z ) vAABBMins.z = ptTest.z;
|
||||
if( ptTest.x > vAABBMaxs.x ) vAABBMaxs.x = ptTest.x;
|
||||
if( ptTest.y > vAABBMaxs.y ) vAABBMaxs.y = ptTest.y;
|
||||
if( ptTest.z > vAABBMaxs.z ) vAABBMaxs.z = ptTest.z;
|
||||
}
|
||||
|
||||
|
||||
/*{
|
||||
Vector ptAABBCenter = (vAABBMins + vAABBMaxs) * 0.5f;
|
||||
Vector vAABBExtent = (vAABBMaxs - vAABBMins) * 0.5f;
|
||||
NDebugOverlay::Box( ptAABBCenter, -vAABBExtent, vAABBExtent, 0, 0, 255, 128, 10.0f );
|
||||
}*/
|
||||
|
||||
|
||||
int count = UTIL_EntitiesInBox( pList, 1024, vAABBMins, vAABBMaxs, 0 );
|
||||
trace_t tr;
|
||||
UTIL_ClearTrace( tr );
|
||||
|
||||
|
||||
//Iterate over all the possible targets
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
CBaseEntity *pEntity = pList[i];
|
||||
|
||||
if ( pEntity && (pEntity != this) )
|
||||
{
|
||||
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
|
||||
|
||||
if( pPhysicsObject )
|
||||
{
|
||||
CCollisionProperty *pEntCollision = pEntity->CollisionProp();
|
||||
Vector ptEntityCenter = pEntCollision->GetCollisionOrigin();
|
||||
|
||||
//double check intersection at the OBB vs OBB level, we don't want to affect large piles of physics objects if we don't have to, it gets slow
|
||||
if( IsOBBIntersectingOBB( ptOrigin, qAngles, vLocalMins, vLocalMaxs,
|
||||
ptEntityCenter, pEntCollision->GetCollisionAngles(), pEntCollision->OBBMins(), pEntCollision->OBBMaxs() ) )
|
||||
{
|
||||
tr.endpos = (ptOrigin + ptEntityCenter) * 0.5;
|
||||
PhysicsMarkEntitiesAsTouching( pEntity, tr );
|
||||
//StartTouch( pEntity );
|
||||
|
||||
//pEntity->WakeRestingObjects();
|
||||
//pPhysicsObject->Wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPhysicsCloneArea::CloneTouchingEntities( void )
|
||||
{
|
||||
if( m_pAttachedPortal && m_pAttachedPortal->IsActive() )
|
||||
{
|
||||
touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
|
||||
if( root )
|
||||
{
|
||||
for( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
|
||||
m_pAttachedSimulator->StartCloningEntityFromMain( link->entityTouched );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CPhysicsCloneArea *CPhysicsCloneArea::CreatePhysicsCloneArea( CPortal_Base2D *pFollowPortal )
|
||||
{
|
||||
if( !pFollowPortal )
|
||||
return NULL;
|
||||
|
||||
CPhysicsCloneArea *pCloneArea = (CPhysicsCloneArea *)CreateEntityByName( "physicsclonearea" );
|
||||
|
||||
pCloneArea->m_pAttachedPortal = pFollowPortal;
|
||||
pCloneArea->m_pAttachedSimulator = &pFollowPortal->m_PortalSimulator;
|
||||
|
||||
DispatchSpawn( pCloneArea );
|
||||
|
||||
pCloneArea->UpdatePosition();
|
||||
|
||||
return pCloneArea;
|
||||
}
|
||||
|
||||
|
||||
void CPhysicsCloneArea::Resize( float fPortalHalfWidth, float fPortalHalfHeight )
|
||||
{
|
||||
fPortalHalfWidth *= s_fPhysicsCloneAreaScale;
|
||||
fPortalHalfHeight *= s_fPhysicsCloneAreaScale;
|
||||
|
||||
if( (fPortalHalfWidth == m_fHalfWidth) && (fPortalHalfHeight == m_fHalfHeight) )
|
||||
return;
|
||||
|
||||
m_fHalfWidth = fPortalHalfWidth;
|
||||
m_fHalfHeight = fPortalHalfHeight;
|
||||
m_fHalfDepth = MAX( m_fHalfWidth, m_fHalfHeight );
|
||||
SetSize( GetLocalMins(), GetLocalMaxs() );
|
||||
UpdatePosition();
|
||||
}
|
||||
|
||||
60
game/server/portal/physicsclonearea.h
Normal file
60
game/server/portal/physicsclonearea.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PHYSICSCLONEAREA_H
|
||||
#define PHYSICSCLONEAREA_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "baseentity.h"
|
||||
|
||||
class CPortal_Base2D;
|
||||
class CPortalSimulator;
|
||||
|
||||
|
||||
class CPhysicsCloneArea : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPhysicsCloneArea, CBaseEntity );
|
||||
|
||||
//static const Vector vLocalMins;
|
||||
//static const Vector vLocalMaxs;
|
||||
|
||||
virtual void StartTouch( CBaseEntity *pOther );
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
virtual void EndTouch( CBaseEntity *pOther );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Activate( void );
|
||||
|
||||
virtual int ObjectCaps( void );
|
||||
void UpdatePosition( void );
|
||||
|
||||
void CloneTouchingEntities( void );
|
||||
void CloneNearbyEntities( void );
|
||||
static CPhysicsCloneArea *CreatePhysicsCloneArea( CPortal_Base2D *pFollowPortal );
|
||||
|
||||
inline Vector GetLocalMins( void ) const { return Vector( 3.0f, -m_fHalfWidth, -m_fHalfHeight ); }
|
||||
inline Vector GetLocalMaxs( void ) const { return Vector( m_fHalfDepth, m_fHalfWidth, m_fHalfHeight ); }
|
||||
|
||||
void Resize( float fPortalHalfWidth, float fPortalHalfHeight );
|
||||
private:
|
||||
|
||||
CPortal_Base2D *m_pAttachedPortal;
|
||||
CPortalSimulator *m_pAttachedSimulator;
|
||||
bool m_bActive;
|
||||
|
||||
float m_fHalfWidth, m_fHalfHeight, m_fHalfDepth;
|
||||
static const float s_fPhysicsCloneAreaScale;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif //#ifndef PHYSICSCLONEAREA_H
|
||||
|
||||
1220
game/server/portal/physicsshadowclone.cpp
Normal file
1220
game/server/portal/physicsshadowclone.cpp
Normal file
File diff suppressed because it is too large
Load Diff
148
game/server/portal/physicsshadowclone.h
Normal file
148
game/server/portal/physicsshadowclone.h
Normal file
@@ -0,0 +1,148 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Clones a physics object by use of shadows
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PHYSICSSHADOWCLONE_H
|
||||
#define PHYSICSSHADOWCLONE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "vphysics_interface.h"
|
||||
#include "BaseEntity.h"
|
||||
#include "baseanimating.h"
|
||||
|
||||
class CPhysicsShadowClone;
|
||||
|
||||
struct PhysicsObjectCloneLink_t
|
||||
{
|
||||
IPhysicsObject *pSource;
|
||||
IPhysicsShadowController *pShadowController;
|
||||
IPhysicsObject *pClone;
|
||||
};
|
||||
|
||||
struct CPhysicsShadowCloneLL
|
||||
{
|
||||
CPhysicsShadowClone *pClone;
|
||||
CPhysicsShadowCloneLL *pNext;
|
||||
};
|
||||
|
||||
#define FVPHYSICS_IS_SHADOWCLONE 0x4000
|
||||
|
||||
class CPhysicsShadowClone : public CBaseAnimating
|
||||
{
|
||||
DECLARE_CLASS( CPhysicsShadowClone, CBaseAnimating );
|
||||
|
||||
private:
|
||||
EHANDLE m_hClonedEntity; //the entity we're supposed to be cloning the physics of
|
||||
VMatrix m_matrixShadowTransform; //all cloned coordinates and angles will be run through this matrix before being applied
|
||||
VMatrix m_matrixShadowTransform_Inverse;
|
||||
|
||||
CUtlVector<PhysicsObjectCloneLink_t> m_CloneLinks; //keeps track of which of our physics objects are linked to the source's objects
|
||||
bool m_bShadowTransformIsIdentity; //the shadow transform doesn't update often, so we can cache this
|
||||
bool m_bImmovable; //cloning a track train or door, something that doesn't really work on a force-based level
|
||||
bool m_bInAssumedSyncState;
|
||||
|
||||
void FullSyncClonedPhysicsObjects( bool bTeleport );
|
||||
void SyncEntity( bool bPullChanges );
|
||||
|
||||
IPhysicsEnvironment *m_pOwnerPhysEnvironment; //clones exist because of multi-environment situations
|
||||
|
||||
|
||||
public:
|
||||
CPhysicsShadowClone( void );
|
||||
virtual ~CPhysicsShadowClone( void );
|
||||
|
||||
bool m_bShouldUpSync;
|
||||
DBG_CODE_NOSCOPE( const char *m_szDebugMarker; );
|
||||
|
||||
//do the thing with the stuff, you know, the one that goes WooooWooooWooooWooooWoooo
|
||||
virtual void Spawn( void );
|
||||
|
||||
//crush, kill, DESTROY!!!!!
|
||||
void Free( void );
|
||||
|
||||
//syncs to the source entity in every way possible, assumed sync does some rudimentary tests to see if the object is in sync, and if so, skips the update
|
||||
void FullSync( bool bAllowAssumedSync = false );
|
||||
|
||||
//syncs just the physics objects, bPullChanges should be true when this clone should match it's source, false when it should force differences onto the source entity
|
||||
void PartialSync( bool bPullChanges );
|
||||
|
||||
//virtual bool CreateVPhysics( void );
|
||||
virtual void VPhysicsDestroyObject( void );
|
||||
virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax );
|
||||
virtual int ObjectCaps( void );
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
||||
|
||||
|
||||
//routing to the source entity for cloning goodness
|
||||
virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const;
|
||||
|
||||
//avoid blocking traces that are supposed to hit our source entity
|
||||
virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
|
||||
|
||||
|
||||
|
||||
|
||||
//is this clone occupying the exact same space as the object it's cloning?
|
||||
inline bool IsUntransformedClone( void ) const { return m_bShadowTransformIsIdentity; };
|
||||
void SetCloneTransformationMatrix( const matrix3x4_t &matTransform );
|
||||
|
||||
inline bool IsInAssumedSyncState( void ) const { return m_bInAssumedSyncState; }
|
||||
inline IPhysicsEnvironment *GetOwnerEnvironment( void ) const { return m_pOwnerPhysEnvironment; }
|
||||
|
||||
//what entity are we cloning?
|
||||
void SetClonedEntity( EHANDLE hEntToClone );
|
||||
EHANDLE GetClonedEntity( void );
|
||||
|
||||
|
||||
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
|
||||
|
||||
//damage relays to source entity if anything ever hits the clone
|
||||
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
||||
virtual bool CanBeHitByMeleeAttack( CBaseEntity *pAttacker );
|
||||
virtual int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
|
||||
static CPhysicsShadowClone *CreateShadowClone( IPhysicsEnvironment *pInPhysicsEnvironment, EHANDLE hEntToClone, const char *szDebugMarker, const matrix3x4_t *pTransformationMatrix = NULL );
|
||||
|
||||
//given a physics object that is part of this clone, tells you which physics object in the source
|
||||
IPhysicsObject *TranslatePhysicsToClonedEnt( const IPhysicsObject *pPhysics );
|
||||
|
||||
static bool IsShadowClone( const CBaseEntity *pEntity );
|
||||
static CPhysicsShadowCloneLL *GetClonesOfEntity( const CBaseEntity *pEntity );
|
||||
static void FullSyncAllClones( void );
|
||||
|
||||
static CUtlVector<CPhysicsShadowClone *> const &g_ShadowCloneList;
|
||||
|
||||
friend void DrawDebugOverlayForShadowClone( CPhysicsShadowClone *pClone );
|
||||
|
||||
//only really necessary to call for entities that create custom collideables.
|
||||
static void NotifyDestroy( IPhysicsObject *pDestroyingPhys, CBaseEntity *pOwningEntity = NULL ); //passing in the original owner entity just makes the search faster
|
||||
static void NotifyDestroy( CPhysCollide *pDestroyingCollide, CBaseEntity *pOwningEntity = NULL ); //passing in the original owner entity just makes the search faster
|
||||
|
||||
private:
|
||||
void DestroyClonedPhys( IPhysicsObject *pPhys );
|
||||
void DestroyClonedCollideable( CPhysCollide *pCollide );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CTraceFilterTranslateClones : public CTraceFilter //give it another filter, and it'll translate shadow clones into their source entity for tests
|
||||
{
|
||||
ITraceFilter *m_pActualFilter; //the filter that tests should be forwarded to after translating clones
|
||||
|
||||
public:
|
||||
CTraceFilterTranslateClones( ITraceFilter *pOtherFilter ) : m_pActualFilter(pOtherFilter) {};
|
||||
virtual bool ShouldHitEntity( IHandleEntity *pEntity, int contentsMask );
|
||||
virtual TraceType_t GetTraceType() const;
|
||||
};
|
||||
|
||||
#endif //#ifndef PHYSICSSHADOWCLONE_H
|
||||
2088
game/server/portal/portal_base2d.cpp
Normal file
2088
game/server/portal/portal_base2d.cpp
Normal file
File diff suppressed because it is too large
Load Diff
231
game/server/portal/portal_base2d.h
Normal file
231
game/server/portal/portal_base2d.h
Normal file
@@ -0,0 +1,231 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef PORTAL_BASE2D_H
|
||||
#define PORTAL_BASE2D_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "baseanimating.h"
|
||||
#include "PortalSimulation.h"
|
||||
#include "pvs_extender.h"
|
||||
#include "portal_base2d_shared.h"
|
||||
|
||||
// FIX ME
|
||||
#include "portal_shareddefs.h"
|
||||
|
||||
class CPhysicsCloneArea;
|
||||
|
||||
class CPortal_Base2D : public CBaseAnimating, public CPortalSimulatorEventCallbacks, public CPVS_Extender, public CPortal_Base2D_Shared
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPortal_Base2D, CBaseAnimating );
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CPortal_Base2D( void );
|
||||
virtual ~CPortal_Base2D( void );
|
||||
|
||||
CNetworkHandle( CPortal_Base2D, m_hLinkedPortal ); //the portal this portal is linked to
|
||||
|
||||
|
||||
VMatrix m_matrixThisToLinked; //the matrix that will transform a point relative to this portal, to a point relative to the linked portal
|
||||
CNetworkVar( bool, m_bIsPortal2 ); //For teleportation, this doesn't matter, but for drawing and moving, it matters
|
||||
Vector m_vPrevForward; //used for the indecisive push in find closest passable spaces when portal is moved
|
||||
|
||||
bool m_bSharedEnvironmentConfiguration; //this will be set by an instance of CPortal_Environment when two environments are in close proximity
|
||||
|
||||
EHANDLE m_hMicrophone; //the microphone for teleporting sound
|
||||
EHANDLE m_hSpeaker; //the speaker for teleported sound
|
||||
bool m_bMicAndSpeakersLinkedToRemote;
|
||||
|
||||
Vector m_vAudioOrigin;
|
||||
Vector m_vDelayedPosition;
|
||||
QAngle m_qDelayedAngles;
|
||||
int m_iDelayedFailure;
|
||||
Vector m_vOldPosition;
|
||||
QAngle m_qOldAngles;
|
||||
EHANDLE m_hPlacedBy;
|
||||
int m_nPortalColor;
|
||||
|
||||
COutputEvent m_OnPlacedSuccessfully; // Output in hammer for when this portal was successfully placed (not attempted and fizzed).
|
||||
COutputEvent m_OnEntityTeleportFromMe;
|
||||
COutputEvent m_OnPlayerTeleportFromMe;
|
||||
COutputEvent m_OnEntityTeleportToMe;
|
||||
COutputEvent m_OnPlayerTeleportToMe;
|
||||
|
||||
CNetworkVector( m_ptOrigin );
|
||||
Vector m_vForward, m_vUp, m_vRight;
|
||||
CNetworkQAngle( m_qAbsAngle );
|
||||
cplane_t m_plane_Origin; //a portal plane on the entity origin
|
||||
|
||||
CPhysicsCloneArea *m_pAttachedCloningArea;
|
||||
|
||||
bool IsPortal2() const;
|
||||
void SetIsPortal2( bool bIsPortal2 );
|
||||
const VMatrix& MatrixThisToLinked() const;
|
||||
|
||||
virtual int UpdateTransmitState( void ) // set transmit filter to transmit always
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Activate( void );
|
||||
virtual void OnRestore( void );
|
||||
virtual bool IsActive( void ) const { return m_bActivated; }
|
||||
virtual bool GetOldActiveState( void ) const { return m_bOldActivatedState; }
|
||||
virtual void SetActive( bool bActive );
|
||||
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
||||
void TestRestingSurfaceThink( void );
|
||||
static const char * s_szTestRestingSurfaceThinkContext;
|
||||
|
||||
void DeactivatePortalOnThink( void );
|
||||
void DeactivatePortalNow( void );
|
||||
static const char * s_szDeactivatePortalNowContext;
|
||||
|
||||
virtual void OnPortalDeactivated( void );
|
||||
|
||||
bool IsActivedAndLinked( void ) const;
|
||||
|
||||
bool IsFloorPortal( float fThreshold = 0.8f ) const;
|
||||
bool IsCeilingPortal( float fThreshold = -0.8f ) const;
|
||||
|
||||
void WakeNearbyEntities( void ); //wakes all nearby entities in-case there's been a significant change in how they can rest near a portal
|
||||
|
||||
void ForceEntityToFitInPortalWall( CBaseEntity *pEntity ); //projects an object's center into the middle of the portal wall hall, and traces back to where it wants to be
|
||||
|
||||
virtual void NewLocation( const Vector &vOrigin, const QAngle &qAngles );
|
||||
|
||||
void PunchPenetratingPlayer( CBasePlayer *pPlayer ); // adds outward force to player intersecting the portal plane
|
||||
void PunchAllPenetratingPlayers( void ); // adds outward force to player intersecting the portal plane
|
||||
|
||||
virtual void StartTouch( CBaseEntity *pOther );
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
virtual void EndTouch( CBaseEntity *pOther );
|
||||
bool ShouldTeleportTouchingEntity( CBaseEntity *pOther ); //assuming the entity is or was just touching the portal, check for teleportation conditions
|
||||
void TeleportTouchingEntity( CBaseEntity *pOther );
|
||||
virtual void PreTeleportTouchingEntity( CBaseEntity *pOther ) {};
|
||||
virtual void PostTeleportTouchingEntity( CBaseEntity *pOther ) {};
|
||||
|
||||
virtual void PhysicsSimulate( void );
|
||||
|
||||
virtual void UpdatePortalLinkage( void );
|
||||
void UpdatePortalTeleportMatrix( void ); //computes the transformation from this portal to the linked portal, and will update the remote matrix as well
|
||||
|
||||
//void SendInteractionMessage( CBaseEntity *pEntity, bool bEntering ); //informs clients that the entity is interacting with a portal (mostly used for clip planes)
|
||||
|
||||
bool SharedEnvironmentCheck( CBaseEntity *pEntity ); //does all testing to verify that the object is better handled with this portal instead of the other
|
||||
|
||||
// The four corners of the portal in worldspace, updated on placement. The four points will be coplanar on the portal plane.
|
||||
Vector m_vPortalCorners[4];
|
||||
|
||||
CNetworkVarEmbedded( CPortalSimulator, m_PortalSimulator );
|
||||
|
||||
//virtual bool CreateVPhysics( void );
|
||||
//virtual void VPhysicsDestroyObject( void );
|
||||
|
||||
virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
|
||||
|
||||
virtual void PortalSimulator_TookOwnershipOfEntity( CBaseEntity *pEntity );
|
||||
virtual void PortalSimulator_ReleasedOwnershipOfEntity( CBaseEntity *pEntity );
|
||||
|
||||
virtual void CreateMicAndSpeaker( void );
|
||||
|
||||
// Add or remove listeners
|
||||
void AddPortalEventListener( EHANDLE hListener );
|
||||
void RemovePortalEventListener( EHANDLE hListener );
|
||||
|
||||
void OnEntityTeleportedToPortal( CBaseEntity *pEntity );
|
||||
void OnEntityTeleportedFromPortal( CBaseEntity *pEntity );
|
||||
|
||||
protected:
|
||||
CNetworkVar( bool, m_bActivated ); //a portal can exist and not be active
|
||||
CNetworkVar( bool, m_bOldActivatedState ); //the old state
|
||||
|
||||
void BroadcastPortalEvent( PortalEvent_t nEventType );
|
||||
|
||||
CUtlVector<EHANDLE> m_PortalEventListeners; // Collection of entities (by handle) who wish to receive notification of portal events (fizzle, moved, etc)
|
||||
|
||||
void RemovePortalMicAndSpeaker(); // Cleans up the portal's internal audio members
|
||||
void UpdateCorners( void ); // Updates the four corners of this portal on spawn and placement
|
||||
void UpdateClientCheckPVS( void ); // Tells clients to update the cached PVS in g_ClientCheck
|
||||
void UpdateCollisionShape( void );
|
||||
|
||||
CNetworkVar( float, m_fNetworkHalfWidth );
|
||||
CNetworkVar( float, m_fNetworkHalfHeight );
|
||||
CNetworkVar( bool, m_bIsMobile ); //is this portal currently making small movements along with whatever brush it's attached to? Portal physics are disabled while nudging and resume when stabilized
|
||||
|
||||
CPhysCollide *m_pCollisionShape;
|
||||
|
||||
public:
|
||||
CPortal_Base2D *GetLinkedPortal( void ) { return m_hLinkedPortal; }
|
||||
|
||||
inline float GetHalfWidth( void ) const { return m_fNetworkHalfWidth; }
|
||||
inline float GetHalfHeight( void ) const { return m_fNetworkHalfHeight; }
|
||||
inline Vector GetLocalMins( void ) const { return Vector( 0.0f, -m_fNetworkHalfWidth, -m_fNetworkHalfHeight ); }
|
||||
inline Vector GetLocalMaxs( void ) const { return Vector( 64.0f, m_fNetworkHalfWidth, m_fNetworkHalfHeight ); }
|
||||
//inline void SetHalfSizes( float fHalfWidth, float fHalfHeight ) { m_fHalfWidth = fHalfWidth; m_fHalfHeight = fHalfHeight; }
|
||||
|
||||
inline bool IsMobile( void ) const { return m_bIsMobile; }
|
||||
void SetMobileState( bool bSet );
|
||||
|
||||
void Resize( float fHalfWidth, float fHalfHeight );
|
||||
|
||||
|
||||
virtual CServerNetworkProperty *GetExtenderNetworkProp( void );
|
||||
virtual const edict_t *GetExtenderEdict( void ) const;
|
||||
virtual Vector GetExtensionPVSOrigin( void );
|
||||
|
||||
virtual bool IsExtenderValid( void );
|
||||
|
||||
//to whittle down views through recursive portals, we clip the portal's planar polygon by a frustum, then fit a new (smaller) frustum to that polygon. These two let you specify the polygon we clip and fit to
|
||||
virtual int GetPolyVertCount( void );
|
||||
virtual int ComputeFrustumThroughPolygon( const Vector &vVisOrigin, const VPlane *pInputFrustum, int iInputFrustumPlanes, VPlane *pOutputFrustum, int iOutputFrustumMaxPlanes );
|
||||
|
||||
//This portal is decidedly visible, recursively extend the visibility problem
|
||||
virtual void ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft );
|
||||
|
||||
//it shouldn't matter, but the convention should be that we query the exit portal for these values
|
||||
virtual float GetMinimumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity ); //return -FLT_MAX for no minimum
|
||||
virtual float GetMaximumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity ); //return FLT_MAX for no maximum
|
||||
|
||||
//does all the gruntwork of figuring out flooriness and calling the two above
|
||||
static void GetExitSpeedRange( CPortal_Base2D *pEntrancePortal, bool bPlayer, float &fExitMinimum, float &fExitMaximum, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity );
|
||||
|
||||
private:
|
||||
Vector m_vPortalSpawnLocation; // use this position to check against portal->AbsOrigin of the portal to see if it's moving too far (moving portal is very bad)
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// inline state querying methods
|
||||
//-----------------------------------------------------------------------------
|
||||
inline bool CPortal_Base2D::IsPortal2() const
|
||||
{
|
||||
return m_bIsPortal2;
|
||||
}
|
||||
|
||||
inline void CPortal_Base2D::SetIsPortal2( bool bIsPortal2 )
|
||||
{
|
||||
m_bIsPortal2 = bIsPortal2;
|
||||
}
|
||||
|
||||
inline const VMatrix& CPortal_Base2D::MatrixThisToLinked() const
|
||||
{
|
||||
return m_matrixThisToLinked;
|
||||
}
|
||||
|
||||
|
||||
void AddPortalVisibilityToPVS( CPortal_Base2D* pPortal, int outputpvslength, unsigned char *outputpvs );
|
||||
void EntityPortalled( CPortal_Base2D *pPortal, CBaseEntity *pOther, const Vector &vNewOrigin, const QAngle &qNewAngles, bool bForcedDuck );
|
||||
|
||||
extern ConVar sv_allow_mobile_portals;
|
||||
|
||||
#endif //#ifndef PORTAL_BASE2D_H
|
||||
543
game/server/portal/portal_physics_collisionevent.cpp
Normal file
543
game/server/portal/portal_physics_collisionevent.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "portal_physics_collisionevent.h"
|
||||
#include "physicsshadowclone.h"
|
||||
#include "prop_combine_ball.h"
|
||||
#include "portal_player.h"
|
||||
#include "portal/weapon_physcannon.h" //grab controller
|
||||
|
||||
|
||||
#define DEBUG_COLLISION_RULES 0
|
||||
|
||||
#if (DEBUG_COLLISION_RULES == 1)
|
||||
ConVar sv_watchcollision_1( "sv_watchcollision_1", "-1" );
|
||||
ConVar sv_watchcollision_2( "sv_watchcollision_2", "-1" );
|
||||
#endif
|
||||
|
||||
int CPortal_CollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
|
||||
{
|
||||
if ( !pGameData0 || !pGameData1 )
|
||||
return 1;
|
||||
|
||||
#if (DEBUG_COLLISION_RULES == 1)
|
||||
bool bBreak = false;
|
||||
if( ((((CBaseEntity *)pGameData0)->entindex() == sv_watchcollision_1.GetInt()) || (((CBaseEntity *)pGameData0)->entindex() == sv_watchcollision_2.GetInt())) &&
|
||||
((((CBaseEntity *)pGameData1)->entindex() == sv_watchcollision_1.GetInt()) || (((CBaseEntity *)pGameData1)->entindex() == sv_watchcollision_2.GetInt())) )
|
||||
{
|
||||
bBreak = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
AssertOnce( pObj0 && pObj1 );
|
||||
uint nFlags0 = pObj0->GetGameFlags();
|
||||
uint nFlags1 = pObj1->GetGameFlags();
|
||||
uint nAllFlags = nFlags0 | nFlags1;
|
||||
|
||||
bool bShadowClonesInvolved = ( nAllFlags & FVPHYSICS_IS_SHADOWCLONE) != 0;
|
||||
|
||||
if( bShadowClonesInvolved )
|
||||
{
|
||||
//at least one shadow clone
|
||||
|
||||
if( (nFlags0 & nFlags1) & FVPHYSICS_IS_SHADOWCLONE )
|
||||
return 0; //both are shadow clones
|
||||
|
||||
if( nAllFlags & FVPHYSICS_PLAYER_HELD )
|
||||
{
|
||||
//at least one is held
|
||||
|
||||
//don't let players collide with objects they're holding, they get kinda messed up sometimes
|
||||
if( pGameData0 && ((CBaseEntity *)pGameData0)->IsPlayer() && (GetPlayerHeldEntity( (CBasePlayer *)pGameData0 ) == (CBaseEntity *)pGameData1) )
|
||||
return 0;
|
||||
|
||||
if( pGameData1 && ((CBaseEntity *)pGameData1)->IsPlayer() && (GetPlayerHeldEntity( (CBasePlayer *)pGameData1 ) == (CBaseEntity *)pGameData0) )
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//everything is in one environment. This means we must tightly control what collides with what
|
||||
if( pGameData0 != pGameData1 )
|
||||
{
|
||||
//this code only decides what CAN'T collide due to portal environment differences, things that should collide will pass through here to deeper ShouldCollide() code
|
||||
bool bStatic[2] = { pObj0->IsStatic(), pObj1->IsStatic() };
|
||||
AssertOnce( (bStatic[0] && bStatic[1]) == false ); //hopefully the system doesn't even call in for this, they're both static and can't collide
|
||||
if( bStatic[0] && bStatic[1] )
|
||||
return 0;
|
||||
|
||||
CBaseEntity *pEntities[2] = { (CBaseEntity *)pGameData0, (CBaseEntity *)pGameData1 };
|
||||
IPhysicsObject *pPhysObjects[2] = { pObj0, pObj1 };
|
||||
CPortalSimulator *pSimulators[2];
|
||||
pSimulators[0] = CPortalSimulator::GetSimulatorThatOwnsEntity( (CBaseEntity *)pGameData0 );
|
||||
pSimulators[1] = CPortalSimulator::GetSimulatorThatOwnsEntity( (CBaseEntity *)pGameData1 );
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( (pSimulators[i] != NULL) && CPhysicsShadowClone::IsShadowClone( pEntities[i] ) )
|
||||
{
|
||||
CPhysicsShadowClone *pClone = (CPhysicsShadowClone *)pEntities[i];
|
||||
CBaseEntity *pSource = pClone->GetClonedEntity();
|
||||
|
||||
CPortalSimulator *pSourceSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pClone )->GetLinkedPortalSimulator();
|
||||
Assert( (pSimulators[i]->GetInternalData().Simulation.Dynamic.EntFlags[pClone->entindex()] & PSEF_IS_IN_PORTAL_HOLE) == (pSourceSimulator->GetInternalData().Simulation.Dynamic.EntFlags[pSource->entindex()] & PSEF_IS_IN_PORTAL_HOLE) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( (pSimulators[0] == pSimulators[1]) ) //same simulator
|
||||
{
|
||||
if( pSimulators[0] != NULL ) //and not main world
|
||||
{
|
||||
if( bStatic[0] || bStatic[1] )
|
||||
{
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( bStatic[i] )
|
||||
{
|
||||
if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntities[i] ) )
|
||||
{
|
||||
PS_PhysicsObjectSourceType_t objectSource;
|
||||
if( pSimulators[i]->CreatedPhysicsObject( pPhysObjects[i], &objectSource ) &&
|
||||
((objectSource == PSPOST_REMOTE_BRUSHES) || (objectSource == PSPOST_REMOTE_STATICPROPS)) )
|
||||
{
|
||||
if( (pSimulators[1-i]->GetInternalData().Simulation.Dynamic.EntFlags[pEntities[1-i]->entindex()] & PSEF_IS_IN_PORTAL_HOLE) == 0 )
|
||||
return 0; //require that the entity be in the portal hole before colliding with transformed geometry
|
||||
//FIXME: The above requirement might fail horribly for transformed collision blocking the portal from the other side and fast moving objects
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( bShadowClonesInvolved )
|
||||
{
|
||||
if( ((pSimulators[0]->GetInternalData().Simulation.Dynamic.EntFlags[pEntities[0]->entindex()] |
|
||||
pSimulators[1]->GetInternalData().Simulation.Dynamic.EntFlags[pEntities[1]->entindex()]) &
|
||||
PSEF_IS_IN_PORTAL_HOLE) == 0 )
|
||||
{
|
||||
return 0; //neither entity was actually in the portal hole
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else //different simulators
|
||||
{
|
||||
if( bShadowClonesInvolved ) //entities can only collide with shadow clones "owned" by the same simulator.
|
||||
return 0;
|
||||
|
||||
if( bStatic[0] || bStatic[1] )
|
||||
{
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( bStatic[i] )
|
||||
{
|
||||
int j = 1-i;
|
||||
CPortalSimulator *pSimulator_Entity = pSimulators[j];
|
||||
|
||||
if( pEntities[i]->IsWorld() )
|
||||
{
|
||||
Assert( CPortalSimulator::GetSimulatorThatCreatedPhysicsObject( pPhysObjects[i] ) == NULL );
|
||||
if( pSimulator_Entity )
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPortalSimulator *pSimulator_Static = CPortalSimulator::GetSimulatorThatCreatedPhysicsObject( pPhysObjects[i] ); //might have been a static prop which would yield a new simulator
|
||||
|
||||
if( pSimulator_Static )
|
||||
{
|
||||
if(pSimulator_Static != pSimulator_Entity)
|
||||
return 0; //static collideable is from a different simulator
|
||||
}
|
||||
else if( pSimulator_Entity )
|
||||
{
|
||||
if( (pSimulator_Entity->GetInternalData().Simulation.Dynamic.EntFlags[pEntities[i]->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) == 0 )
|
||||
{
|
||||
//entity is in a portal environment, static is not, static not cloned from main.
|
||||
if( !pPhysObjects[i]->IsTrigger() ) //we should probably do this with triggers too. But it breaks tractor beams in devtest when the cube portals, too late in the ship cycle to chase the sweater thread without a good reason
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntities[0] ) == false );
|
||||
Assert( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntities[1] ) == false );
|
||||
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( pSimulators[i] )
|
||||
{
|
||||
//entities in the physics environment only collide with statics created by the environment (handled above), entities in the same environment (also above), or entities that should be cloned from main to the same environment
|
||||
if( (pSimulators[i]->GetInternalData().Simulation.Dynamic.EntFlags[pEntities[1-i]->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) == 0 ) //not cloned from main
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::ShouldCollide( pObj0, pObj1, pGameData0, pGameData1 );
|
||||
}
|
||||
|
||||
|
||||
static bool s_bPenetrationSolvingDisabled = false;
|
||||
static CUtlStack<bool> s_DisablePenetatrationSolving;
|
||||
|
||||
void CPortal_CollisionEvent::DisablePenetrationSolving_Push( bool bDisable )
|
||||
{
|
||||
s_DisablePenetatrationSolving.Push( bDisable );
|
||||
s_bPenetrationSolvingDisabled = bDisable;
|
||||
}
|
||||
|
||||
void CPortal_CollisionEvent::DisablePenetrationSolving_Pop( void )
|
||||
{
|
||||
s_DisablePenetatrationSolving.Pop();
|
||||
if( s_DisablePenetatrationSolving.Count() > 0 )
|
||||
{
|
||||
s_bPenetrationSolvingDisabled = s_DisablePenetatrationSolving.Top();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_bPenetrationSolvingDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CPortal_CollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
|
||||
{
|
||||
if( s_bPenetrationSolvingDisabled )
|
||||
return 0;
|
||||
|
||||
if( (pGameData0 == NULL) || (pGameData1 == NULL) )
|
||||
return 0;
|
||||
|
||||
// For portal, don't solve penetrations on combine balls
|
||||
if( FClassnameIs( (CBaseEntity *)pGameData0, "prop_energy_ball" ) ||
|
||||
FClassnameIs( (CBaseEntity *)pGameData1, "prop_energy_ball" ) )
|
||||
return 0;
|
||||
|
||||
if( (pObj0->GetGameFlags() | pObj1->GetGameFlags()) & FVPHYSICS_PLAYER_HELD )
|
||||
{
|
||||
//at least one is held
|
||||
CBaseEntity *pHeld;
|
||||
CBaseEntity *pOther;
|
||||
IPhysicsObject *pPhysHeld;
|
||||
IPhysicsObject *pPhysOther;
|
||||
if( pObj0->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
|
||||
{
|
||||
pHeld = (CBaseEntity *)pGameData0;
|
||||
pPhysHeld = pObj0;
|
||||
pOther = (CBaseEntity *)pGameData1;
|
||||
pPhysOther = pObj1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pHeld = (CBaseEntity *)pGameData1;
|
||||
pPhysHeld = pObj1;
|
||||
pOther = (CBaseEntity *)pGameData0;
|
||||
pPhysOther = pObj0;
|
||||
}
|
||||
|
||||
//don't let players collide with objects they're holding, they get kinda messed up sometimes
|
||||
if( pOther->IsPlayer() && GetPlayerHeldEntity( (CBasePlayer *)pOther ) == pHeld )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//held objects are clipping into other objects when travelling across a portal. We're close to ship, so this seems to be the
|
||||
//most localized way to make a fix.
|
||||
//Note that we're not actually going to change whether it should solve, we're just going to tack on some hacks
|
||||
CPortal_Player *pHoldingPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pHeld );
|
||||
if( !pHoldingPlayer && CPhysicsShadowClone::IsShadowClone( pHeld ) )
|
||||
{
|
||||
pHoldingPlayer = (CPortal_Player *)GetPlayerHoldingEntity( ((CPhysicsShadowClone *)pHeld)->GetClonedEntity() );
|
||||
}
|
||||
|
||||
Assert( pHoldingPlayer );
|
||||
if ( pHoldingPlayer && !pHoldingPlayer->IsUsingVMGrab() )
|
||||
{
|
||||
CGrabController *pGrabController = GetGrabControllerForPlayer( pHoldingPlayer );
|
||||
|
||||
if ( !pGrabController )
|
||||
pGrabController = GetGrabControllerForPhysCannon( pHoldingPlayer->GetActiveWeapon() );
|
||||
|
||||
Assert( pGrabController );
|
||||
if( pGrabController )
|
||||
{
|
||||
GrabController_SetPortalPenetratingEntity( pGrabController, pOther );
|
||||
}
|
||||
|
||||
//NDebugOverlay::EntityBounds( pHeld, 0, 0, 255, 16, 1.0f );
|
||||
//NDebugOverlay::EntityBounds( pOther, 255, 0, 0, 16, 1.0f );
|
||||
//pPhysOther->Wake();
|
||||
//FindClosestPassableSpace( pOther, Vector( 0.0f, 0.0f, 1.0f ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( (pObj0->GetGameFlags() | pObj1->GetGameFlags()) & FVPHYSICS_IS_SHADOWCLONE )
|
||||
{
|
||||
//at least one shadowclone is involved
|
||||
|
||||
if( (pObj0->GetGameFlags() & pObj1->GetGameFlags()) & FVPHYSICS_IS_SHADOWCLONE ) //don't solve between two shadowclones, they're just going to resync in a frame anyways
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
IPhysicsObject * const pObjects[2] = { pObj0, pObj1 };
|
||||
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( pObjects[i]->GetGameFlags() & FVPHYSICS_IS_SHADOWCLONE )
|
||||
{
|
||||
int j = 1 - i;
|
||||
if( !pObjects[j]->IsMoveable() )
|
||||
return 0; //don't solve between shadow clones and statics
|
||||
|
||||
if( ((CPhysicsShadowClone *)(pObjects[i]->GetGameData()))->GetClonedEntity() == (pObjects[j]->GetGameData()) )
|
||||
return 0; //don't solve between a shadow clone and its source entity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::ShouldSolvePenetration( pObj0, pObj1, pGameData0, pGameData1, dt );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Data for energy ball vs held item mass swapping hack
|
||||
static float s_fSavedMass[2];
|
||||
static bool s_bChangedMass[2] = { false, false };
|
||||
static bool s_bUseUnshadowed[2] = { false, false };
|
||||
static IPhysicsObject *s_pUnshadowed[2] = { NULL, NULL };
|
||||
|
||||
|
||||
static void ModifyWeight_PreCollision( vcollisionevent_t *pEvent )
|
||||
{
|
||||
Assert( (pEvent->pObjects[0] != NULL) && (pEvent->pObjects[1] != NULL) );
|
||||
|
||||
CBaseEntity *pUnshadowedEntities[2];
|
||||
IPhysicsObject *pUnshadowedObjects[2];
|
||||
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( pEvent->pObjects[i]->GetGameFlags() & FVPHYSICS_IS_SHADOWCLONE )
|
||||
{
|
||||
CPhysicsShadowClone *pClone = ((CPhysicsShadowClone *)pEvent->pObjects[i]->GetGameData());
|
||||
pUnshadowedEntities[i] = pClone->GetClonedEntity();
|
||||
|
||||
if( pUnshadowedEntities[i] == NULL )
|
||||
return;
|
||||
|
||||
pUnshadowedObjects[i] = pClone->TranslatePhysicsToClonedEnt( pEvent->pObjects[i] );
|
||||
|
||||
if( pUnshadowedObjects[i] == NULL )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
pUnshadowedEntities[i] = (CBaseEntity *)pEvent->pObjects[i]->GetGameData();
|
||||
pUnshadowedObjects[i] = pEvent->pObjects[i];
|
||||
}
|
||||
}
|
||||
|
||||
// HACKHACK: Reduce mass for combine ball vs movable brushes so the collision
|
||||
// appears fully elastic regardless of mass ratios
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
int j = 1-i;
|
||||
|
||||
// One is a combine ball, if the other is a movable brush, reduce the combine ball mass
|
||||
if ( pUnshadowedEntities[i] != NULL && FClassnameIs( pUnshadowedEntities[j], "prop_combine_ball") )
|
||||
{
|
||||
if ( pUnshadowedEntities[i]->GetMoveType() == MOVETYPE_PUSH )
|
||||
{
|
||||
s_bChangedMass[j] = true;
|
||||
s_fSavedMass[j] = pUnshadowedObjects[j]->GetMass();
|
||||
pEvent->pObjects[j]->SetMass( VPHYSICS_MIN_MASS );
|
||||
if( pUnshadowedObjects[j] != pEvent->pObjects[j] )
|
||||
{
|
||||
s_bUseUnshadowed[j] = true;
|
||||
s_pUnshadowed[j] = pUnshadowedObjects[j];
|
||||
|
||||
pUnshadowedObjects[j]->SetMass( VPHYSICS_MIN_MASS );
|
||||
}
|
||||
}
|
||||
|
||||
//HACKHACK: last minute problem knocking over turrets with energy balls, up the mass of the ball by a lot
|
||||
if( FClassnameIs( pUnshadowedEntities[i], "npc_portal_turret_floor" ) )
|
||||
{
|
||||
pUnshadowedObjects[j]->SetMass( pUnshadowedEntities[i]->VPhysicsGetObject()->GetMass() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( ( pUnshadowedObjects[i] && pUnshadowedObjects[i]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
|
||||
{
|
||||
int j = 1-i;
|
||||
if( FClassnameIs( pUnshadowedEntities[j], "prop_combine_ball") )
|
||||
{
|
||||
// [j] is the combine ball, set mass low
|
||||
// if the above ball vs brush entity check didn't already change the mass, change the mass
|
||||
if ( !s_bChangedMass[j] )
|
||||
{
|
||||
s_bChangedMass[j] = true;
|
||||
s_fSavedMass[j] = pUnshadowedObjects[j]->GetMass();
|
||||
pEvent->pObjects[j]->SetMass( VPHYSICS_MIN_MASS );
|
||||
if( pUnshadowedObjects[j] != pEvent->pObjects[j] )
|
||||
{
|
||||
s_bUseUnshadowed[j] = true;
|
||||
s_pUnshadowed[j] = pUnshadowedObjects[j];
|
||||
|
||||
pUnshadowedObjects[j]->SetMass( VPHYSICS_MIN_MASS );
|
||||
}
|
||||
}
|
||||
|
||||
// [i] is the held object, set mass high
|
||||
s_bChangedMass[i] = true;
|
||||
s_fSavedMass[i] = pUnshadowedObjects[i]->GetMass();
|
||||
pEvent->pObjects[i]->SetMass( VPHYSICS_MAX_MASS );
|
||||
if( pUnshadowedObjects[i] != pEvent->pObjects[i] )
|
||||
{
|
||||
s_bUseUnshadowed[i] = true;
|
||||
s_pUnshadowed[i] = pUnshadowedObjects[i];
|
||||
|
||||
pUnshadowedObjects[i]->SetMass( VPHYSICS_MAX_MASS );
|
||||
}
|
||||
}
|
||||
else if( pEvent->pObjects[j]->GetGameFlags() & FVPHYSICS_IS_SHADOWCLONE )
|
||||
{
|
||||
//held object vs shadow clone, set held object mass back to grab controller saved mass
|
||||
|
||||
// [i] is the held object
|
||||
s_bChangedMass[i] = true;
|
||||
s_fSavedMass[i] = pUnshadowedObjects[i]->GetMass();
|
||||
|
||||
CGrabController *pGrabController = NULL;
|
||||
CBaseEntity *pLookingForEntity = (CBaseEntity*)pEvent->pObjects[i]->GetGameData();
|
||||
CBasePlayer *pHoldingPlayer = GetPlayerHoldingEntity( pLookingForEntity );
|
||||
if( pHoldingPlayer )
|
||||
pGrabController = GetGrabControllerForPlayer( pHoldingPlayer );
|
||||
|
||||
float fSavedMass, fSavedRotationalDamping;
|
||||
|
||||
AssertMsg( pGrabController, "Physics object is held, but we can't find the holding controller." );
|
||||
GetSavedParamsForCarriedPhysObject( pGrabController, pUnshadowedObjects[i], &fSavedMass, &fSavedRotationalDamping );
|
||||
|
||||
pEvent->pObjects[i]->SetMass( fSavedMass );
|
||||
if( pUnshadowedObjects[i] != pEvent->pObjects[i] )
|
||||
{
|
||||
s_bUseUnshadowed[i] = true;
|
||||
s_pUnshadowed[i] = pUnshadowedObjects[i];
|
||||
|
||||
pUnshadowedObjects[i]->SetMass( fSavedMass );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CPortal_CollisionEvent::PreCollision( vcollisionevent_t *pEvent )
|
||||
{
|
||||
ModifyWeight_PreCollision( pEvent );
|
||||
return BaseClass::PreCollision( pEvent );
|
||||
}
|
||||
|
||||
|
||||
static void ModifyWeight_PostCollision( vcollisionevent_t *pEvent )
|
||||
{
|
||||
for( int i = 0; i != 2; ++i )
|
||||
{
|
||||
if( s_bChangedMass[i] )
|
||||
{
|
||||
pEvent->pObjects[i]->SetMass( s_fSavedMass[i] );
|
||||
if( s_bUseUnshadowed[i] )
|
||||
{
|
||||
s_pUnshadowed[i]->SetMass( s_fSavedMass[i] );
|
||||
s_bUseUnshadowed[i] = false;
|
||||
}
|
||||
s_bChangedMass[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPortal_CollisionEvent::PostCollision( vcollisionevent_t *pEvent )
|
||||
{
|
||||
ModifyWeight_PostCollision( pEvent );
|
||||
|
||||
return BaseClass::PostCollision( pEvent );
|
||||
}
|
||||
|
||||
void CPortal_CollisionEvent::PostSimulationFrame()
|
||||
{
|
||||
//this actually happens once per physics environment simulation, and we don't want that, so do nothing and we'll get a different version manually called
|
||||
}
|
||||
|
||||
void CPortal_CollisionEvent::PortalPostSimulationFrame( void )
|
||||
{
|
||||
BaseClass::PostSimulationFrame();
|
||||
}
|
||||
|
||||
|
||||
void CPortal_CollisionEvent::AddDamageEvent( CBaseEntity *pEntity, const CTakeDamageInfo &info, IPhysicsObject *pInflictorPhysics, bool bRestoreVelocity, const Vector &savedVel, const AngularImpulse &savedAngVel )
|
||||
{
|
||||
const CTakeDamageInfo *pPassDownInfo = &info;
|
||||
CTakeDamageInfo ReplacementDamageInfo; //only used some of the time
|
||||
|
||||
if( (info.GetDamageType() & DMG_CRUSH) &&
|
||||
(pInflictorPhysics->GetGameFlags() & FVPHYSICS_IS_SHADOWCLONE) &&
|
||||
(!info.BaseDamageIsValid()) &&
|
||||
(info.GetDamageForce().LengthSqr() > (20000.0f * 20000.0f))
|
||||
)
|
||||
{
|
||||
//VERY likely this was caused by the penetration solver. Since a shadow clone is involved we're going to ignore it becuase it causes more problems than it solves in this case
|
||||
ReplacementDamageInfo = info;
|
||||
ReplacementDamageInfo.SetDamage( 0.0f );
|
||||
pPassDownInfo = &ReplacementDamageInfo;
|
||||
}
|
||||
|
||||
BaseClass::AddDamageEvent( pEntity, *pPassDownInfo, pInflictorPhysics, bRestoreVelocity, savedVel, savedAngVel );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
35
game/server/portal/portal_physics_collisionevent.h
Normal file
35
game/server/portal/portal_physics_collisionevent.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTAL_PHYSICS_COLLISIONEVENT_H
|
||||
#define PORTAL_PHYSICS_COLLISIONEVENT_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "physics_collisionevent.h"
|
||||
|
||||
class CPortal_CollisionEvent : public CCollisionEvent
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS_GAMEROOT( CPortal_CollisionEvent, CCollisionEvent );
|
||||
|
||||
virtual int ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
|
||||
virtual void PreCollision( vcollisionevent_t *pEvent );
|
||||
virtual void PostCollision( vcollisionevent_t *pEvent );
|
||||
virtual int ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt );
|
||||
|
||||
virtual void PostSimulationFrame( void );
|
||||
void PortalPostSimulationFrame( void );
|
||||
void AddDamageEvent( CBaseEntity *pEntity, const CTakeDamageInfo &info, IPhysicsObject *pInflictorPhysics, bool bRestoreVelocity, const Vector &savedVel, const AngularImpulse &savedAngVel );
|
||||
|
||||
static void DisablePenetrationSolving_Push( bool bDisable );
|
||||
static void DisablePenetrationSolving_Pop( void );
|
||||
};
|
||||
|
||||
#endif //#ifndef PORTAL_PHYSICS_COLLISIONEVENT_H
|
||||
5737
game/server/portal/portal_player.cpp
Normal file
5737
game/server/portal/portal_player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
729
game/server/portal/portal_player.h
Normal file
729
game/server/portal/portal_player.h
Normal file
@@ -0,0 +1,729 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef PORTAL_PLAYER_H
|
||||
#define PORTAL_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
class CPortal_Player;
|
||||
|
||||
#include "player.h"
|
||||
#include "portal_playeranimstate.h"
|
||||
#include "portal_playerlocaldata.h"
|
||||
#include "simtimer.h"
|
||||
#include "soundenvelope.h"
|
||||
#include "npc_security_camera.h"
|
||||
#include "portal_player_shared.h"
|
||||
#include "weapon_portalbase.h"
|
||||
#include "in_buttons.h"
|
||||
#include "ai_speech.h" // For expresser host
|
||||
#include "basemultiplayerplayer.h"
|
||||
#include "paint_power_user.h"
|
||||
#include "paintable_entity.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "npc_security_camera.h"
|
||||
#include "portal_base2d.h"
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
||||
#include "portal2_item_inventory.h"
|
||||
#endif
|
||||
|
||||
extern bool UTIL_TimeScaleIsNonStandard( void );
|
||||
|
||||
|
||||
#define PORTAL_COLOR_FLAG_BLUE ( 1 << 0 )
|
||||
#define PORTAL_COLOR_FLAG_PURPLE ( 1 << 1 )
|
||||
#define PORTAL_COLOR_FLAG_ORANGE ( 1 << 2 )
|
||||
#define PORTAL_COLOR_FLAG_RED ( 1 << 3 )
|
||||
|
||||
enum PlayerGunType
|
||||
{
|
||||
PLAYER_NO_GUN = 0,
|
||||
PLAYER_PAINT_GUN,
|
||||
PLAYER_PORTAL_GUN
|
||||
};
|
||||
|
||||
enum ForcedGrabControllerType
|
||||
{
|
||||
FORCE_GRAB_CONTROLLER_DEFAULT = 0,
|
||||
FORCE_GRAB_CONTROLLER_VM,
|
||||
FORCE_GRAB_CONTROLLER_PHYSICS
|
||||
};
|
||||
|
||||
class CEntityPortalledNetworkMessage
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS_NOBASE( CEntityPortalledNetworkMessage );
|
||||
|
||||
CEntityPortalledNetworkMessage( void );
|
||||
|
||||
CHandle<CBaseEntity> m_hEntity;
|
||||
CHandle<CPortal_Base2D> m_hPortal;
|
||||
float m_fTime;
|
||||
bool m_bForcedDuck;
|
||||
uint32 m_iMessageCount;
|
||||
};
|
||||
|
||||
class CMoveData;
|
||||
|
||||
//=============================================================================
|
||||
// >> Portal_Player
|
||||
//=============================================================================
|
||||
class CPortal_Player : public PaintPowerUser< CPaintableEntity< CBaseMultiplayerPlayer > >
|
||||
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
||||
, public IInventoryUpdateListener
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPortal_Player, PaintPowerUser< CPaintableEntity< CBaseMultiplayerPlayer > > );
|
||||
|
||||
CPortal_Player();
|
||||
virtual ~CPortal_Player( void );
|
||||
|
||||
static CPortal_Player *CreatePlayer( const char *className, edict_t *ed );
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_ENT_SCRIPTDESC();
|
||||
|
||||
virtual void Precache( void );
|
||||
virtual void CreateSounds( void );
|
||||
virtual void StopLoopingSounds( void );
|
||||
virtual void Spawn( void );
|
||||
virtual void SharedSpawn();
|
||||
virtual void OnRestore( void );
|
||||
virtual int Restore( IRestore &restore );
|
||||
virtual void Activate( void );
|
||||
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
|
||||
#ifdef PORTAL2
|
||||
virtual void InitialSpawn( void );
|
||||
|
||||
void SetPlacingPhoto( bool bPlacing );
|
||||
void OnPhotoAdded( int nIndex );
|
||||
void OnPhotoRemoved( int nIndex );
|
||||
void SetSelectedPhoto( int nIndex );
|
||||
int GetSelectedPhoto( void );
|
||||
void ClearPhotos( void );
|
||||
void StripPhotos( bool bNotifyPlayer = true );
|
||||
void FlashDenyIndicator( float flDuration, unsigned char nType );
|
||||
void FlashInventory( float flDuration, unsigned char nType );
|
||||
void Flash( float flDuration, const Vector &vecPosition );
|
||||
void ControlHelperAnimate( unsigned char nActiveIcon, bool bClear = false );
|
||||
void UpdateLocatorEntityIndices( int *pIndices, int nNumIndices );
|
||||
void SetMotionBlurAmount( float flAmt ) { m_flMotionBlurAmount = flAmt; }
|
||||
#endif // PORTAL2
|
||||
|
||||
virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const;
|
||||
virtual float PlayScene( const char *pszScene, float flDelay, AI_Response *response, IRecipientFilter *filter );
|
||||
virtual void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t ¶ms );
|
||||
|
||||
virtual void PostThink( void );
|
||||
virtual void PreThink( void );
|
||||
void SwapThink();
|
||||
virtual void PlayerDeathThink();
|
||||
virtual void PlayerTransitionCompleteThink();
|
||||
virtual void PlayerCatchPatnerNotConnectingThink();
|
||||
|
||||
void UpdatePortalPlaneSounds( void );
|
||||
void UpdateWooshSounds( void );
|
||||
|
||||
Activity TranslateActivity( Activity ActToTranslate, bool *pRequired = NULL );
|
||||
virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity, bool bUseSlowHighAccuracyContacts = true );
|
||||
|
||||
Activity TranslateTeamActivity( Activity ActToTranslate );
|
||||
|
||||
virtual void SetAnimation( PLAYER_ANIM playerAnim );
|
||||
|
||||
virtual void PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper);
|
||||
|
||||
virtual bool ClientCommand( const CCommand &args );
|
||||
virtual void CreateViewModel( int viewmodelindex = 0 );
|
||||
virtual bool BecomeRagdollOnClient( const Vector &force );
|
||||
virtual int OnTakeDamage( const CTakeDamageInfo &inputInfo );
|
||||
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
virtual void Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info );
|
||||
virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const;
|
||||
virtual void FireBullets ( const FireBulletsInfo_t &info );
|
||||
|
||||
virtual bool Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0);
|
||||
virtual Vector Weapon_ShootPosition();
|
||||
virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon );
|
||||
virtual CBaseCombatWeapon* Weapon_OwnsThisType( const char *pszWeapon, int iSubType = 0 ) const; // True if already owns a weapon of this class
|
||||
virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon );
|
||||
virtual void SelectItem( const char *pstr, int iSubType );
|
||||
|
||||
|
||||
virtual void ShutdownUseEntity( void );
|
||||
virtual bool ShouldDropActiveWeaponWhenKilled( void ) { return false; }
|
||||
|
||||
virtual Vector EyeDirection3D( void );
|
||||
virtual Vector EyeDirection2D( void );
|
||||
virtual const Vector &WorldSpaceCenter() const;
|
||||
|
||||
//virtual bool StartReplayMode( float fDelay, float fDuration, int iEntity );
|
||||
//virtual void StopReplayMode();
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
virtual void Jump( void );
|
||||
|
||||
void UnDuck( void );
|
||||
bool UseFoundEntity( CBaseEntity *pUseEntity, bool bAutoGrab );
|
||||
bool IsInvalidHandoff( CBaseEntity *pObject );
|
||||
void PollForUseEntity( bool bBasicUse, CBaseEntity **ppUseEnt, CPortal_Base2D **ppUseThroughPortal );
|
||||
CBaseEntity* FindUseEntity( CPortal_Base2D **pThroughPortal );
|
||||
CBaseEntity* FindUseEntityThroughPortal( void );
|
||||
|
||||
void ZoomIn( void );
|
||||
void ZoomOut( void );
|
||||
virtual bool IsZoomed( void );
|
||||
|
||||
virtual void PlayerUse( void );
|
||||
//virtual bool StartObserverMode( int mode );
|
||||
virtual void GetStepSoundVelocities( float *velwalk, float *velrun );
|
||||
virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force );
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
||||
virtual void OnSave( IEntitySaveUtils *pUtils );
|
||||
|
||||
virtual void SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
|
||||
virtual void UpdatePortalViewAreaBits( unsigned char *pvs, int pvssize );
|
||||
virtual void ItemPostFrame( void );
|
||||
|
||||
bool ValidatePlayerModel( const char *pModel );
|
||||
|
||||
void ClearScriptedInteractions( void );
|
||||
void ParseScriptedInteractions( void );
|
||||
void AddScriptedInteraction( ScriptedNPCInteraction_t *pInteraction );
|
||||
CUtlVector<ScriptedNPCInteraction_t> *GetScriptedInteractions( void ) { return &m_ScriptedInteractions; }
|
||||
|
||||
void FireConcept( const char *pConcept );
|
||||
bool IsTaunting( void );
|
||||
int GetTeamTauntState( void ) const { return m_nTeamTauntState; }
|
||||
void SetTeamTauntState( int nTeamTauntState );
|
||||
Vector GetTeamTauntPosition( void ) { return m_vTauntPosition; }
|
||||
QAngle GetTeamTauntAngles( void ) { return m_vTauntAngles; }
|
||||
QAngle GetTrickFireAngles( void ) { return m_vPreTauntAngles; }
|
||||
bool IsTrickFiring( void ) const { return m_bTrickFire; }
|
||||
void ClearTrickFiring( void ) { m_bTrickFire = false; }
|
||||
|
||||
QAngle GetAnimEyeAngles( void ) { return m_angEyeAngles.Get(); }
|
||||
|
||||
Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL );
|
||||
|
||||
virtual void CheatImpulseCommands( int iImpulse );
|
||||
void CreateRagdollEntity( const CTakeDamageInfo &info );
|
||||
void GiveAllItems( void );
|
||||
|
||||
void GiveDefaultItems( void );
|
||||
|
||||
void NoteWeaponFired( void );
|
||||
|
||||
void ResetAnimation( void );
|
||||
|
||||
void SetPlayerModel( void );
|
||||
|
||||
int GetPlayerModelType( void ) { return m_iPlayerSoundType; }
|
||||
|
||||
virtual void ForceDuckThisFrame( void );
|
||||
//void UnDuck ( void );
|
||||
//inline void ForceJumpThisFrame( void ) { ForceButtons( IN_JUMP ); }
|
||||
|
||||
void DoAnimationEvent( PlayerAnimEvent_t event, int nData );
|
||||
void SetupBones( matrix3x4a_t *pBoneToWorld, int boneMask );
|
||||
|
||||
// physics interactions
|
||||
virtual void PickupObject(CBaseEntity *pObject, bool bLimitMassAndSize );
|
||||
virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis );
|
||||
|
||||
void ToggleHeldObjectOnOppositeSideOfPortal( void ) { m_bHeldObjectOnOppositeSideOfPortal = !m_bHeldObjectOnOppositeSideOfPortal; }
|
||||
void SetHeldObjectOnOppositeSideOfPortal( bool p_bHeldObjectOnOppositeSideOfPortal ) { m_bHeldObjectOnOppositeSideOfPortal = p_bHeldObjectOnOppositeSideOfPortal; }
|
||||
bool IsHeldObjectOnOppositeSideOfPortal( void ) { return m_bHeldObjectOnOppositeSideOfPortal; }
|
||||
CPortal_Base2D *GetHeldObjectPortal( void ) const { return m_hHeldObjectPortal.Get(); }
|
||||
void SetHeldObjectPortal( CPortal_Base2D *pPortal ) { m_hHeldObjectPortal = pPortal; }
|
||||
void SetUsingVMGrabState( bool bState ) { m_bUsingVMGrabState = bState; }
|
||||
bool IsUsingVMGrab( void );
|
||||
bool WantsVMGrab( void );
|
||||
void UpdateVMGrab( CBaseEntity *pEntity );
|
||||
bool IsForcingDrop( void ) { return m_bForcingDrop; }
|
||||
|
||||
// This is set by the client when it picks something up
|
||||
// and used to initiate server grab logic. If we actually pick something up
|
||||
// it's held in 'm_hAttachedObject'.
|
||||
EHANDLE m_hGrabbedEntity;
|
||||
EHANDLE m_hPortalThroughWhichGrabOccured;
|
||||
bool m_bForcingDrop;
|
||||
CNetworkVar( bool, m_bUseVMGrab );
|
||||
CNetworkVar( bool, m_bUsingVMGrabState );
|
||||
float m_flUseKeyStartTime; // for long duration uses, record the initial keypress start time
|
||||
float m_flAutoGrabLockOutTime;
|
||||
|
||||
void SetForcedGrabControllerType( ForcedGrabControllerType type );
|
||||
ForcedGrabControllerType m_ForcedGrabController;
|
||||
|
||||
// Object we're successfully holding we network down to the client
|
||||
// for clientside simulation under multiplayer
|
||||
CNetworkHandle( CBaseEntity, m_hAttachedObject );
|
||||
CNetworkQAngle( m_vecCarriedObjectAngles );
|
||||
//not simulating physics on the client, network down any inability the held object has in reaching it's target position/orientation
|
||||
CNetworkVector( m_vecCarriedObject_CurPosToTargetPos );
|
||||
CNetworkQAngle( m_vecCarriedObject_CurAngToTargetAng );
|
||||
|
||||
void SetUseKeyCooldownTime( float flCooldownDuration );
|
||||
|
||||
void SetStuckOnPortalCollisionObject( void ) { m_bStuckOnPortalCollisionObject = true; }
|
||||
|
||||
CWeaponPortalBase* GetActivePortalWeapon() const;
|
||||
|
||||
void IncrementPortalsPlaced( bool bSecondaryPortal );
|
||||
void IncrementStepsTaken( void );
|
||||
void IncrementDistanceTaken( void );
|
||||
void UpdateSecondsTaken( void );
|
||||
void ResetThisLevelStats( void );
|
||||
int NumPortalsPlaced( void ) const { return m_StatsThisLevel.iNumPortalsPlaced; }
|
||||
int NumStepsTaken( void ) const { return m_StatsThisLevel.iNumStepsTaken; }
|
||||
float NumSecondsTaken( void ) const { return m_StatsThisLevel.fNumSecondsTaken; }
|
||||
float NumDistanceTaken( void ) const { return m_StatsThisLevel.fDistanceTaken; }
|
||||
|
||||
bool IsHoldingEntity( CBaseEntity *pEnt );
|
||||
float GetHeldObjectMass( IPhysicsObject *pHeldObject );
|
||||
|
||||
void SetNeuroToxinDamageTime( float fCountdownSeconds ) { m_fNeuroToxinDamageTime = gpGlobals->curtime + fCountdownSeconds; }
|
||||
|
||||
void IncNumCamerasDetatched( void ) { ++m_iNumCamerasDetatched; }
|
||||
int GetNumCamerasDetatched( void ) const { return m_iNumCamerasDetatched; }
|
||||
|
||||
void MarkClientCheckPVSDirty( void ) { m_bClientCheckPVSDirty = true; }
|
||||
|
||||
void SetIsHoldingObject( bool bSet ) { m_bIsHoldingSomething = bSet; }
|
||||
|
||||
virtual void ApplyPortalTeleportation( const CPortal_Base2D *pEnteredPortal, CMoveData *pMove );
|
||||
|
||||
Vector m_vecTotalBulletForce; //Accumulator for bullet force in a single frame
|
||||
|
||||
bool m_bSilentDropAndPickup;
|
||||
|
||||
// Tracks our ragdoll entity.
|
||||
CNetworkHandle( CBaseEntity, m_hRagdoll ); // networked entity handle
|
||||
|
||||
#if USE_SLOWTIME
|
||||
bool IsSlowingTime( void ) { return ( m_PortalLocal.m_bSlowingTime || UTIL_TimeScaleIsNonStandard() ); }
|
||||
#endif // USE_SLOWTIME
|
||||
|
||||
void SetAirControlSupressionTime( float flDuration ) { m_PortalLocal.m_flAirControlSupressionTime = flDuration; }
|
||||
bool IsSuppressingAirControl( void ) { return m_PortalLocal.m_flAirControlSupressionTime != 0.0f; }
|
||||
|
||||
virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
|
||||
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet );
|
||||
|
||||
float GetImplicitVerticalStepSpeed() const;
|
||||
void SetImplicitVerticalStepSpeed( float speed );
|
||||
|
||||
const CPortalPlayerLocalData& GetPortalPlayerLocalData() const;
|
||||
|
||||
virtual Vector EyePosition();
|
||||
|
||||
#if USE_SLOWTIME
|
||||
// Time modulation
|
||||
void StartSlowingTime( float flDuration );
|
||||
void StopSlowingTime( void );
|
||||
#endif // USE_SLOWTIME
|
||||
|
||||
void ShowViewFinder( void );
|
||||
void HideViewFinder( void );
|
||||
|
||||
// Coop ping effect
|
||||
void PlayCoopPingEffect( void );
|
||||
|
||||
CNetworkVar( bool, m_bPitchReorientation );
|
||||
CNetworkHandle( CPortal_Base2D, m_hPortalEnvironment ); //if the player is in a portal environment, this is the associated portal
|
||||
|
||||
friend class CPortal_Base2D;
|
||||
|
||||
virtual CBaseEntity* EntSelectSpawnPoint( void );
|
||||
void PickTeam( void );
|
||||
static void ClientDisconnected( edict_t *pPlayer );
|
||||
virtual void ChangeTeam( int iTeamNum );
|
||||
|
||||
int FlashlightIsOn( void );
|
||||
bool FlashlightTurnOn( bool playSound /*= false*/ );
|
||||
void FlashlightTurnOff( bool playSound /*= false*/ );
|
||||
|
||||
void SetInTractorBeam( CTrigger_TractorBeam *pTractorBeam );
|
||||
void SetLeaveTractorBeam( CTrigger_TractorBeam *pTractorBeam, bool bKeepFloating );
|
||||
CTrigger_TractorBeam* GetTractorBeam( void ) const { return m_PortalLocal.m_hTractorBeam.Get(); }
|
||||
|
||||
friend class CPortalGameMovement;
|
||||
|
||||
void PreventCrouchJump( CUserCmd* ucmd );
|
||||
|
||||
void BridgeRemovedFromUnder( void );
|
||||
void WasDroppedByOtherPlayerWhileTaunting() { m_bWasDroppedByOtherPlayerWhileTaunting = true; }
|
||||
void OnPlayerLanded();
|
||||
|
||||
void IncWheatleyMonitorDestructionCount( void ) { m_nWheatleyMonitorDestructionCount++; }
|
||||
int GetWheatleyMonitorDestructionCount( void ) { return m_nWheatleyMonitorDestructionCount; }
|
||||
|
||||
void TurnOffPotatos( void ) { m_bPotatos = false; }
|
||||
void TurnOnPotatos( void ) { m_bPotatos = true; }
|
||||
|
||||
void PlayCoopStepSound( const Vector& origin, int side, float volume );
|
||||
|
||||
protected:
|
||||
mutable Vector m_vWorldSpaceCenterHolder; //WorldSpaceCenter() returns a reference, need an actual value somewhere
|
||||
CNetworkVarEmbedded( CPortalPlayerLocalData, m_PortalLocal );
|
||||
|
||||
CHandle< CNPC_SecurityCamera > m_hRemoteTauntCamera;
|
||||
|
||||
void RespawnPlayer( void );
|
||||
|
||||
private:
|
||||
virtual const char *GetPlayerModelName( void );
|
||||
void PlayUseDenySound( void );
|
||||
|
||||
public:
|
||||
void Taunt( const char *pchTauntForce = NULL, bool bAuto = false );
|
||||
|
||||
private:
|
||||
bool SolveTeamTauntPositionAndAngles( CPortal_Player *pInitiator );
|
||||
bool ValidateTeamTaunt( CPortal_Player *pInitiator, Vector &vInitiatorPos, QAngle &angInitiatorAng, Vector &vAcceptorPos, QAngle &angAcceptorAng, bool bRecursed = false );
|
||||
void StartTaunt( void );
|
||||
bool FindRemoteTauntViewpoint( Vector *pOriginOut, QAngle *pAnglesOut );
|
||||
|
||||
#if USE_SLOWTIME
|
||||
CBaseEntity *m_pSlowTimeColorFX;
|
||||
#endif // USE_SLOWTIME
|
||||
|
||||
CSoundPatch *m_pWooshSound;
|
||||
CSoundPatch *m_pGrabSound;
|
||||
|
||||
int m_nWheatleyMonitorDestructionCount;
|
||||
|
||||
CNetworkQAngleXYZ( m_angEyeAngles );
|
||||
|
||||
CPortalPlayerAnimState* m_PlayerAnimState;
|
||||
|
||||
int m_iLastWeaponFireUsercmd;
|
||||
CNetworkVar( int, m_iSpawnInterpCounter );
|
||||
CNetworkVar( int, m_iPlayerSoundType );
|
||||
|
||||
CUtlVector<ScriptedNPCInteraction_t> m_ScriptedInteractions;
|
||||
|
||||
CNetworkVar( bool, m_bPingDisabled );
|
||||
CNetworkVar( bool, m_bTauntDisabled );
|
||||
CNetworkVar( bool, m_bTauntRemoteView );
|
||||
bool m_bTauntRemoteViewFOVFixup;
|
||||
CNetworkVector( m_vecRemoteViewOrigin );
|
||||
CNetworkQAngle( m_vecRemoteViewAngles );
|
||||
CNetworkVar( float, m_fTauntCameraDistance );
|
||||
CNetworkVar( int, m_nTeamTauntState );
|
||||
CNetworkVector( m_vTauntPosition );
|
||||
CNetworkQAngle( m_vTauntAngles );
|
||||
CNetworkQAngle( m_vPreTauntAngles );
|
||||
CNetworkVar( bool, m_bTrickFire );
|
||||
CNetworkHandle( CPortal_Player, m_hTauntPartnerInRange );
|
||||
CNetworkString( m_szTauntForce, PORTAL2_MP_TEAM_TAUNT_FORCE_LENGTH );
|
||||
|
||||
CNetworkVar( bool, m_bHeldObjectOnOppositeSideOfPortal );
|
||||
CNetworkHandle( CPortal_Base2D, m_hHeldObjectPortal ); // networked entity handle
|
||||
|
||||
#if USE_SLOWTIME
|
||||
bool m_bHasPlayedSlowTimeStopSound;
|
||||
#endif // USE_SLOWTIME
|
||||
|
||||
bool m_bIntersectingPortalPlane;
|
||||
bool m_bStuckOnPortalCollisionObject;
|
||||
bool m_bPlayUseDenySound; // Signaled by PlayerUse, but can be unset by HL2 ladder code...
|
||||
|
||||
float m_fNeuroToxinDamageTime;
|
||||
|
||||
CNetworkVarEmbedded( PortalPlayerStatistics_t, m_StatsThisLevel );
|
||||
float m_fTimeLastNumSecondsUpdate;
|
||||
Vector m_vPrevPosition;
|
||||
|
||||
int m_iNumCamerasDetatched;
|
||||
|
||||
// In multiplayer, last time we used a coop ping to draw our partner's attention
|
||||
float m_flLastPingTime;
|
||||
|
||||
// When a portal is placed in the same cluster as us, we flip this on
|
||||
// to signal the updating of the g_ClientCheck's cached PVS bits.
|
||||
bool m_bClientCheckPVSDirty;
|
||||
|
||||
float m_flUseKeyCooldownTime; // Disable use key until curtime >= this number
|
||||
|
||||
CNetworkVar( bool, m_bIsHoldingSomething );
|
||||
|
||||
float m_flImplicitVerticalStepSpeed; // When moving with step code, the player has an implicit vertical
|
||||
// velocity that keeps her on ramps, steps, etc. We need this to
|
||||
// correctly transform her velocity when she teleports.
|
||||
|
||||
// to check if the player just respawned on client
|
||||
CNetworkVar( bool, m_iSpawnCounter );
|
||||
|
||||
// to check if the player was dropped from the bridge by the other player
|
||||
bool m_bWasDroppedByOtherPlayerWhileTaunting;
|
||||
|
||||
public: // PAINT SPECIFIC
|
||||
bool IsReorienting() const;
|
||||
Vector GetPaintGunShootPosition();
|
||||
|
||||
bool IsPressingJumpKey() const;
|
||||
bool IsHoldingJumpKey() const;
|
||||
bool IsTryingToSuperJump( const PaintPowerInfo_t* pInfo = NULL ) const;
|
||||
void SetJumpedThisFrame( bool jumped );
|
||||
bool JumpedThisFrame() const;
|
||||
void SetBouncedThisFrame( bool bounced );
|
||||
bool BouncedThisFrame() const;
|
||||
InAirState GetInAirState() const;
|
||||
|
||||
bool WantsToSwapGuns( void );
|
||||
void SetWantsToSwapGuns( bool bWantsToSwap );
|
||||
|
||||
bool IsUsingPostTeleportationBox() const;
|
||||
|
||||
const Vector& GetInputVector() const;
|
||||
void SetInputVector( const Vector& vInput );
|
||||
|
||||
virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy);
|
||||
|
||||
const Vector& GetPrevGroundNormal() const;
|
||||
void SetPrevGroundNormal( const Vector& vPrevNormal );
|
||||
float PredictedAirTimeEnd( void ); // Uses the current velocity
|
||||
float PredictedBounce( void ); // Uses the current velocity
|
||||
void OnBounced( float fTimeOffset = 0.0f );
|
||||
|
||||
virtual void SetFogController( CFogController *pFogController );
|
||||
|
||||
virtual PaintPowerType GetPaintPowerAtPoint( const Vector& worldContactPt ) const;
|
||||
virtual void Paint( PaintPowerType type, const Vector& worldContactPt );
|
||||
virtual void CleansePaint();
|
||||
|
||||
void Reorient( QAngle& viewAngles );
|
||||
float GetReorientationProgress() const;
|
||||
bool IsDoneReorienting() const;
|
||||
virtual const Vector GetPlayerMins() const;
|
||||
virtual const Vector GetPlayerMaxs() const;
|
||||
const Vector& GetHullMins() const;
|
||||
const Vector& GetHullMaxs() const;
|
||||
const Vector& GetStandHullMins() const;
|
||||
const Vector& GetStandHullMaxs() const;
|
||||
const Vector& GetDuckHullMins() const;
|
||||
const Vector& GetDuckHullMaxs() const;
|
||||
|
||||
float GetHullHeight() const;
|
||||
float GetHullWidth() const;
|
||||
float GetStandHullHeight() const;
|
||||
float GetStandHullWidth() const;
|
||||
float GetDuckHullHeight() const;
|
||||
float GetDuckHullWidth() const;
|
||||
|
||||
virtual void UpdateCollisionBounds();
|
||||
virtual void InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity );
|
||||
|
||||
bool PlayGesture( const char *pGestureName );
|
||||
|
||||
void SetAirDuck( bool bDuckedInAir );
|
||||
|
||||
StickCameraState GetStickCameraState() const;
|
||||
void SetQuaternionPunch( const Quaternion& qPunch );
|
||||
void DecayQuaternionPunch();
|
||||
|
||||
using BaseClass::AddSurfacePaintPowerInfo;
|
||||
void AddSurfacePaintPowerInfo( const BrushContact& contact, char const* context = 0 );
|
||||
void AddSurfacePaintPowerInfo( const trace_t& trace, char const* context = 0 );
|
||||
|
||||
void SetEyeUpOffset( const Vector& vOldUp, const Vector& vNewUp );
|
||||
void SetEyeOffset( const Vector& vOldOrigin, const Vector& vNewOrigin );
|
||||
|
||||
void GivePlayerPaintGun( bool bActivatePaintPowers, bool bSwitchTo );
|
||||
void GivePlayerPortalGun( bool bUpgraded, bool bSwitchTo );
|
||||
void GivePlayerWearable( const char *pItemName );
|
||||
void RemovePlayerWearable( const char *pItemName );
|
||||
|
||||
void ResetBounceCount() { m_nBounceCount = 0; }
|
||||
void ResetAirTauntCount() { m_nAirTauntCount = 0; }
|
||||
|
||||
// Anim state code
|
||||
CNetworkVarEmbedded( CPortalPlayerShared, m_Shared );
|
||||
|
||||
void SetHullHeight( float flHeight );
|
||||
|
||||
virtual void ChooseActivePaintPowers( PaintPowerInfoVector& activePowers );
|
||||
|
||||
bool IsFullyConnected() { return m_bIsFullyConnected; }
|
||||
void OnFullyConnected();
|
||||
|
||||
void NetworkPortalTeleportation( CBaseEntity *pOther, CPortal_Base2D *pPortal, float fTime, bool bForcedDuck );
|
||||
|
||||
private: // PAINT SPECIFIC
|
||||
void DecayEyeOffset();
|
||||
|
||||
void GivePortalPlayerItems( void );
|
||||
|
||||
// Find all the contacts
|
||||
void DeterminePaintContacts();
|
||||
void PredictPaintContacts( const Vector& contactBoxMin,
|
||||
const Vector& contactBoxMax,
|
||||
const Vector& traceBoxMin,
|
||||
const Vector& traceBoxMax,
|
||||
float lookAheadTime,
|
||||
char const* context );
|
||||
void ChooseBestPaintPowersInRange( PaintPowerChoiceResultArray& bestPowers,
|
||||
PaintPowerConstIter begin,
|
||||
PaintPowerConstIter end,
|
||||
const PaintPowerChoiceCriteria_t& info ) const;
|
||||
|
||||
// Paint Power User Implementation
|
||||
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 );
|
||||
|
||||
void PlayPaintSounds( const PaintPowerChoiceResultArray& touchedPowers );
|
||||
void UpdatePaintedPower();
|
||||
void UpdateAirInputScaleFadeIn();
|
||||
void UpdateInAirState();
|
||||
void CachePaintPowerChoiceResults( const PaintPowerChoiceResultArray& choiceInfo );
|
||||
bool LateSuperJumpIsValid() const;
|
||||
void RecomputeBoundsForOrientation();
|
||||
void TryToChangeCollisionBounds( const Vector& newStandHullMin,
|
||||
const Vector& newStandHullMax,
|
||||
const Vector& newDuckHullMin,
|
||||
const Vector& newDuckHullMax );
|
||||
|
||||
float SpeedPaintAcceleration( float flDefaultMaxSpeed,
|
||||
float flSpeed,
|
||||
float flWishCos,
|
||||
float flWishDirSpeed ) const;
|
||||
|
||||
bool CheckToUseBouncePower( PaintPowerInfo_t& info );
|
||||
|
||||
// stick camera
|
||||
void RotateUpVector( Vector& vForward, Vector& vUp );
|
||||
void SnapCamera( StickCameraState nCameraState, bool bLookingInBadDirection );
|
||||
void PostTeleportationCameraFixup( const CPortal_Base2D *pEnteredPortal );
|
||||
|
||||
// Paint power debug
|
||||
void DrawJumpHelperDebug( PaintPowerConstIter begin, PaintPowerConstIter end, float duration, bool noDepthTest, const PaintPowerInfo_t* pSelected ) const;
|
||||
|
||||
// PAINT POWER STATE
|
||||
PaintPowerInfo_t m_CachedJumpPower;
|
||||
Vector m_vInputVector;
|
||||
float m_flCachedJumpPowerTime;
|
||||
float m_flUsePostTeleportationBoxTime;
|
||||
float m_flSpeedDecelerationTime;
|
||||
float m_flPredictedJumpTime;
|
||||
bool m_bJumpWasPressedWhenForced; // The jump button was actually pressed when ForceDuckThisFrame() was called
|
||||
int m_nBounceCount; // Number of bounces in a row without touching the ground
|
||||
float m_LastGroundBouncePlaneDistance;
|
||||
float m_flLastSuppressedBounceTime;
|
||||
float m_flTimeSinceLastTouchedPower[3];
|
||||
int m_nPortalsEnteredInAirFlags;
|
||||
int m_nAirTauntCount;
|
||||
|
||||
bool m_bIsFullyConnected;
|
||||
CNetworkVar( float, m_flMotionBlurAmount );
|
||||
|
||||
//Swapping guns
|
||||
CNetworkVar( bool, m_bWantsToSwapGuns );
|
||||
bool m_bSendSwapProximityFailEvent;
|
||||
|
||||
CNetworkVar( bool, m_bPotatos );
|
||||
|
||||
PlayerGunType m_PlayerGunType;
|
||||
PlayerGunType m_PlayerGunTypeWhenDead;
|
||||
bool m_bSpawnFromDeath;
|
||||
|
||||
bool m_bIsBendy;
|
||||
|
||||
Vector m_vPrevGroundNormal; // Our ground normal from the previous frame
|
||||
|
||||
Vector m_vGravity;
|
||||
|
||||
CNetworkVar( float, m_flHullHeight );
|
||||
|
||||
//encoding these messages directly in the player to ensure it's received in sync with the corresponding entity post-teleport update
|
||||
//each player has their own copy of the buffer that is only sent to them
|
||||
CUtlVector<CEntityPortalledNetworkMessage> m_EntityPortalledNetworkMessages;
|
||||
enum
|
||||
{
|
||||
MAX_ENTITY_PORTALLED_NETWORK_MESSAGES = 32,
|
||||
};
|
||||
CNetworkVar( uint32, m_iEntityPortalledNetworkMessageCount ); //always ticks up by one per add
|
||||
|
||||
//variables we'd like to persist between instances of grab controllers
|
||||
struct GrabControllerPersistentVars_t
|
||||
{
|
||||
CHandle<CPortal_Base2D> m_hOscillationWatch;
|
||||
CHandle<CPortal_Base2D> m_hLookingThroughPortalLastUpdate;
|
||||
Vector m_vLastTargetPosition;
|
||||
bool m_bLastUpdateWasForcedPull;
|
||||
|
||||
void ResetOscillationWatch( void )
|
||||
{
|
||||
m_hOscillationWatch = NULL;
|
||||
m_hLookingThroughPortalLastUpdate = NULL;
|
||||
m_vLastTargetPosition.Init();
|
||||
m_bLastUpdateWasForcedPull = false;
|
||||
}
|
||||
};
|
||||
GrabControllerPersistentVars_t m_GrabControllerPersistentVars;
|
||||
|
||||
struct RecentPortalTransform_t
|
||||
{
|
||||
int command_number;
|
||||
CHandle<CPortal_Base2D> Portal;
|
||||
matrix3x4_t matTransform;
|
||||
};
|
||||
|
||||
CUtlVector<RecentPortalTransform_t> m_PendingPortalTransforms; //portal transforms we've sent to the client but they have not yet acknowledged, needed for some input fixup
|
||||
|
||||
friend class CPortalPlayerShared;
|
||||
friend class CGrabController;
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
||||
|
||||
//----------------------------
|
||||
// ECONOMY INVENTORY MANAGEMENT
|
||||
public:
|
||||
// IInventoryUpdateListener
|
||||
virtual void InventoryUpdated( CPlayerInventory *pInventory );
|
||||
virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner ) { m_Shared.SetLoadoutUnavailable( true ); }
|
||||
|
||||
void UpdateInventory( bool bInit );
|
||||
void VerifySOCache();
|
||||
|
||||
// Inventory access
|
||||
CPortalPlayerInventory *Inventory( void ) { return &m_Inventory; }
|
||||
CEconItemView *GetItemInLoadoutSlot( int iLoadoutSlot );
|
||||
|
||||
private:
|
||||
CPortalPlayerInventory m_Inventory;
|
||||
|
||||
#endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
||||
|
||||
bool m_bReadyForDLCItemUpdates;
|
||||
};
|
||||
|
||||
inline CPortal_Player *ToPortalPlayer( CBaseEntity *pEntity )
|
||||
{
|
||||
if ( !pEntity || !pEntity->IsPlayer() )
|
||||
return NULL;
|
||||
|
||||
return assert_cast<CPortal_Player*>( pEntity );
|
||||
}
|
||||
|
||||
inline CPortal_Player *GetPortalPlayer( int iPlayerIndex )
|
||||
{
|
||||
return static_cast<CPortal_Player*>( UTIL_PlayerByIndex( iPlayerIndex ) );
|
||||
}
|
||||
|
||||
#endif //PORTAL_PLAYER_H
|
||||
450
game/server/portal/prop_mirror.cpp
Normal file
450
game/server/portal/prop_mirror.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
//NOTE: Mirrors with models require an attachment named "MirrorSurface_Attach" with x facing out of the mirror plane.
|
||||
//They also require that the mirror surface be in a bodygroup by itself named "MirrorSurface" with the first index being the mirror, second being empty.
|
||||
//Lastly, they require that all non-mirror geometry be in bodygroups that have the second entry as empty.
|
||||
//It's a good idea to put a cubemap on the mirror surface material because they're not infinitely recursive
|
||||
|
||||
#include "cbase.h"
|
||||
#include "baseanimating.h"
|
||||
#include "pvs_extender.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//#define TEST_ANIMATION //uncomment to run "testanim" in a loop
|
||||
|
||||
class CProp_Mirror : public CBaseAnimating, CPVS_Extender
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CProp_Mirror, CBaseAnimating );
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CProp_Mirror( void );
|
||||
virtual void Precache( void );
|
||||
virtual void Spawn( void );
|
||||
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | (m_bPhysicsEnabled ? FCAP_IMPULSE_USE : 0); };
|
||||
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void UpdateReflectionPlane( void );
|
||||
void UpdateReflectionPolygon( void );
|
||||
|
||||
virtual CServerNetworkProperty *GetExtenderNetworkProp( void ) { return NetworkProp(); }
|
||||
virtual const edict_t *GetExtenderEdict( void ) const { return edict(); }
|
||||
virtual Vector GetExtensionPVSOrigin( void ) { return GetAbsOrigin(); }
|
||||
|
||||
virtual bool IsExtenderValid( void ) { return true; }
|
||||
|
||||
int ComputeFrustumThroughPolygon( const Vector &vVisOrigin, const VPlane *pInputFrustum, int iInputFrustumPlanes, VPlane *pOutputFrustum, int iOutputFrustumMaxPlanes );
|
||||
|
||||
//This portal is decidedly visible, recursively extend the visibility problem
|
||||
virtual void ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft );
|
||||
|
||||
#if defined( TEST_ANIMATION )
|
||||
virtual void Think( void );
|
||||
#endif
|
||||
|
||||
Vector m_LocalSpaceReflectionPolygonVerts[10]; //best guess at the reflection polygon by intersecting the reflection plane with the local space OBB
|
||||
int m_LocalSpaceReflectionPolygonVertCount;
|
||||
|
||||
struct ReflectPlaneCachedData_t
|
||||
{
|
||||
Vector vAttachmentOrigin;
|
||||
QAngle qAttachmentAngle;
|
||||
|
||||
bool bModel;
|
||||
Vector vLocalSpaceAttachmentOrigin;
|
||||
QAngle qLocalSpaceAttachmentAngles;
|
||||
Vector vLocalOBB_Mins;
|
||||
Vector vLocalOBB_Maxs;
|
||||
};
|
||||
ReflectPlaneCachedData_t m_CachedReflectedData;
|
||||
|
||||
VMatrix m_matReflection;
|
||||
|
||||
CNetworkVar( float, m_fWidth );
|
||||
CNetworkVar( float, m_fHeight );
|
||||
int m_iMirrorFaceAttachment;
|
||||
bool m_bModel;
|
||||
bool m_bPhysicsEnabled;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CProp_Mirror )
|
||||
DEFINE_KEYFIELD( m_fWidth, FIELD_FLOAT, "Width" ),
|
||||
DEFINE_KEYFIELD( m_fHeight, FIELD_FLOAT, "Height" ),
|
||||
DEFINE_FIELD( m_iMirrorFaceAttachment, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_bModel, FIELD_BOOLEAN ),
|
||||
DEFINE_KEYFIELD( m_bPhysicsEnabled, FIELD_BOOLEAN, "PhysicsEnabled" ),
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CProp_Mirror, DT_Prop_Mirror )
|
||||
SendPropFloat( SENDINFO(m_fWidth) ),
|
||||
SendPropFloat( SENDINFO(m_fHeight) ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( prop_mirror, CProp_Mirror );
|
||||
|
||||
|
||||
CProp_Mirror::CProp_Mirror( void )
|
||||
{
|
||||
m_matReflection.m[3][0] = 0.0f;
|
||||
m_matReflection.m[3][1] = 0.0f;
|
||||
m_matReflection.m[3][2] = 0.0f;
|
||||
m_matReflection.m[3][3] = 1.0f;
|
||||
|
||||
m_CachedReflectedData.vAttachmentOrigin.Invalidate();
|
||||
m_CachedReflectedData.qAttachmentAngle.Invalidate();
|
||||
m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
|
||||
m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
|
||||
m_CachedReflectedData.vLocalOBB_Maxs.Invalidate();
|
||||
m_CachedReflectedData.vLocalOBB_Mins.Invalidate();
|
||||
}
|
||||
|
||||
void CProp_Mirror::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
if( (m_ModelName.ToCStr() != NULL) && (m_ModelName.ToCStr()[0] != '\0') )
|
||||
{
|
||||
PrecacheModel( m_ModelName.ToCStr() );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Mirror::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
BaseClass::Spawn();
|
||||
|
||||
if( m_ModelName.ToCStr() != NULL && m_ModelName.ToCStr()[0] != '\0' )
|
||||
{
|
||||
SetModel( m_ModelName.ToCStr() );
|
||||
SetSolid( SOLID_VPHYSICS );
|
||||
SetCollisionGroup( COLLISION_GROUP_INTERACTIVE );
|
||||
|
||||
if( m_bPhysicsEnabled )
|
||||
{
|
||||
SetMoveType( MOVETYPE_VPHYSICS );
|
||||
VPhysicsInitNormal( GetSolid(), GetSolidFlags(), false );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
}
|
||||
|
||||
#if defined( TEST_ANIMATION )
|
||||
ResetSequence( LookupSequence( "testanim" ) );
|
||||
ResetSequenceInfo();
|
||||
SetPlaybackRate( 0.1f );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
#endif
|
||||
|
||||
m_iMirrorFaceAttachment = LookupAttachment( "MirrorSurface_Attach" );
|
||||
m_bModel = ( m_iMirrorFaceAttachment > 0 ); //0 is an invalid attachment index according to LookupAttachment()
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vExtent( 2.0f, m_fWidth/2.0f, m_fHeight/2.0f );
|
||||
SetSize( -vExtent, vExtent );
|
||||
m_bModel = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Mirror::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if( m_bPhysicsEnabled )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( pActivator );
|
||||
if ( pPlayer )
|
||||
{
|
||||
pPlayer->PickupObject( this );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::Use( pActivator, pCaller, useType, value );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Mirror::UpdateReflectionPlane( void )
|
||||
{
|
||||
Vector vMirrorAttachmentOrigin;
|
||||
QAngle qMirrorAttachmentAngles;
|
||||
if( m_bModel )
|
||||
{
|
||||
GetAttachment( m_iMirrorFaceAttachment, vMirrorAttachmentOrigin, qMirrorAttachmentAngles );
|
||||
}
|
||||
else
|
||||
{
|
||||
vMirrorAttachmentOrigin = GetAbsOrigin();
|
||||
qMirrorAttachmentAngles = GetAbsAngles();
|
||||
}
|
||||
|
||||
if( (m_CachedReflectedData.vAttachmentOrigin != vMirrorAttachmentOrigin) || (m_CachedReflectedData.qAttachmentAngle != qMirrorAttachmentAngles) )
|
||||
{
|
||||
m_CachedReflectedData.vAttachmentOrigin = vMirrorAttachmentOrigin;
|
||||
m_CachedReflectedData.qAttachmentAngle = qMirrorAttachmentAngles;
|
||||
|
||||
Vector vOrigin = m_CachedReflectedData.vAttachmentOrigin;
|
||||
Vector vForward, vRight, vUp;
|
||||
AngleVectors( qMirrorAttachmentAngles, &vForward, &vRight, &vUp );
|
||||
Vector vToOrigin( vOrigin.Dot( vForward ), vOrigin.Dot( vRight ), -vOrigin.Dot( vUp ) );
|
||||
|
||||
//generate mirroring matrix. Move mirror to origin using base vectors, flip on forward axis, move back to position and orientation
|
||||
{
|
||||
m_matReflection.m[0][0] = (-vForward.x * vForward.x) + (vRight.x * vRight.x) + (vUp.x * vUp.x);
|
||||
m_matReflection.m[0][1] = (-vForward.x * vForward.y) + (vRight.x * vRight.y) + (vUp.x * vUp.y);
|
||||
m_matReflection.m[0][2] = (-vForward.x * vForward.z) + (vRight.x * vRight.z) + (vUp.x * vUp.z);
|
||||
m_matReflection.m[0][3] = (vToOrigin.x * vForward.x) - (vToOrigin.y * vRight.x) + (vToOrigin.z * vUp.x) + vOrigin.x;
|
||||
m_matReflection.m[1][0] = m_matReflection.m[0][1]; //rotation portion of the matrix is equal to it's own transpose
|
||||
m_matReflection.m[1][1] = (-vForward.y * vForward.y) + (vRight.y * vRight.y) + (vUp.y * vUp.y);
|
||||
m_matReflection.m[1][2] = (-vForward.y * vForward.z) + (vRight.y * vRight.z) + (vUp.y * vUp.z);
|
||||
m_matReflection.m[1][3] = (vToOrigin.x * vForward.y) - (vToOrigin.y * vRight.y) + (vToOrigin.z * vUp.y) + vOrigin.y;
|
||||
m_matReflection.m[2][0] = m_matReflection.m[0][2]; //rotation portion of the matrix is equal to it's own transpose
|
||||
m_matReflection.m[2][1] = m_matReflection.m[1][2]; //rotation portion of the matrix is equal to it's own transpose
|
||||
m_matReflection.m[2][2] = (-vForward.z * vForward.z) + (vRight.z * vRight.z) + (vUp.z * vUp.z);
|
||||
m_matReflection.m[2][3] = (vToOrigin.x * vForward.z) - (vToOrigin.y * vRight.z) + (vToOrigin.z * vUp.z) + vOrigin.z;
|
||||
}
|
||||
|
||||
UpdateReflectionPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Mirror::UpdateReflectionPolygon( void )
|
||||
{
|
||||
if( m_bModel != m_CachedReflectedData.bModel )
|
||||
{
|
||||
m_CachedReflectedData.qAttachmentAngle.Invalidate();
|
||||
m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
|
||||
m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
|
||||
m_CachedReflectedData.vLocalOBB_Maxs.Invalidate();
|
||||
m_CachedReflectedData.bModel = m_bModel;
|
||||
}
|
||||
|
||||
if( m_bModel )
|
||||
{
|
||||
Vector vMins, vMaxs;
|
||||
vMins = WorldAlignMins();
|
||||
vMaxs = WorldAlignMaxs();
|
||||
|
||||
Vector vLocalAttachmentOrigin;
|
||||
QAngle qLocalAttachmentAngles;
|
||||
GetAttachmentLocal( m_iMirrorFaceAttachment, vLocalAttachmentOrigin, qLocalAttachmentAngles );
|
||||
|
||||
if( (vMins == m_CachedReflectedData.vLocalOBB_Mins) && (vMaxs == m_CachedReflectedData.vLocalOBB_Maxs) &&
|
||||
(vLocalAttachmentOrigin == m_CachedReflectedData.vLocalSpaceAttachmentOrigin) && (qLocalAttachmentAngles == m_CachedReflectedData.qLocalSpaceAttachmentAngles) )
|
||||
{
|
||||
return; //nothing to update
|
||||
}
|
||||
|
||||
m_CachedReflectedData.vLocalOBB_Mins = vMins;
|
||||
m_CachedReflectedData.vLocalOBB_Maxs = vMaxs;
|
||||
m_CachedReflectedData.vLocalSpaceAttachmentOrigin = vLocalAttachmentOrigin;
|
||||
m_CachedReflectedData.qLocalSpaceAttachmentAngles = qLocalAttachmentAngles;
|
||||
|
||||
Vector vAttachmentVectors[3];
|
||||
AngleVectors( qLocalAttachmentAngles, &vAttachmentVectors[0], &vAttachmentVectors[1], &vAttachmentVectors[2] );
|
||||
float fLargestOBBDiff = vMaxs.x - vMins.x;
|
||||
for( int i = 1; i != 3; ++i )
|
||||
{
|
||||
float fDiff = vMaxs[i] - vMins[i];
|
||||
if( fDiff > fLargestOBBDiff )
|
||||
{
|
||||
fLargestOBBDiff = fDiff;
|
||||
}
|
||||
}
|
||||
fLargestOBBDiff *= 4.0f; //to easily cover diagonal intersection and then some
|
||||
|
||||
Vector vClipBuffers[2][10]; //4 starting points, possible to create 1 extra point per cut, 6 cuts
|
||||
vClipBuffers[0][0] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
|
||||
vClipBuffers[0][1] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
|
||||
vClipBuffers[0][2] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
|
||||
vClipBuffers[0][3] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
|
||||
int iVertCount = 4;
|
||||
|
||||
VPlane vClipPlanes[6];
|
||||
vClipPlanes[0].Init( Vector( 1.0f, 0.0f, 0.0f ), vMins.x );
|
||||
vClipPlanes[1].Init( Vector( -1.0f, 0.0f, 0.0f ), -vMaxs.x );
|
||||
vClipPlanes[2].Init( Vector( 0.0f, 1.0f, 0.0f ), vMins.y );
|
||||
vClipPlanes[3].Init( Vector( 0.0f, -1.0f, 0.0f ), -vMaxs.y );
|
||||
vClipPlanes[4].Init( Vector( 0.0f, 0.0f, 1.0f ), vMins.z );
|
||||
vClipPlanes[5].Init( Vector( 0.0f, 0.0f, -1.0f ), -vMaxs.z );
|
||||
|
||||
for( int i = 0; i != 6; ++i )
|
||||
{
|
||||
iVertCount = ClipPolyToPlane( vClipBuffers[i & 1], iVertCount, vClipBuffers[(i & 1) ^ 1], vClipPlanes[i].m_Normal, vClipPlanes[i].m_Dist, 0.01f );
|
||||
}
|
||||
Assert( iVertCount >= 3 );
|
||||
|
||||
m_LocalSpaceReflectionPolygonVertCount = iVertCount;
|
||||
memcpy( m_LocalSpaceReflectionPolygonVerts, vClipBuffers[0], sizeof( Vector ) * iVertCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (m_CachedReflectedData.vLocalOBB_Maxs.x == m_fWidth) && (m_CachedReflectedData.vLocalOBB_Maxs.y == m_fHeight) )
|
||||
return;
|
||||
|
||||
m_LocalSpaceReflectionPolygonVertCount = 4;
|
||||
float fHalfWidth = m_fWidth / 2.0f;
|
||||
float fHalfHeight = m_fHeight / 2.0f;
|
||||
m_LocalSpaceReflectionPolygonVerts[0].Init( 0.0f, fHalfWidth, fHalfHeight );
|
||||
m_LocalSpaceReflectionPolygonVerts[1].Init( 0.0f, -fHalfWidth, fHalfHeight );
|
||||
m_LocalSpaceReflectionPolygonVerts[2].Init( 0.0f, -fHalfWidth, -fHalfHeight );
|
||||
m_LocalSpaceReflectionPolygonVerts[3].Init( 0.0f, fHalfWidth, -fHalfHeight );
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( TEST_ANIMATION )
|
||||
void CProp_Mirror::Think( void )
|
||||
{
|
||||
StudioFrameAdvance();
|
||||
DispatchAnimEvents( this );
|
||||
|
||||
if (IsSequenceFinished() && !SequenceLoops())
|
||||
{
|
||||
// ResetSequenceInfo();
|
||||
// hack to avoid reloading model every frame
|
||||
m_flAnimTime = gpGlobals->curtime;
|
||||
m_flPlaybackRate = 0.1f;
|
||||
m_bSequenceFinished = false;
|
||||
m_flLastEventCheck = 0;
|
||||
m_flCycle = 0;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int CProp_Mirror::ComputeFrustumThroughPolygon( const Vector &vVisOrigin, const VPlane *pInputFrustum, int iInputFrustumPlanes, VPlane *pOutputFrustum, int iOutputFrustumMaxPlanes )
|
||||
{
|
||||
Vector vTransformedPolyVerts[10];
|
||||
const matrix3x4_t &matLocalToWorld = CollisionProp()->CollisionToWorldTransform();
|
||||
for( int i = 0; i != m_LocalSpaceReflectionPolygonVertCount; ++i )
|
||||
{
|
||||
VectorTransform( &m_LocalSpaceReflectionPolygonVerts[i].x, matLocalToWorld, &vTransformedPolyVerts[i].x );
|
||||
}
|
||||
|
||||
int iReturnedPlanes = UTIL_CalcFrustumThroughConvexPolygon( vTransformedPolyVerts, m_LocalSpaceReflectionPolygonVertCount, vVisOrigin, pInputFrustum, iInputFrustumPlanes, pOutputFrustum, iOutputFrustumMaxPlanes, 0 );
|
||||
|
||||
if( (iReturnedPlanes < iOutputFrustumMaxPlanes) && (iReturnedPlanes != 0) )
|
||||
{
|
||||
Vector vForward;
|
||||
AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward );
|
||||
vForward = -vForward;
|
||||
|
||||
//add the reflection plane as a near plane
|
||||
pOutputFrustum[iReturnedPlanes].Init( vForward, vForward.Dot( m_CachedReflectedData.vAttachmentOrigin ) );
|
||||
++iReturnedPlanes;
|
||||
}
|
||||
|
||||
return iReturnedPlanes;
|
||||
}
|
||||
|
||||
void CProp_Mirror::ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft )
|
||||
{
|
||||
if( iAreasNetworked[MAX_MAP_AREAS - 1] != -1 ) //early out, can't add any more data if we wanted to
|
||||
return;
|
||||
|
||||
UpdateReflectionPlane();
|
||||
|
||||
Vector vForward;
|
||||
AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward );
|
||||
if( vForward.Dot( vVisOrigin ) < vForward.Dot( m_CachedReflectedData.vAttachmentOrigin ) )
|
||||
return; //vis origin is behind the reflection plane
|
||||
|
||||
//both test if the portal is within the view frustum, and calculate the new one at the same time
|
||||
int iFrustumPlanesMax = (iVisFrustumPlanes + m_LocalSpaceReflectionPolygonVertCount + 1);
|
||||
VPlane *pNewFrustum = (VPlane *)stackalloc( sizeof( VPlane ) * iFrustumPlanesMax );
|
||||
|
||||
int iNewFrustumPlanes = ComputeFrustumThroughPolygon( vVisOrigin, pVisFrustum, iVisFrustumPlanes, pNewFrustum, iFrustumPlanesMax );
|
||||
if( iNewFrustumPlanes == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//NDebugOverlay::EntityBounds( this, 0, 255, 0, 100, 0.0f );
|
||||
|
||||
int iArea = NetworkProp()->AreaNum();
|
||||
|
||||
unsigned char *pPVS = m_pExtenderData->iPVSBits;
|
||||
|
||||
if( !m_pExtenderData->bAddedToPVSAlready )
|
||||
{
|
||||
bool bFound = false;
|
||||
for( int i = 0; i != MAX_MAP_AREAS; ++i )
|
||||
{
|
||||
if( iAreasNetworked[i] == iArea )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if( iAreasNetworked[i] == -1 )
|
||||
{
|
||||
bFound = true; //we found it by adding it
|
||||
iAreasNetworked[i] = iArea;
|
||||
int iOutputPVSIntSize = pvssize / sizeof( unsigned int );
|
||||
for( int j = 0; j != iOutputPVSIntSize; ++j )
|
||||
{
|
||||
((unsigned int *)outputPVS)[j] |= ((unsigned int *)pPVS)[j];
|
||||
}
|
||||
for( int j = iOutputPVSIntSize * sizeof( unsigned int ); j != pvssize; ++j )
|
||||
{
|
||||
outputPVS[j] |= pPVS[j];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector vForward;
|
||||
AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward, NULL, NULL );
|
||||
engine->AddOriginToPVS( m_CachedReflectedData.vAttachmentOrigin + vForward );
|
||||
m_pExtenderData->bAddedToPVSAlready = true;
|
||||
}
|
||||
|
||||
--iMaxRecursionsLeft;
|
||||
if( iMaxRecursionsLeft == 0 )
|
||||
return;
|
||||
|
||||
edict_t *myEdict = edict();
|
||||
|
||||
VisExtensionChain_t chainNode;
|
||||
chainNode.m_nArea = iArea;
|
||||
chainNode.pParentChain = pVisChain;
|
||||
|
||||
|
||||
//transform vis origin to linked space
|
||||
Vector vTransformedVisOrigin = m_matReflection * vVisOrigin;
|
||||
|
||||
Vector vTranslation = m_matReflection.GetTranslation();
|
||||
|
||||
//transform the planes into the linked portal space
|
||||
for( int i = 0; i != iNewFrustumPlanes; ++i )
|
||||
{
|
||||
pNewFrustum[i].m_Normal = m_matReflection.ApplyRotation( pNewFrustum[i].m_Normal );
|
||||
pNewFrustum[i].m_Dist += pNewFrustum[i].m_Normal.Dot( vTranslation );
|
||||
}
|
||||
|
||||
Assert( pPVS != NULL );
|
||||
|
||||
//extend the vis by what the linked portal can see
|
||||
for( int i = 0; i != iExtenderCount; ++i )
|
||||
{
|
||||
CPVS_Extender *pExtender = pExtenders[i];
|
||||
|
||||
if ( pExtender->GetExtenderEdict() == myEdict )
|
||||
continue;
|
||||
|
||||
if ( pExtender->GetExtenderNetworkProp()->IsInPVS( myEdict, pPVS, (MAX_MAP_LEAFS/8) ) ) //test against linked portal PVS, not aggregate PVS
|
||||
{
|
||||
chainNode.pExtender = pExtender;
|
||||
pExtender->ComputeSubVisibility( pExtenders, iExtenderCount, outputPVS, pvssize, vTransformedVisOrigin, pNewFrustum, iNewFrustumPlanes, &chainNode, iAreasNetworked, iMaxRecursionsLeft );
|
||||
}
|
||||
}
|
||||
}
|
||||
988
game/server/portal/prop_portal.cpp
Normal file
988
game/server/portal/prop_portal.cpp
Normal file
@@ -0,0 +1,988 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "prop_portal_shared.h"
|
||||
#include "portal_player.h"
|
||||
#include "portal/weapon_physcannon.h"
|
||||
#include "physics_npc_solver.h"
|
||||
#include "envmicrophone.h"
|
||||
#include "env_speaker.h"
|
||||
#include "func_portal_detector.h"
|
||||
#include "model_types.h"
|
||||
#include "te_effect_dispatch.h"
|
||||
#include "collisionutils.h"
|
||||
#include "physobj.h"
|
||||
#include "world.h"
|
||||
#include "hierarchy.h"
|
||||
#include "physics_saverestore.h"
|
||||
#include "PhysicsCloneArea.h"
|
||||
#include "portal_gamestats.h"
|
||||
#include "weapon_portalgun.h"
|
||||
#include "portal_placement.h"
|
||||
#include "physicsshadowclone.h"
|
||||
#include "particle_parse.h"
|
||||
#include "rumble_shared.h"
|
||||
#include "func_portal_orientation.h"
|
||||
#include "env_debughistory.h"
|
||||
#include "tier1/callqueue.h"
|
||||
#include "baseprojector.h"
|
||||
#include "tier1/convar.h"
|
||||
#include "iextpropportallocator.h"
|
||||
#include "matchmaking/imatchframework.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef PORTAL2
|
||||
extern bool UTIL_FizzlePlayerPhotos( CPortal_Player *pPlayer );
|
||||
#endif // PORTAL2
|
||||
|
||||
static CUtlVector<CProp_Portal *> s_PortalLinkageGroups[256];
|
||||
const char *CProp_Portal::s_szDelayedPlacementThinkContext = "CProp_Portal::DelayedPlacementThink";
|
||||
extern ConVar sv_portal_placement_never_fail;
|
||||
extern ConVar use_server_portal_particles;
|
||||
|
||||
|
||||
BEGIN_DATADESC( CProp_Portal )
|
||||
//saving
|
||||
DEFINE_KEYFIELD( m_iLinkageGroupID, FIELD_CHARACTER, "LinkageGroupID" ),
|
||||
DEFINE_KEYFIELD( m_bActivated, FIELD_BOOLEAN, "Activated" ),
|
||||
DEFINE_KEYFIELD( m_bOldActivatedState, FIELD_BOOLEAN, "OldActivated" ),
|
||||
DEFINE_KEYFIELD( m_bIsPortal2, FIELD_BOOLEAN, "PortalTwo" ),
|
||||
|
||||
DEFINE_FIELD( m_NotifyOnPortalled, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hFiredByPlayer, FIELD_EHANDLE ),
|
||||
|
||||
DEFINE_SOUNDPATCH( m_pAmbientSound ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_THINKFUNC( DelayedPlacementThink ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetActivatedState", InputSetActivatedState ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Fizzle", InputFizzle ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "NewLocation", InputNewLocation ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "Resize", InputResize ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetLinkageGroupId", InputSetLinkageGroupId ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CProp_Portal, DT_Prop_Portal )
|
||||
SendPropEHandle( SENDINFO( m_hFiredByPlayer ) ),
|
||||
SendPropInt( SENDINFO( m_nPlacementAttemptParity ), EF_PARITY_BITS, SPROP_UNSIGNED ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( prop_portal, CProp_Portal );
|
||||
|
||||
|
||||
|
||||
CProp_Portal::CProp_Portal( void )
|
||||
{
|
||||
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_FizzleEffect = PORTAL_FIZZLE_KILLED;
|
||||
CProp_Portal_Shared::AllPortals.AddToTail( this );
|
||||
}
|
||||
|
||||
CProp_Portal::~CProp_Portal( void )
|
||||
{
|
||||
CProp_Portal_Shared::AllPortals.FindAndRemove( this );
|
||||
s_PortalLinkageGroups[m_iLinkageGroupID].FindAndRemove( this );
|
||||
}
|
||||
|
||||
void CProp_Portal::Precache( void )
|
||||
{
|
||||
PrecacheScriptSound( "Portal.ambient_loop" );
|
||||
|
||||
PrecacheScriptSound( "Portal.open_blue" );
|
||||
PrecacheScriptSound( "Portal.open_red" );
|
||||
PrecacheScriptSound( "Portal.close_blue" );
|
||||
PrecacheScriptSound( "Portal.close_red" );
|
||||
PrecacheScriptSound( "Portal.fizzle_moved" );
|
||||
PrecacheScriptSound( "Portal.fizzle_invalid_surface" );
|
||||
|
||||
PrecacheModel( "models/portals/portal1.mdl" );
|
||||
PrecacheModel( "models/portals/portal2.mdl" );
|
||||
|
||||
//PrecacheParticleSystem( "portal_1_particles" );
|
||||
//PrecacheParticleSystem( "portal_2_particles" );
|
||||
//PrecacheParticleSystem( "portal_1_edge" );
|
||||
//PrecacheParticleSystem( "portal_2_edge" );
|
||||
//PrecacheParticleSystem( "portal_1_close" );
|
||||
//PrecacheParticleSystem( "portal_2_close" );
|
||||
//PrecacheParticleSystem( "portal_1_badsurface" );
|
||||
//PrecacheParticleSystem( "portal_2_badsurface" );
|
||||
//PrecacheParticleSystem( "portal_1_success" );
|
||||
//PrecacheParticleSystem( "portal_2_success" );
|
||||
|
||||
// adjustable color for coop, two colorable systems instead of four unique -mtw
|
||||
// need two systems here because they spin different directions
|
||||
PrecacheParticleSystem( "portal_edge" );
|
||||
PrecacheParticleSystem( "portal_edge_reverse" );
|
||||
PrecacheParticleSystem( "portal_close" );
|
||||
PrecacheParticleSystem( "portal_badsurface" );
|
||||
PrecacheParticleSystem( "portal_success" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CProp_Portal::CreateSounds()
|
||||
{
|
||||
if (!m_pAmbientSound)
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
m_pAmbientSound = controller.SoundCreate( filter, entindex(), "Portal.ambient_loop" );
|
||||
controller.Play( m_pAmbientSound, 0, 100 );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::StopLoopingSounds()
|
||||
{
|
||||
if ( m_pAmbientSound )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
controller.SoundDestroy( m_pAmbientSound );
|
||||
m_pAmbientSound = NULL;
|
||||
}
|
||||
|
||||
BaseClass::StopLoopingSounds();
|
||||
}
|
||||
|
||||
class CPortalServerDllPropPortalLocator : public IPortalServerDllPropPortalLocator
|
||||
{
|
||||
public:
|
||||
virtual void LocateAllPortals( CUtlVector<PortalInfo_t> &arrPortals )
|
||||
{
|
||||
for ( int iLinkageGroupID = 0; iLinkageGroupID < 3; ++iLinkageGroupID )
|
||||
{
|
||||
for ( int nPortal = 0; nPortal < 2; ++nPortal )
|
||||
{
|
||||
CProp_Portal *pPortal = CProp_Portal::FindPortal( iLinkageGroupID, (nPortal != 0), false );
|
||||
if ( !pPortal )
|
||||
continue;
|
||||
|
||||
const Vector &vecOrigin = pPortal->GetAbsOrigin();
|
||||
const QAngle &vecAngle = pPortal->GetAbsAngles();
|
||||
|
||||
PortalInfo_t pi;
|
||||
pi.iLinkageGroupId = iLinkageGroupID;
|
||||
pi.nPortal = nPortal;
|
||||
pi.vecOrigin = vecOrigin;
|
||||
pi.vecAngle = vecAngle;
|
||||
arrPortals.AddToTail( pi );
|
||||
}
|
||||
}
|
||||
}
|
||||
} s_PortalServerDllPropPortalLocator;
|
||||
|
||||
void CProp_Portal::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
AddToLinkageGroup();
|
||||
|
||||
ResetModel();
|
||||
if( (GetHalfWidth() <= 0) || (GetHalfHeight() <= 0) )
|
||||
Resize( ms_DefaultPortalHalfWidth, ms_DefaultPortalHalfHeight );
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
static bool s_bPortalLocatorForClientRegistered;
|
||||
if ( !s_bPortalLocatorForClientRegistered && g_pMatchFramework )
|
||||
{
|
||||
s_bPortalLocatorForClientRegistered = true;
|
||||
g_pMatchFramework->GetMatchExtensions()->RegisterExtensionInterface( IEXTPROPPORTALLOCATOR_INTERFACE_NAME, &s_PortalServerDllPropPortalLocator );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::OnRestore()
|
||||
{
|
||||
BaseClass::OnRestore();
|
||||
|
||||
if ( IsActive() )
|
||||
{
|
||||
// Place the particles in position
|
||||
DispatchPortalPlacementParticles( m_bIsPortal2 );
|
||||
}
|
||||
|
||||
AddToLinkageGroup();
|
||||
}
|
||||
|
||||
ConVar sv_portals_block_other_players( "sv_portals_block_other_players", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
void CProp_Portal::StartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if( sv_portals_block_other_players.GetBool() && g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
if( pOther->IsPlayer() && (m_hFiredByPlayer.Get() != pOther) )
|
||||
return; //block the interaction
|
||||
}
|
||||
|
||||
return BaseClass::StartTouch( pOther );
|
||||
}
|
||||
|
||||
void CProp_Portal::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if( sv_portals_block_other_players.GetBool() && g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
if( pOther->IsPlayer() && (m_hFiredByPlayer.Get() != pOther) )
|
||||
return; //block the interaction
|
||||
}
|
||||
|
||||
return BaseClass::Touch( pOther );
|
||||
}
|
||||
|
||||
void CProp_Portal::EndTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if( sv_portals_block_other_players.GetBool() && g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
if( pOther->IsPlayer() && (m_hFiredByPlayer.Get() != pOther) )
|
||||
return; //block the interaction
|
||||
}
|
||||
|
||||
return BaseClass::EndTouch( pOther );
|
||||
}
|
||||
|
||||
void DumpActiveCollision( const CPortalSimulator *pPortalSimulator, const char *szFileName );
|
||||
void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename );
|
||||
|
||||
|
||||
|
||||
|
||||
void CProp_Portal::ResetModel( void )
|
||||
{
|
||||
if( !m_bIsPortal2 )
|
||||
SetModel( "models/portals/portal1.mdl" );
|
||||
else
|
||||
SetModel( "models/portals/portal2.mdl" );
|
||||
|
||||
if( IsMobile() || ((m_hLinkedPortal.Get() != NULL) && !m_hLinkedPortal->IsMobile()) )
|
||||
{
|
||||
SetSize( GetLocalMins(), Vector( 4.0f, m_fNetworkHalfWidth, m_fNetworkHalfHeight ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSize( GetLocalMins(), GetLocalMaxs() );
|
||||
}
|
||||
|
||||
SetSolid( SOLID_OBB );
|
||||
SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID | FSOLID_CUSTOMBOXTEST | FSOLID_CUSTOMRAYTEST );
|
||||
}
|
||||
|
||||
void CProp_Portal::DoFizzleEffect( int iEffect, bool bDelayedPos /*= true*/ )
|
||||
{
|
||||
m_vAudioOrigin = ( ( bDelayedPos ) ? ( m_vDelayedPosition ) : ( m_vOldPosition ) );
|
||||
|
||||
CEffectData fxData;
|
||||
|
||||
fxData.m_vAngles = ( ( bDelayedPos ) ? ( m_qDelayedAngles ) : ( m_qOldAngles ) );
|
||||
|
||||
Vector vForward, vUp;
|
||||
AngleVectors( fxData.m_vAngles, &vForward, &vUp, NULL );
|
||||
fxData.m_vOrigin = m_vAudioOrigin + vForward * 1.0f;
|
||||
|
||||
fxData.m_nColor = ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) );
|
||||
|
||||
EmitSound_t ep;
|
||||
CPASAttenuationFilter filter( m_vDelayedPosition );
|
||||
|
||||
ep.m_nChannel = CHAN_STATIC;
|
||||
ep.m_flVolume = 1.0f;
|
||||
ep.m_pOrigin = &m_vAudioOrigin;
|
||||
|
||||
int nTeam = GetTeamNumber();
|
||||
int nPortalNum = m_bIsPortal2 ? 2 : 1;
|
||||
|
||||
// Rumble effects on the firing player (if one exists)
|
||||
CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( m_hPlacedBy.Get() );
|
||||
CBasePlayer* pPlayer = NULL;
|
||||
|
||||
if ( pPortalGun )
|
||||
{
|
||||
pPlayer = (CBasePlayer*)pPortalGun->GetOwner();
|
||||
if ( pPlayer )
|
||||
{
|
||||
if ( iEffect != PORTAL_FIZZLE_CLOSE &&
|
||||
iEffect != PORTAL_FIZZLE_SUCCESS &&
|
||||
iEffect != PORTAL_FIZZLE_NONE )
|
||||
{
|
||||
pPlayer->RumbleEffect( RUMBLE_PORTAL_PLACEMENT_FAILURE, 0, RUMBLE_FLAGS_NONE );
|
||||
}
|
||||
|
||||
nTeam = pPlayer->GetTeamNumber();
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a fizzle effect
|
||||
switch ( iEffect )
|
||||
{
|
||||
case PORTAL_FIZZLE_CANT_FIT:
|
||||
//DispatchEffect( "PortalFizzleCantFit", fxData );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_OVERLAPPED_LINKED:
|
||||
{
|
||||
/*CProp_Portal *pLinkedPortal = m_hLinkedPortal;
|
||||
if ( pLinkedPortal )
|
||||
{
|
||||
Vector vLinkedForward;
|
||||
pLinkedPortal->GetVectors( &vLinkedForward, NULL, NULL );
|
||||
fxData.m_vStart = pLink3edPortal->GetAbsOrigin() + vLinkedForward * 5.0f;
|
||||
}*/
|
||||
|
||||
//DispatchEffect( "PortalFizzleOverlappedLinked", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
}
|
||||
|
||||
case PORTAL_FIZZLE_BAD_VOLUME:
|
||||
//DispatchEffect( "PortalFizzleBadVolume", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_BAD_SURFACE:
|
||||
//DispatchEffect( "PortalFizzleBadSurface", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_KILLED:
|
||||
//DispatchEffect( "PortalFizzleKilled", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_CLOSE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_moved";
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_CLEANSER:
|
||||
//DispatchEffect( "PortalFizzleCleanser", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_CLOSE:
|
||||
//DispatchEffect( "PortalFizzleKilled", fxData );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_CLOSE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = ( ( m_bIsPortal2 ) ? ( "Portal.close_red" ) : ( "Portal.close_blue" ) );
|
||||
break;
|
||||
|
||||
case PORTAL_FIZZLE_NEAR_BLUE:
|
||||
{
|
||||
if ( !m_bIsPortal2 )
|
||||
{
|
||||
Vector vLinkedForward;
|
||||
m_hLinkedPortal->GetVectors( &vLinkedForward, NULL, NULL );
|
||||
fxData.m_vOrigin = m_hLinkedPortal->GetAbsOrigin() + vLinkedForward * 16.0f;
|
||||
fxData.m_vAngles = m_hLinkedPortal->GetAbsAngles();
|
||||
}
|
||||
else
|
||||
{
|
||||
GetVectors( &vForward, NULL, NULL );
|
||||
fxData.m_vOrigin = GetAbsOrigin() + vForward * 16.0f;
|
||||
fxData.m_vAngles = GetAbsAngles();
|
||||
}
|
||||
|
||||
//DispatchEffect( "PortalFizzleNear", fxData );
|
||||
AngleVectors( fxData.m_vAngles, &vForward, &vUp, NULL );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
}
|
||||
|
||||
case PORTAL_FIZZLE_NEAR_RED:
|
||||
{
|
||||
if ( m_bIsPortal2 )
|
||||
{
|
||||
Vector vLinkedForward;
|
||||
m_hLinkedPortal->GetVectors( &vLinkedForward, NULL, NULL );
|
||||
fxData.m_vOrigin = m_hLinkedPortal->GetAbsOrigin() + vLinkedForward * 16.0f;
|
||||
fxData.m_vAngles = m_hLinkedPortal->GetAbsAngles();
|
||||
}
|
||||
else
|
||||
{
|
||||
GetVectors( &vForward, NULL, NULL );
|
||||
fxData.m_vOrigin = GetAbsOrigin() + vForward * 16.0f;
|
||||
fxData.m_vAngles = GetAbsAngles();
|
||||
}
|
||||
|
||||
//DispatchEffect( "PortalFizzleNear", fxData );
|
||||
AngleVectors( fxData.m_vAngles, &vForward, &vUp, NULL );
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_BAD_SURFACE, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
ep.m_pSoundName = "Portal.fizzle_invalid_surface";
|
||||
break;
|
||||
}
|
||||
|
||||
case PORTAL_FIZZLE_SUCCESS:
|
||||
VectorAngles( vUp, vForward, fxData.m_vAngles );
|
||||
CreatePortalEffect( pPlayer, PORTAL_FIZZLE_SUCCESS, fxData.m_vOrigin, fxData.m_vAngles, nTeam, nPortalNum );
|
||||
// Don't make a sound!
|
||||
return;
|
||||
|
||||
case PORTAL_FIZZLE_NONE:
|
||||
// Don't do anything!
|
||||
return;
|
||||
}
|
||||
|
||||
EmitSound( filter, SOUND_FROM_WORLD, ep );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create the portal effect
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::CreatePortalEffect( CBasePlayer* pPlayer, int iEffect, Vector vecOrigin, QAngle qAngles, int nTeam, int nPortalNum )
|
||||
{
|
||||
if ( !pPlayer || iEffect == PORTAL_FIZZLE_NONE )
|
||||
return;
|
||||
|
||||
CBroadcastRecipientFilter filter;
|
||||
filter.MakeReliable();
|
||||
|
||||
// remove the player who shot it because we handle this in
|
||||
// the client code and don't need to send a message
|
||||
if ( pPlayer->m_bPredictionEnabled )
|
||||
{
|
||||
filter.RemoveRecipient( pPlayer );
|
||||
}
|
||||
|
||||
UserMessageBegin( filter, "PortalFX_Surface" );
|
||||
WRITE_SHORT( entindex() );
|
||||
WRITE_SHORT( pPlayer->entindex() );
|
||||
WRITE_BYTE( nTeam );
|
||||
WRITE_BYTE( nPortalNum );
|
||||
WRITE_BYTE( iEffect );
|
||||
WRITE_VEC3COORD( vecOrigin );
|
||||
WRITE_ANGLES( qAngles );
|
||||
MessageEnd();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Fizzle the portal
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::OnPortalDeactivated( void )
|
||||
{
|
||||
if ( m_pAmbientSound )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
controller.SoundChangeVolume( m_pAmbientSound, 0.0, 0.0 );
|
||||
}
|
||||
|
||||
//TODO: Fizzle Effects
|
||||
DoFizzleEffect( m_FizzleEffect );
|
||||
m_FizzleEffect = PORTAL_FIZZLE_KILLED; //assume we want a generic killed type unless someone sets it to something else before we fizzle next. Lets CPortal_Base2D kill us with a fizzle effect while it has no knowledge of fizzling
|
||||
|
||||
BaseClass::OnPortalDeactivated();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Portal will fizzle next time we get to think
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::Fizzle( void )
|
||||
{
|
||||
m_FizzleEffect = PORTAL_FIZZLE_NONE; //Logic that uses Fizzle() always calls DoFizzleEffect() manually
|
||||
//DeactivatePortalOnThink();
|
||||
DeactivatePortalNow();
|
||||
}
|
||||
|
||||
|
||||
void CProp_Portal::Activate( void )
|
||||
{
|
||||
|
||||
CreateSounds();
|
||||
|
||||
BaseClass::Activate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Kinda sucks... Normal triggers won't find portals because they're also triggers.
|
||||
// Rather than addressing that directly, portal detectors look for portals with an explicit OBB check.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::UpdatePortalDetectorsOnPortalMoved( void )
|
||||
{
|
||||
for ( CFuncPortalDetector *pDetector = GetPortalDetectorList(); pDetector != NULL; pDetector = pDetector->m_pNext )
|
||||
{
|
||||
pDetector->UpdateOnPortalMoved( this );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::UpdatePortalDetectorsOnPortalActivated( void )
|
||||
{
|
||||
for ( CFuncPortalDetector *pDetector = GetPortalDetectorList(); pDetector != NULL; pDetector = pDetector->m_pNext )
|
||||
{
|
||||
pDetector->UpdateOnPortalActivated( this );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::UpdatePortalLinkage( void )
|
||||
{
|
||||
if( IsActive() )
|
||||
{
|
||||
CProp_Portal *pLink = (CProp_Portal *)m_hLinkedPortal.Get();
|
||||
|
||||
if( !(pLink && pLink->IsActive()) )
|
||||
{
|
||||
//no old link, or inactive old link
|
||||
|
||||
if( pLink )
|
||||
{
|
||||
//we had an old link, must be inactive. Make doubly sure it's disconnected
|
||||
if( pLink->m_hLinkedPortal.Get() != NULL )
|
||||
{
|
||||
if( pLink->m_hLinkedPortal.Get() == this )
|
||||
pLink->m_hLinkedPortal = NULL; //avoid recursion
|
||||
|
||||
pLink->UpdatePortalLinkage();
|
||||
}
|
||||
|
||||
pLink = NULL;
|
||||
}
|
||||
|
||||
int iPortalCount = s_PortalLinkageGroups[m_iLinkageGroupID].Count();
|
||||
|
||||
// More than two sharing a linkage id? is that valid?
|
||||
//Assert( iPortalCount <3 ); yes it is as long as only two are active
|
||||
|
||||
if( iPortalCount != 0 )
|
||||
{
|
||||
CProp_Portal **pPortals = s_PortalLinkageGroups[m_iLinkageGroupID].Base();
|
||||
for( int i = 0; i != iPortalCount; ++i )
|
||||
{
|
||||
CProp_Portal *pCurrentPortal = pPortals[i];
|
||||
if( pCurrentPortal == this )
|
||||
continue;
|
||||
if( pCurrentPortal->IsActive() &&
|
||||
(pCurrentPortal->m_hLinkedPortal.Get() == NULL) &&
|
||||
(pCurrentPortal->m_fNetworkHalfWidth == m_fNetworkHalfWidth) &&
|
||||
(pCurrentPortal->m_fNetworkHalfHeight == m_fNetworkHalfHeight) )
|
||||
{
|
||||
pLink = pCurrentPortal;
|
||||
pCurrentPortal->m_hLinkedPortal = this;
|
||||
pCurrentPortal->UpdatePortalLinkage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_hLinkedPortal = pLink;
|
||||
}
|
||||
else
|
||||
{
|
||||
CProp_Portal *pRemote = (CProp_Portal *)m_hLinkedPortal.Get();
|
||||
//apparently we've been deactivated
|
||||
m_PortalSimulator.DetachFromLinked();
|
||||
m_PortalSimulator.ReleaseAllEntityOwnership();
|
||||
|
||||
m_hLinkedPortal = NULL;
|
||||
if( pRemote )
|
||||
pRemote->UpdatePortalLinkage();
|
||||
}
|
||||
|
||||
BaseClass::UpdatePortalLinkage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CProp_Portal::DispatchPortalPlacementParticles( bool bIsSecondaryPortal )
|
||||
{
|
||||
// never do this in multiplayer
|
||||
if ( GameRules()->IsMultiplayer() )
|
||||
return;
|
||||
|
||||
// the particle effects are no longer created on the server in SP unless this convar is set,
|
||||
// if it's not set, they are created on the client in function: CreateAttachedParticles
|
||||
if ( !use_server_portal_particles.GetBool() )
|
||||
return;
|
||||
|
||||
// Send the particles only to the player who
|
||||
CBasePlayer *pFiringPlayer = ToBasePlayer( m_hFiredByPlayer.Get() );
|
||||
if ( pFiringPlayer )
|
||||
{
|
||||
CSingleUserRecipientFilter localFilter( pFiringPlayer );
|
||||
localFilter.MakeReliable();
|
||||
DispatchParticleEffect( ( ( bIsSecondaryPortal ) ? ( "portal_2_edge" ) : ( "portal_1_edge" ) ), PATTACH_POINT_FOLLOW, this, "particles", true, -1, &localFilter );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::NewLocation( const Vector &vOrigin, const QAngle &qAngles )
|
||||
{
|
||||
BaseClass::NewLocation( vOrigin, qAngles );
|
||||
CreateSounds();
|
||||
|
||||
UpdatePortalDetectorsOnPortalMoved();
|
||||
if( (m_hLinkedPortal.Get() != NULL) && (m_bOldActivatedState == false) && (IsActive() == true) )
|
||||
{
|
||||
//went from inactive to active
|
||||
UpdatePortalDetectorsOnPortalActivated();
|
||||
((CProp_Portal *)m_hLinkedPortal.Get())->UpdatePortalDetectorsOnPortalActivated();
|
||||
}
|
||||
|
||||
if( m_NotifyOnPortalled && !m_NotifyOnPortalled->IsPortalTouchingDetector( this ) )
|
||||
m_NotifyOnPortalled = NULL;
|
||||
|
||||
if ( m_pAmbientSound )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
controller.SoundChangeVolume( m_pAmbientSound, 0.4, 0.1 );
|
||||
}
|
||||
|
||||
// Place the particles in position
|
||||
DispatchPortalPlacementParticles( m_bIsPortal2 );
|
||||
|
||||
if( !IsMobile() )
|
||||
{
|
||||
if ( m_bIsPortal2 )
|
||||
{
|
||||
EmitSound( "Portal.open_red" );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( "Portal.open_blue" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::PreTeleportTouchingEntity( CBaseEntity *pOther )
|
||||
{
|
||||
if( m_NotifyOnPortalled )
|
||||
m_NotifyOnPortalled->OnPrePortalled( pOther, true );
|
||||
|
||||
CProp_Portal *pLinked = (CProp_Portal *)m_hLinkedPortal.Get();
|
||||
if( pLinked->m_NotifyOnPortalled )
|
||||
pLinked->m_NotifyOnPortalled->OnPrePortalled( pOther, false );
|
||||
|
||||
BaseClass::PreTeleportTouchingEntity( pOther );
|
||||
}
|
||||
|
||||
void CProp_Portal::PostTeleportTouchingEntity( CBaseEntity *pOther )
|
||||
{
|
||||
if( m_NotifyOnPortalled )
|
||||
m_NotifyOnPortalled->OnPostPortalled( pOther, true );
|
||||
|
||||
CProp_Portal *pLinked = (CProp_Portal *)m_hLinkedPortal.Get();
|
||||
if( pLinked->m_NotifyOnPortalled )
|
||||
pLinked->m_NotifyOnPortalled->OnPostPortalled( pOther, false );
|
||||
|
||||
BaseClass::PostTeleportTouchingEntity( pOther );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::ActivatePortal( void )
|
||||
{
|
||||
m_hPlacedBy = NULL;
|
||||
|
||||
Vector vOrigin;
|
||||
vOrigin = GetAbsOrigin();
|
||||
|
||||
Vector vForward, vUp;
|
||||
GetVectors( &vForward, 0, &vUp );
|
||||
|
||||
CTraceFilterSimpleClassnameList baseFilter( this, COLLISION_GROUP_NONE );
|
||||
UTIL_Portal_Trace_Filter( &baseFilter );
|
||||
CTraceFilterTranslateClones traceFilterPortalShot( &baseFilter );
|
||||
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( vOrigin + vForward, vOrigin + vForward * -8.0f, MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
|
||||
|
||||
QAngle qAngles;
|
||||
VectorAngles( tr.plane.normal, vUp, qAngles );
|
||||
|
||||
PortalPlacementResult_t eResult = VerifyPortalPlacementAndFizzleBlockingPortals( this, tr.endpos, qAngles, GetHalfWidth(), GetHalfHeight(), PORTAL_PLACED_BY_FIXED );
|
||||
|
||||
PlacePortal( tr.endpos, qAngles, eResult );
|
||||
|
||||
// If the fixed portal is overlapping a portal that was placed before it... kill it!
|
||||
if ( PortalPlacementSucceeded( eResult ) )
|
||||
{
|
||||
CreateSounds();
|
||||
|
||||
if ( m_pAmbientSound )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
controller.SoundChangeVolume( m_pAmbientSound, 0.4, 0.1 );
|
||||
}
|
||||
|
||||
// Place the particles in position
|
||||
DispatchPortalPlacementParticles( m_bIsPortal2 );
|
||||
|
||||
if ( m_bIsPortal2 )
|
||||
{
|
||||
EmitSound( "Portal.open_red" );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( "Portal.open_blue" );
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePortalTeleportMatrix();
|
||||
|
||||
UpdatePortalLinkage();
|
||||
|
||||
CBaseProjector::TestAllForProjectionChanges();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::DeactivatePortal( void )
|
||||
{
|
||||
if ( m_pAmbientSound )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
controller.SoundChangeVolume( m_pAmbientSound, 0.0, 0.0 );
|
||||
}
|
||||
|
||||
StopParticleEffects( this );
|
||||
|
||||
UpdatePortalTeleportMatrix();
|
||||
|
||||
UpdatePortalLinkage();
|
||||
|
||||
CBaseProjector::TestAllForProjectionChanges();
|
||||
}
|
||||
|
||||
void CProp_Portal::InputSetActivatedState( inputdata_t &inputdata )
|
||||
{
|
||||
SetActive( inputdata.value.Bool() );
|
||||
|
||||
if ( IsActive() )
|
||||
{
|
||||
ActivatePortal();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivatePortal();
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::InputFizzle( inputdata_t &inputdata )
|
||||
{
|
||||
DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
|
||||
Fizzle();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Map can call new location, so far it's only for debugging purposes so it's not made to be very robust.
|
||||
// Input : &inputdata - String with 6 float entries with space delimiters, location and orientation
|
||||
//-----------------------------------------------------------------------------
|
||||
void CProp_Portal::InputNewLocation( inputdata_t &inputdata )
|
||||
{
|
||||
char sLocationStats[MAX_PATH];
|
||||
Q_strncpy( sLocationStats, inputdata.value.String(), sizeof(sLocationStats) );
|
||||
|
||||
// first 3 are location of new origin
|
||||
Vector vNewOrigin;
|
||||
char* pTok = strtok( sLocationStats, " " );
|
||||
vNewOrigin.x = atof(pTok);
|
||||
pTok = strtok( NULL, " " );
|
||||
vNewOrigin.y = atof(pTok);
|
||||
pTok = strtok( NULL, " " );
|
||||
vNewOrigin.z = atof(pTok);
|
||||
|
||||
// Next 3 entries are new angles
|
||||
QAngle vNewAngles;
|
||||
pTok = strtok( NULL, " " );
|
||||
vNewAngles.x = atof(pTok);
|
||||
pTok = strtok( NULL, " " );
|
||||
vNewAngles.y = atof(pTok);
|
||||
pTok = strtok( NULL, " " );
|
||||
vNewAngles.z = atof(pTok);
|
||||
|
||||
// Call main placement function (skipping placement rules)
|
||||
NewLocation( vNewOrigin, vNewAngles );
|
||||
}
|
||||
|
||||
void CProp_Portal::InputResize( inputdata_t &inputdata )
|
||||
{
|
||||
char sResizeStats[MAX_PATH];
|
||||
Q_strncpy( sResizeStats, inputdata.value.String(), sizeof(sResizeStats) );
|
||||
|
||||
char* pTok = strtok( sResizeStats, " " );
|
||||
float fHalfWidth = atof(pTok);
|
||||
pTok = strtok( NULL, " " );
|
||||
float fHalfHeight = atof(pTok);
|
||||
|
||||
Resize( fHalfWidth, fHalfHeight );
|
||||
}
|
||||
|
||||
void CProp_Portal::InputSetLinkageGroupId( inputdata_t &inputdata )
|
||||
{
|
||||
int iGroupId = inputdata.value.Int();
|
||||
|
||||
if ( ( iGroupId >= 0 ) && ( iGroupId < 255 ) )
|
||||
{
|
||||
ChangeLinkageGroup( iGroupId );
|
||||
|
||||
if ( IsActive() )
|
||||
{
|
||||
SetActive( false );
|
||||
|
||||
// shut the portal down and reactivate it so it will re-link with new portal group id
|
||||
DeactivatePortal();
|
||||
SetActive( true );
|
||||
ActivatePortal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "*** SetLinkageGroupId input failed because Portal ID must be between 0 and 255!\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::AddToLinkageGroup( void )
|
||||
{
|
||||
if ( m_iLinkageGroupID != PORTAL_LINKAGE_GROUP_INVALID )
|
||||
{
|
||||
if( s_PortalLinkageGroups[m_iLinkageGroupID].Find( this ) == -1 )
|
||||
s_PortalLinkageGroups[m_iLinkageGroupID].AddToTail( this );
|
||||
}
|
||||
}
|
||||
|
||||
void CProp_Portal::ChangeLinkageGroup( unsigned char iLinkageGroupID )
|
||||
{
|
||||
if ( iLinkageGroupID == PORTAL_LINKAGE_GROUP_INVALID )
|
||||
{
|
||||
// invalid is the 'inactive portal' group for portals not yet linked.
|
||||
m_iLinkageGroupID = iLinkageGroupID;
|
||||
return;
|
||||
}
|
||||
|
||||
// We should be moving from a linkage id to another one, unles we're coming from INVALID
|
||||
Assert( s_PortalLinkageGroups[m_iLinkageGroupID].Find( this ) != -1 || m_iLinkageGroupID == PORTAL_LINKAGE_GROUP_INVALID );
|
||||
s_PortalLinkageGroups[m_iLinkageGroupID].FindAndRemove( this );
|
||||
s_PortalLinkageGroups[iLinkageGroupID].AddToTail( this );
|
||||
m_iLinkageGroupID = iLinkageGroupID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CProp_Portal *CProp_Portal::FindPortal( unsigned char iLinkageGroupID, bool bPortal2, bool bCreateIfNothingFound /*= false*/ )
|
||||
{
|
||||
int iPortalCount = s_PortalLinkageGroups[iLinkageGroupID].Count();
|
||||
|
||||
if( iPortalCount != 0 )
|
||||
{
|
||||
CProp_Portal *pFoundInactive = NULL;
|
||||
CProp_Portal **pPortals = s_PortalLinkageGroups[iLinkageGroupID].Base();
|
||||
for( int i = 0; i != iPortalCount; ++i )
|
||||
{
|
||||
if( pPortals[i]->m_bIsPortal2 == bPortal2 )
|
||||
{
|
||||
if( pPortals[i]->IsActive() )
|
||||
return pPortals[i];
|
||||
else
|
||||
pFoundInactive = pPortals[i];
|
||||
}
|
||||
}
|
||||
|
||||
if( pFoundInactive )
|
||||
return pFoundInactive;
|
||||
}
|
||||
|
||||
if( bCreateIfNothingFound )
|
||||
{
|
||||
CProp_Portal *pPortal = (CProp_Portal *)CreateEntityByName( "prop_portal" );
|
||||
pPortal->m_iLinkageGroupID = iLinkageGroupID;
|
||||
pPortal->m_bIsPortal2 = bPortal2;
|
||||
DispatchSpawn( pPortal );
|
||||
return pPortal;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CUtlVector<CProp_Portal *> *CProp_Portal::GetPortalLinkageGroup( unsigned char iLinkageGroupID )
|
||||
{
|
||||
return &s_PortalLinkageGroups[iLinkageGroupID];
|
||||
}
|
||||
|
||||
|
||||
// Hands out linkage IDs in order. If somebody has taken the slot, it walks to a free one and picks that as the new starting location.
|
||||
static unsigned char s_iBestGuessUnusedLinkageID = 0;
|
||||
unsigned char UTIL_GetUnusedLinkageID( void )
|
||||
{
|
||||
if ( s_PortalLinkageGroups[s_iBestGuessUnusedLinkageID].Count() == 0 )
|
||||
{
|
||||
// early out for best guess
|
||||
return s_iBestGuessUnusedLinkageID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// walk all linkage groups for a free one
|
||||
for ( int i = 0; i < 256; ++i )
|
||||
{
|
||||
if ( s_PortalLinkageGroups[i].Count() == 0 )
|
||||
{
|
||||
s_iBestGuessUnusedLinkageID = i+1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Warning( "*** All portal linkage IDs in use! ***\nThere may be >254 portal pairs, or some bug causing the linkage IDs not to be freed up.\n" );
|
||||
Assert( 0 );
|
||||
return PORTAL_LINKAGE_GROUP_INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose: Create an NPC of the given type
|
||||
//------------------------------------------------------------------------------
|
||||
void CC_Resize_Portals( const CCommand &args )
|
||||
{
|
||||
if( args.ArgC() < 3 )
|
||||
{
|
||||
Warning( "syntax: Portals_ResizeAll [half width] [half height]\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
float fHalfWidth = atof(args[1]);
|
||||
float fHalfHeight = atof(args[2]);
|
||||
|
||||
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
|
||||
|
||||
for( int i = 0; i != iPortalCount; ++i )
|
||||
{
|
||||
CProp_Portal_Shared::AllPortals[i]->Resize( fHalfWidth, fHalfHeight );
|
||||
}
|
||||
|
||||
CProp_Portal::ms_DefaultPortalHalfWidth = fHalfWidth;
|
||||
CProp_Portal::ms_DefaultPortalHalfHeight = fHalfHeight;
|
||||
}
|
||||
static ConCommand Portals_ResizeAll("Portals_ResizeAll", CC_Resize_Portals, "Resizes all portals (for testing), Portals_ResizeAll [half width] [half height]", FCVAR_CHEAT);
|
||||
|
||||
122
game/server/portal/prop_portal.h
Normal file
122
game/server/portal/prop_portal.h
Normal file
@@ -0,0 +1,122 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef PROP_PORTAL_H
|
||||
#define PROP_PORTAL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "baseanimating.h"
|
||||
#include "PortalSimulation.h"
|
||||
#include "portal_base2d.h"
|
||||
#include "../portal2/func_portalled.h"
|
||||
|
||||
// FIX ME
|
||||
#include "portal_shareddefs.h"
|
||||
|
||||
// This is for portals not yet linked.
|
||||
#define PORTAL_LINKAGE_GROUP_INVALID 255
|
||||
|
||||
class CPhysicsCloneArea;
|
||||
|
||||
class CProp_Portal : public CPortal_Base2D
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CProp_Portal, CPortal_Base2D );
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CProp_Portal( void );
|
||||
virtual ~CProp_Portal( void );
|
||||
|
||||
CSoundPatch *m_pAmbientSound;
|
||||
|
||||
virtual void Precache( void );
|
||||
virtual void CreateSounds( void );
|
||||
virtual void StopLoopingSounds( void );
|
||||
virtual void Spawn( void );
|
||||
virtual void Activate( void );
|
||||
virtual void OnRestore( void );
|
||||
|
||||
void DelayedPlacementThink( void );
|
||||
static const char * s_szDelayedPlacementThinkContext;
|
||||
|
||||
void PlacePortal( const Vector &vOrigin, const QAngle &qAngles, PortalPlacementResult_t eResult, bool bDelay = false );
|
||||
void NewLocation( const Vector &vOrigin, const QAngle &qAngles );
|
||||
|
||||
virtual void PreTeleportTouchingEntity( CBaseEntity *pOther );
|
||||
virtual void PostTeleportTouchingEntity( CBaseEntity *pOther );
|
||||
|
||||
void ResetModel( void ); //sets the model and bounding box
|
||||
void DoFizzleEffect( int iEffect, bool bDelayedPos = true ); //display cool visual effect
|
||||
void CreatePortalEffect( CBasePlayer* pPlayer, int iEffect, Vector vecOrigin, QAngle qAngles, int nTeam, int nPortalNum );
|
||||
void OnPortalDeactivated( void );
|
||||
void Fizzle( void );
|
||||
void ActivatePortal( void );
|
||||
void DeactivatePortal( void );
|
||||
|
||||
void InputSetActivatedState( inputdata_t &inputdata );
|
||||
void InputFizzle( inputdata_t &inputdata );
|
||||
void InputNewLocation( inputdata_t &inputdata );
|
||||
void InputResize( inputdata_t &inputdata );
|
||||
void InputSetLinkageGroupId( inputdata_t &inputdata );
|
||||
|
||||
void AddToLinkageGroup( void );
|
||||
virtual void UpdatePortalLinkage( void );
|
||||
|
||||
virtual void StartTouch( CBaseEntity *pOther );
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
virtual void EndTouch( CBaseEntity *pOther );
|
||||
|
||||
//virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo ) { return FL_EDICT_ALWAYS; }
|
||||
|
||||
virtual int UpdateTransmitState( void ) // set transmit filter to transmit always
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
private:
|
||||
void DispatchPortalPlacementParticles( bool bIsSecondaryPortal );
|
||||
void UpdatePortalDetectorsOnPortalMoved( void );
|
||||
void UpdatePortalDetectorsOnPortalActivated( void );
|
||||
|
||||
unsigned char m_iLinkageGroupID; //a group ID specifying which portals this one can possibly link to
|
||||
PortalFizzleType_t m_FizzleEffect;
|
||||
|
||||
CNetworkHandle( CBaseEntity, m_hFiredByPlayer );
|
||||
CHandle<CFunc_Portalled> m_NotifyOnPortalled; //an entity that forwards notifications of teleports to map logic entities
|
||||
|
||||
public:
|
||||
friend class CPropPortalTunnel;
|
||||
inline unsigned char GetLinkageGroup( void ) const { return m_iLinkageGroupID; };
|
||||
void ChangeLinkageGroup( unsigned char iLinkageGroupID );
|
||||
void SetFiredByPlayer( CBasePlayer *pPlayer );
|
||||
inline CBasePlayer *GetFiredByPlayer( void ) const { return (CBasePlayer *)m_hFiredByPlayer.Get(); }
|
||||
|
||||
inline void SetFuncPortalled( CFunc_Portalled *pPortalledEnt = NULL ) { m_NotifyOnPortalled = pPortalledEnt; }
|
||||
|
||||
static bool ms_DefaultPortalSizeInitialized; // for CEG protection
|
||||
static float ms_DefaultPortalHalfWidth;
|
||||
static float ms_DefaultPortalHalfHeight;
|
||||
|
||||
//NULL portal will return default width/height
|
||||
static void GetPortalSize( float &fHalfWidth, float &fHalfHeight, CProp_Portal *pPortal = NULL );
|
||||
|
||||
//find a portal with the designated attributes, or creates one with them, favors active portals over inactive
|
||||
static CProp_Portal *FindPortal( unsigned char iLinkageGroupID, bool bPortal2, bool bCreateIfNothingFound = false );
|
||||
static const CUtlVector<CProp_Portal *> *GetPortalLinkageGroup( unsigned char iLinkageGroupID );
|
||||
|
||||
virtual float GetMinimumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity ); //return -FLT_MAX for no minimum
|
||||
virtual float GetMaximumExitSpeed( bool bPlayer, bool bEntranceOnFloor, bool bExitOnFloor, const Vector &vEntityCenterAtExit, CBaseEntity *pEntity ); //return FLT_MAX for no maximum
|
||||
|
||||
CNetworkVar( int, m_nPlacementAttemptParity ); //Increments every time we try to move the portal in a predictable way. Will send a network packet to catch cases where placement succeeds on the client, but fails on the server.
|
||||
};
|
||||
|
||||
// Finds a free linkage id for a portal.
|
||||
unsigned char UTIL_GetUnusedLinkageID( void );
|
||||
#endif //#ifndef PROP_PORTAL_H
|
||||
158
game/server/portal/pvs_extender.cpp
Normal file
158
game/server/portal/pvs_extender.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "pvs_extender.h"
|
||||
#include "util_shared.h"
|
||||
#include "tier1/utlvector.h"
|
||||
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static CUtlVector<CPVS_Extender *> s_AllExtenders;
|
||||
CPVS_Extender::CPVS_Extender( void )
|
||||
{
|
||||
s_AllExtenders.AddToTail( this );
|
||||
}
|
||||
|
||||
CPVS_Extender::~CPVS_Extender( void )
|
||||
{
|
||||
s_AllExtenders.FindAndFastRemove( this );
|
||||
}
|
||||
|
||||
|
||||
void CPVS_Extender::ComputeExtendedPVS( const CBaseEntity *pViewEntity, const Vector &vVisOrigin, unsigned char *outputPVS, int pvssize, int iMaxRecursions )
|
||||
{
|
||||
int iAllExtenderCount = s_AllExtenders.Count();
|
||||
if( iAllExtenderCount == 0 )
|
||||
return;
|
||||
|
||||
static CThreadFastMutex s_PVSExtenderMutex;
|
||||
AUTO_LOCK_FM( s_PVSExtenderMutex );
|
||||
|
||||
CPVS_Extender **pAllExtenders = s_AllExtenders.Base();
|
||||
|
||||
int iExtenderCount = 0;
|
||||
CPVS_Extender **pExtenders = (CPVS_Extender **)stackalloc( sizeof( CPVS_Extender * ) * iAllExtenderCount );
|
||||
|
||||
//filter out portals that can't possibly extend visibility up-front. So we don't have to do so in our recursions
|
||||
for( int i = 0; i != iAllExtenderCount; ++i )
|
||||
{
|
||||
CPVS_Extender *pExtender = pAllExtenders[i];
|
||||
Assert( pExtender );
|
||||
|
||||
if ( pExtender->IsExtenderValid() )
|
||||
{
|
||||
if ( pExtender->GetExtenderNetworkProp()->AreaNum() < 0 )
|
||||
{
|
||||
Assert( false );
|
||||
continue;
|
||||
}
|
||||
|
||||
pExtenders[iExtenderCount] = pExtender;
|
||||
++iExtenderCount;
|
||||
}
|
||||
}
|
||||
|
||||
if( iExtenderCount == 0 )
|
||||
return;
|
||||
|
||||
int iAreasNetworked[MAX_MAP_AREAS];
|
||||
iAreasNetworked[0] = pViewEntity->NetworkProp()->AreaNum(); //safe assumption?
|
||||
for( int i = 1; i != MAX_MAP_AREAS; ++i )
|
||||
{
|
||||
iAreasNetworked[i] = -1;
|
||||
}
|
||||
|
||||
//unsigned char *viewEntPVS = (unsigned char *)stackalloc( sizeof( unsigned char ) * pvssize );
|
||||
//memcpy( viewEntPVS, outputPVS, sizeof( unsigned char ) * pvssize );
|
||||
|
||||
unsigned char viewEntPVS[MAX_MAP_LEAFS/8];
|
||||
engine->GetPVSForCluster( engine->GetClusterForOrigin( vVisOrigin ), sizeof( viewEntPVS ), viewEntPVS );
|
||||
//do we OR that into the output PVS?
|
||||
|
||||
//grab the local pvs of every extender up front to avoid repeating it in recursions
|
||||
//unsigned char *pExtenderPVSs = (unsigned char *)stackalloc( sizeof( unsigned char ) * (MAX_MAP_LEAFS/8) * iPortalCount );
|
||||
ExtenderInstanceData_t *pExtenderData = (ExtenderInstanceData_t *)stackalloc( sizeof( ExtenderInstanceData_t ) * iExtenderCount );
|
||||
for( int i = 0; i != iExtenderCount; ++i )
|
||||
{
|
||||
pExtenders[i]->m_pExtenderData = &pExtenderData[i];
|
||||
engine->GetPVSForCluster( engine->GetClusterForOrigin( pExtenders[i]->GetExtensionPVSOrigin() ), sizeof( unsigned char ) * (MAX_MAP_LEAFS/8), pExtenderData[i].iPVSBits );
|
||||
pExtenders[i]->m_pExtenderData->bAddedToPVSAlready = false;
|
||||
}
|
||||
|
||||
VisExtensionChain_t chainRoot;
|
||||
chainRoot.m_nArea = iAreasNetworked[0];
|
||||
chainRoot.pParentChain = NULL;
|
||||
|
||||
const edict_t *viewEdict = pViewEntity->edict();
|
||||
|
||||
for( int i = 0; i != iExtenderCount; ++i )
|
||||
{
|
||||
CPVS_Extender *pExtender = pExtenders[i];
|
||||
|
||||
if ( pExtender->GetExtenderEdict() == viewEdict )
|
||||
continue;
|
||||
|
||||
if ( pExtender->GetExtenderNetworkProp()->IsInPVS( viewEdict, viewEntPVS, pvssize ) ) //test against pViewEntity PVS, not aggregate PVS
|
||||
{
|
||||
chainRoot.pExtender = pExtender;
|
||||
pExtender->ComputeSubVisibility( pExtenders, iExtenderCount, outputPVS, pvssize, vVisOrigin, NULL, 0, &chainRoot, iAreasNetworked, iMaxRecursions );
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = 0; i != iExtenderCount; ++i )
|
||||
{
|
||||
pExtenders[i]->m_pExtenderData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//does the grunt work of checking if the data has already been added, and if not, adding it
|
||||
/*void CPVS_Extender::AddToPVS( unsigned char *outputPVS, int pvssize, int iAreasNetworked[MAX_MAP_AREAS] )
|
||||
{
|
||||
|
||||
|
||||
if( !m_pExtenderData->bAddedToPVSAlready )
|
||||
{
|
||||
bool bFound = false;
|
||||
for( int i = 0; i != MAX_MAP_AREAS; ++i )
|
||||
{
|
||||
if( iAreasNetworked[i] == iLinkedArea )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if( iAreasNetworked[i] == -1 )
|
||||
{
|
||||
bFound = true; //we found it by adding it
|
||||
iAreasNetworked[i] = iLinkedArea;
|
||||
int iOutputPVSIntSize = pvssize / sizeof( unsigned int );
|
||||
for( int j = 0; j != iOutputPVSIntSize; ++j )
|
||||
{
|
||||
((unsigned int *)outputPVS)[j] |= ((unsigned int *)pLinkedPVS)[j];
|
||||
}
|
||||
for( int j = iOutputPVSIntSize * sizeof( unsigned int ); j != pvssize; ++j )
|
||||
{
|
||||
outputPVS[j] |= pLinkedPVS[j];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !bFound )
|
||||
return;
|
||||
|
||||
AddPortalCornersToEnginePVS( pLinkedPortal );
|
||||
m_pExtenderData->bAddedToPVSAlready = true;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
52
game/server/portal/pvs_extender.h
Normal file
52
game/server/portal/pvs_extender.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef PVS_EXTENDER_H
|
||||
#define PVS_EXTENDER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CPVS_Extender
|
||||
{
|
||||
public:
|
||||
CPVS_Extender( void ); //self-registration
|
||||
virtual ~CPVS_Extender( void ); //self-unregistration
|
||||
|
||||
struct VisExtensionChain_t
|
||||
{
|
||||
VisExtensionChain_t *pParentChain;
|
||||
int m_nArea;
|
||||
CPVS_Extender *pExtender;
|
||||
};
|
||||
|
||||
virtual CServerNetworkProperty *GetExtenderNetworkProp( void )= 0;
|
||||
virtual const edict_t *GetExtenderEdict( void ) const = 0;
|
||||
virtual bool IsExtenderValid( void ) = 0;
|
||||
virtual Vector GetExtensionPVSOrigin( void ) = 0;
|
||||
|
||||
//given an entity and initial pvs. Extend that pvs through any visible portals
|
||||
static void ComputeExtendedPVS( const CBaseEntity *pViewEntity, const Vector &vVisOrigin, unsigned char *outputPVS, int pvssize, int iMaxRecursions );
|
||||
|
||||
//This extender is decidedly visible, recursively extend the visibility problem
|
||||
virtual void ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft ) = 0;
|
||||
|
||||
//cached data to make the algorithms a bit simpler in recursions
|
||||
struct ExtenderInstanceData_t
|
||||
{
|
||||
unsigned char iPVSBits[MAX_MAP_LEAFS/8];
|
||||
bool bAddedToPVSAlready; //no need to add our data to the PVS again, but should still recurse if we alter the frustum at all
|
||||
};
|
||||
|
||||
protected:
|
||||
ExtenderInstanceData_t *m_pExtenderData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //#ifndef PVS_EXTENDER_H
|
||||
Reference in New Issue
Block a user