add hl1,portal,dod source code

This commit is contained in:
nillerusr
2022-04-16 12:05:19 +03:00
parent bc6873014e
commit 23a370d9bb
524 changed files with 174173 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
//========= Copyright 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 "prop_portal.h"
#include "portal_shareddefs.h"
#include "collisionutils.h"
#include "env_debughistory.h"
LINK_ENTITY_TO_CLASS( physicsclonearea, CPhysicsCloneArea );
#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->StartCloningEntity( 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->StopCloningEntity( 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 );
SetSize( vLocalMins, vLocalMaxs );
}
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 );
}
}
SetAbsOrigin( m_pAttachedPortal->GetAbsOrigin() );
SetAbsAngles( m_pAttachedPortal->GetAbsAngles() );
m_bActive = m_pAttachedPortal->m_bActivated;
//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 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->m_bActivated )
{
touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
if( root )
{
for( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
m_pAttachedSimulator->StartCloningEntity( link->entityTouched );
}
}
}
CPhysicsCloneArea *CPhysicsCloneArea::CreatePhysicsCloneArea( CProp_Portal *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;
}
+51
View File
@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef PHYSICSCLONEAREA_H
#define PHYSICSCLONEAREA_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
class CProp_Portal;
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( CProp_Portal *pFollowPortal );
private:
CProp_Portal *m_pAttachedPortal;
CPortalSimulator *m_pAttachedSimulator;
bool m_bActive;
};
#endif //#ifndef PHYSICSCLONEAREA_H
@@ -0,0 +1,302 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "filesystem.h"
#include "portal_gamestats.h"
#include "util_shared.h"
#include "prop_portal_shared.h"
#include "utlstring.h"
#ifdef PORTAL_GAMESTATS_VERBOSE //this shouldn't be in release versions, and most loading/saving code is only enabled with this defined
static ConVar portalstats_visdeaths( "portalstats_visdeaths", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
static ConVar portalstats_visjumps( "portalstats_visjumps", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
static ConVar portalstats_visusages( "portalstats_visusages", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
static ConVar portalstats_visstucks( "portalstats_visstucks", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
static ConVar portalstats_visplacement( "portalstats_visplacement", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
class CPortalGameStatsVisualizer : public CPortalGameStats, public CAutoGameSystemPerFrame
{
public:
float m_fRefreshTimer;
void FrameUpdatePostEntityThink();
void LevelInitPreEntity();
void PrepareLevelData( void );
CUtlVector<QAngle> m_PortalPlacementAngles;
CPortalGameStatsVisualizer( void )
: m_fRefreshTimer( 0.0f )
{ };
};
CPortalGameStatsVisualizer s_GameStatsVisualizer;
void CPortalGameStatsVisualizer::LevelInitPreEntity()
{
m_fRefreshTimer = gpGlobals->curtime;
PrepareLevelData();
}
void CPortalGameStatsVisualizer::PrepareLevelData( void )
{
m_pCurrentMapStats = FindOrAddMapStats( STRING( gpGlobals->mapname ) );
m_PortalPlacementAngles.SetSize( m_pCurrentMapStats->m_pPlacements->Count() );
for( int i = m_pCurrentMapStats->m_pPlacements->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( i );
trace_t tr;
// Trace to see where the portal hit
Vector vDirection = PlacementStat.ptPlacementPosition - PlacementStat.ptPlayerFiredFrom;
UTIL_TraceLine( PlacementStat.ptPlayerFiredFrom, PlacementStat.ptPlayerFiredFrom + (vDirection * 100000.0f), MASK_SHOT_PORTAL, NULL, &tr );
Vector vUp( 0.0f, 0.0f, 1.0f );
if( ( tr.plane.normal.x > -0.001f && tr.plane.normal.x < 0.001f ) && ( tr.plane.normal.y > -0.001f && tr.plane.normal.y < 0.001f ) )
{
//plane is a level floor/ceiling
vUp = vDirection;
}
VectorAngles( tr.plane.normal, vUp, m_PortalPlacementAngles[i] );
}
}
#define PORTALSTATSVISUALIZER_REFRESHTIME 1.0f
#define PORTALSTATSVISUALIZER_DISPLAYTIME (PORTALSTATSVISUALIZER_REFRESHTIME + 0.05f)
void CPortalGameStatsVisualizer::FrameUpdatePostEntityThink()
{
if( m_fRefreshTimer > gpGlobals->curtime )
return;
//refresh now
m_fRefreshTimer = gpGlobals->curtime + PORTALSTATSVISUALIZER_REFRESHTIME;
static const Vector vPlayerBoxMins( -16.0f, -16.0f, 0.0f ), vPlayerBoxMaxs( 16.0f, 16.0f, 72.0f ), vPlayerTextOffset( 0.0f, 0.0f, 36.0f );
static const Vector vSmallBoxMins( -7.5f, -7.5f, -7.5f ), vSmallBoxMaxs( 7.5f, 7.5f, 7.5f );
if( portalstats_visdeaths.GetBool() )
{
for( int i = m_pCurrentMapStats->m_pDeaths->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::PlayerDeaths_t &DeathStat = m_pCurrentMapStats->m_pDeaths->Element( i );
NDebugOverlay::Box( DeathStat.ptPositionOfDeath, vPlayerBoxMins, vPlayerBoxMaxs, 255, 0, 0, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Text( DeathStat.ptPositionOfDeath + vPlayerTextOffset, DeathStat.szAttackerClassName, true, PORTALSTATSVISUALIZER_DISPLAYTIME );
}
}
if( portalstats_visjumps.GetBool() )
{
for( int i = m_pCurrentMapStats->m_pJumps->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::JumpEvent_t &JumpStat = m_pCurrentMapStats->m_pJumps->Element( i );
NDebugOverlay::Box( JumpStat.ptPlayerPositionAtJumpStart, vPlayerBoxMins, vPlayerBoxMaxs, 0, 255, 0, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Line( JumpStat.ptPlayerPositionAtJumpStart + vPlayerTextOffset, JumpStat.ptPlayerPositionAtJumpStart + vPlayerTextOffset + (JumpStat.vPlayerVelocityAtJumpStart), 0, 255, 0, false, PORTALSTATSVISUALIZER_REFRESHTIME );
}
}
if( portalstats_visusages.GetBool() )
{
for( int i = m_pCurrentMapStats->m_pUseEvents->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::PlayerUse_t &UseEvent = m_pCurrentMapStats->m_pUseEvents->Element( i );
NDebugOverlay::Box( UseEvent.ptTraceStart, vSmallBoxMins, vSmallBoxMaxs, 0, 0, 255, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Line( UseEvent.ptTraceStart, UseEvent.ptTraceStart + (UseEvent.vTraceDelta * 100.0f), 0, 0, 255, false, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Text( UseEvent.ptTraceStart, UseEvent.szUseEntityClassName, true, PORTALSTATSVISUALIZER_DISPLAYTIME );
}
}
if( portalstats_visstucks.GetBool() )
{
for( int i = m_pCurrentMapStats->m_pStuckSpots->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::StuckEvent_t &StuckEvent = m_pCurrentMapStats->m_pStuckSpots->Element( i );
NDebugOverlay::Box( StuckEvent.ptPlayerPosition, vPlayerBoxMins, vPlayerBoxMaxs, 255, 0, 255, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
}
}
if( portalstats_visplacement.GetBool() )
{
Assert( m_pCurrentMapStats->m_pPlacements->Count() == m_PortalPlacementAngles.Count() );
for( int i = m_pCurrentMapStats->m_pPlacements->Count(); --i >= 0; )
{
Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( i );
unsigned char brightnessval;
if( PlacementStat.iSuccessCode == 0 )
brightnessval = 255;//worked
else
brightnessval = 64;//failed
NDebugOverlay::BoxAngles( PlacementStat.ptPlacementPosition, CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs, m_PortalPlacementAngles[i], 0, brightnessval, brightnessval, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Box( PlacementStat.ptPlayerFiredFrom, vSmallBoxMins, vSmallBoxMaxs, 0, brightnessval, brightnessval, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
NDebugOverlay::Line( PlacementStat.ptPlayerFiredFrom, PlacementStat.ptPlacementPosition, 0, brightnessval, brightnessval, false, PORTALSTATSVISUALIZER_DISPLAYTIME );
}
}
}
static CUtlVector<CUtlString> s_PortalStatsDataFileList;
static void PortalStats_UpdateFileList( void )
{
s_PortalStatsDataFileList.RemoveAll();
FileFindHandle_t fileHandle;
const char *pszFileName = filesystem->FindFirst( "customstats/*.dat", &fileHandle );
while( pszFileName )
{
// Skip it if it's a directory
if( !filesystem->FindIsDirectory( fileHandle ) )
{
char szRelativeFileName[_MAX_PATH];
Q_snprintf( szRelativeFileName, sizeof( szRelativeFileName ), "customstats/%s", pszFileName );
// Only load files from the current mod's directory hierarchy
if( filesystem->FileExists( szRelativeFileName, "MOD" ) )
{
int index = s_PortalStatsDataFileList.AddToTail();
s_PortalStatsDataFileList[index].Set( pszFileName );
}
}
pszFileName = filesystem->FindNext( fileHandle );
}
filesystem->FindClose( fileHandle );
}
static int PortalStats_LoadFile_f_CompletionFunc( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
{
PortalStats_UpdateFileList();
int iRetCount = MIN( s_PortalStatsDataFileList.Count(), COMMAND_COMPLETION_MAXITEMS );
for( int i = 0; i != iRetCount; ++i )
{
Q_snprintf( commands[i], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", partial, s_PortalStatsDataFileList[i].Get() );
}
return iRetCount;
}
static char s_szCurrentlyLoadedStatsFile[256] = "";
static void PortalStats_LoadFileHelper( const char *szFileName )
{
CUtlBuffer statsfileContents;
char szRelativeName[256];
Q_snprintf( szRelativeName, sizeof( szRelativeName ), "customstats/%s", szFileName );
if( filesystem->ReadFile( szRelativeName, "MOD", statsfileContents ) )
{
Q_strncpy( s_szCurrentlyLoadedStatsFile, szFileName, sizeof( s_szCurrentlyLoadedStatsFile ) );
s_GameStatsVisualizer.Clear();
s_GameStatsVisualizer.LoadCustomDataFromBuffer( statsfileContents );
s_GameStatsVisualizer.PrepareLevelData();
s_GameStatsVisualizer.m_fRefreshTimer = 0.0f; //start drawing new data right away
}
}
static void PortalStats_LoadFile_f( const CCommand &args )
{
if( args.ArgC() > 1 )
{
PortalStats_LoadFileHelper( args[1] );
}
}
static void PortalStats_LoadNextFile_f( void )
{
PortalStats_UpdateFileList();
if( s_PortalStatsDataFileList.Count() == 0 )
return;
int i;
for( i = s_PortalStatsDataFileList.Count(); --i >= 0; )
{
if( Q_stricmp( s_PortalStatsDataFileList[i].Get(), s_szCurrentlyLoadedStatsFile ) == 0 )
break;
}
if( i < 0 )
{
//currently loaded file not found, just load the first
PortalStats_LoadFileHelper( s_PortalStatsDataFileList[0] );
}
else
{
++i;
if( i == s_PortalStatsDataFileList.Count() )
{
DevMsg( "PortalStats_LoadNextFile looping to first file in directory.\n" );
NDebugOverlay::ScreenText( 0.3f, 0.5f, "PortalStats_LoadNextFile looping to first file in directory.", 255, 255, 255, 255, 2.0f );
i = 0;
}
PortalStats_LoadFileHelper( s_PortalStatsDataFileList[i] );
}
}
static void PortalStats_LoadPrevFile_f( void )
{
PortalStats_UpdateFileList();
if( s_PortalStatsDataFileList.Count() == 0 )
return;
int i;
for( i = s_PortalStatsDataFileList.Count(); --i >= 0; )
{
if( Q_stricmp( s_PortalStatsDataFileList[i].Get(), s_szCurrentlyLoadedStatsFile ) == 0 )
break;
}
if( i < 0 )
{
//currently loaded file not found, just load the first
PortalStats_LoadFileHelper( s_PortalStatsDataFileList[0] );
}
else
{
--i;
if( i < 0 )
{
i = s_PortalStatsDataFileList.Count() - 1;
DevMsg( "PortalStats_LoadPrevFile looping to last file in directory.\n" );
NDebugOverlay::ScreenText( 0.3f, 0.5f, "PortalStats_LoadPrevFile looping to last file in directory.", 255, 255, 255, 255, 2.0f );
}
PortalStats_LoadFileHelper( s_PortalStatsDataFileList[i] );
}
}
//static ConCommand Portal_LoadCustomStatsFile("portal_loadcustomstatsfile", Portal_LoadCustomStatsFile_f, "Load a custom stats file for visualizing.", FCVAR_DONTRECORD, Portal_LoadCustomStatsFile_f_CompletionFunc );
static ConCommand PortalStats_LoadFile("portalstats_loadfile", PortalStats_LoadFile_f, "Load a custom stats file for visualizing.", FCVAR_DONTRECORD, PortalStats_LoadFile_f_CompletionFunc );
static ConCommand PortalStats_LoadNextFile("portalstats_loadnextfile", PortalStats_LoadNextFile_f, "Load the next custom stats file for visualizing.", FCVAR_DONTRECORD );
static ConCommand PortalStats_LoadPrevFile("portalstats_loadprevfile", PortalStats_LoadPrevFile_f, "Load the next custom stats file for visualizing.", FCVAR_DONTRECORD );
#endif //#ifdef PORTAL_GAMESTATS_VERBOSE
@@ -0,0 +1,83 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for simple projectiles
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "cbaseanimatingprojectile.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( baseanimating_projectile, CBaseAnimatingProjectile );
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CBaseAnimatingProjectile )
DEFINE_FIELD( m_iDmg, FIELD_INTEGER ),
DEFINE_FIELD( m_iDmgType, FIELD_INTEGER ),
END_DATADESC()
//---------------------------------------------------------
//---------------------------------------------------------
void CBaseAnimatingProjectile::Spawn( char *pszModel,
const Vector &vecOrigin,
const Vector &vecVelocity,
edict_t *pOwner,
MoveType_t iMovetype,
MoveCollide_t nMoveCollide,
int iDamage,
int iDamageType )
{
Precache();
SetSolid( SOLID_BBOX );
SetModel( pszModel );
UTIL_SetSize( this, vec3_origin, vec3_origin );
m_iDmg = iDamage;
m_iDmgType = iDamageType;
SetMoveType( iMovetype, nMoveCollide );
UTIL_SetOrigin( this, vecOrigin );
SetAbsVelocity( vecVelocity );
SetOwnerEntity( Instance( pOwner ) );
QAngle qAngles;
VectorAngles( vecVelocity, qAngles );
SetAbsAngles( qAngles );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CBaseAnimatingProjectile::Touch( CBaseEntity *pOther )
{
CBaseEntity *pOwner;
pOwner = GetOwnerEntity();
if( !pOwner )
{
pOwner = this;
}
trace_t tr;
tr = BaseClass::GetTouchTrace( );
CTakeDamageInfo info( this, pOwner, m_iDmg, m_iDmgType );
GuessDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
pOther->TakeDamage( info );
UTIL_Remove( this );
}
@@ -0,0 +1,43 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for simple projectiles
//
// $NoKeywords: $
//=============================================================================//
#ifndef CBASEANIMATINGPROJECTILE_H
#define CBASEANIMATINGPROJECTILE_H
#ifdef _WIN32
#pragma once
#endif
enum MoveType_t;
enum MoveCollide_t;
//=============================================================================
//=============================================================================
class CBaseAnimatingProjectile : public CBaseAnimating
{
DECLARE_DATADESC();
DECLARE_CLASS( CBaseAnimatingProjectile, CBaseAnimating );
public:
void Touch( CBaseEntity *pOther );
void Spawn( char *pszModel,
const Vector &vecOrigin,
const Vector &vecVelocity,
edict_t *pOwner,
MoveType_t iMovetype,
MoveCollide_t nMoveCollide,
int iDamage,
int iDamageType );
virtual void Precache( void ) {};
int m_iDmg;
int m_iDmgType;
};
#endif // CBASEANIMATINGPROJECTILE_H
@@ -0,0 +1,190 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "env_lightrail_endpoint_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( env_lightrail_endpoint, CEnv_Lightrail_Endpoint );
BEGIN_DATADESC( CEnv_Lightrail_Endpoint )
DEFINE_KEYFIELD( m_flSmallScale, FIELD_FLOAT, "small_fx_scale" ),
DEFINE_KEYFIELD( m_flLargeScale, FIELD_FLOAT, "large_fx_scale" ),
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
DEFINE_FIELD( m_flDuration, FIELD_FLOAT ),
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "StartCharge", InputStartCharge ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartSmallFX", InputStartSmallFX ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartLargeFX", InputStartLargeFX ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Stop", InputStop ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CEnv_Lightrail_Endpoint, DT_Env_Lightrail_Endpoint )
SendPropFloat( SENDINFO(m_flSmallScale), 0, SPROP_NOSCALE),
SendPropFloat( SENDINFO(m_flLargeScale), 0, SPROP_NOSCALE),
SendPropInt( SENDINFO(m_nState), 8, SPROP_UNSIGNED),
SendPropFloat( SENDINFO(m_flDuration), 0, SPROP_NOSCALE),
SendPropFloat( SENDINFO(m_flStartTime), 0, SPROP_NOSCALE),
SendPropInt( SENDINFO(m_spawnflags), 0, SPROP_UNSIGNED),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Precache:
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::Precache()
{
BaseClass::Precache();
PrecacheMaterial( "effects/light_rail_endpoint" ); //Sprite used for the effects
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::Spawn( void )
{
Precache();
UTIL_SetSize( this, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
// See if we start active
if ( HasSpawnFlags( SF_ENDPOINT_START_SMALLFX ) )
{
m_nState = (int)ENDPOINT_STATE_LARGEFX; //THIS NEEDS TO BE CHANGED TO SMALL FX STATE
m_flStartTime = gpGlobals->curtime;
}
// No model but we still need to force this!
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flWarmUpTime -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::StartCharge( float flWarmUpTime )
{
m_nState = (int)ENDPOINT_STATE_CHARGING;
m_flDuration = flWarmUpTime;
m_flStartTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::StartLargeFX( void )
{
m_nState = (int)ENDPOINT_STATE_LARGEFX;
m_flStartTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::StartSmallFX( void )
{
m_nState = (int)ENDPOINT_STATE_SMALLFX;
m_flStartTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flCoolDownTime -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::StopLargeFX( float flCoolDownTime )
{
m_nState = (int)ENDPOINT_STATE_OFF;
m_flDuration = flCoolDownTime;
m_flStartTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flCoolDownTime -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::StopSmallFX( float flCoolDownTime )
{
m_nState = (int)ENDPOINT_STATE_OFF;
m_flDuration = flCoolDownTime;
m_flStartTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::InputStartCharge( inputdata_t &inputdata )
{
StartCharge( inputdata.value.Float() );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::InputStartLargeFX( inputdata_t &inputdata )
{
StartLargeFX();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::InputStartSmallFX( inputdata_t &inputdata )
{
StartSmallFX();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CEnv_Lightrail_Endpoint::InputStop( inputdata_t &inputdata )
{
StopLargeFX( inputdata.value.Float() );
}
CBaseViewModel *IsViewModelMoveParent_( CBaseEntity *pEffect )
{
if ( pEffect->GetMoveParent() )
{
CBaseViewModel *pViewModel = dynamic_cast<CBaseViewModel *>( pEffect->GetMoveParent() );
if ( pViewModel )
{
return pViewModel;
}
}
return NULL;
}
int CEnv_Lightrail_Endpoint::UpdateTransmitState( void )
{
if ( IsViewModelMoveParent_( this ) )
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
return BaseClass::UpdateTransmitState();
}
int CEnv_Lightrail_Endpoint::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
CBaseViewModel *pViewModel = IsViewModelMoveParent_( this );
if ( pViewModel )
{
return pViewModel->ShouldTransmit( pInfo );
}
return BaseClass::ShouldTransmit( pInfo );
}
+186
View File
@@ -0,0 +1,186 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EnvMessage.h"
#include "engine/IEngineSound.h"
#include "KeyValues.h"
#include "filesystem.h"
#include "Color.h"
#include "gamestats.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CPortalCredits : public CPointEntity
{
public:
DECLARE_CLASS( CMessage, CPointEntity );
DECLARE_DATADESC();
void Spawn( void );
void InputRollCredits( inputdata_t &inputdata );
void InputRollOutroCredits( inputdata_t &inputdata );
void InputShowLogo( inputdata_t &inputdata );
void InputSetLogoLength( inputdata_t &inputdata );
void InputRollPortalOutroCredits( inputdata_t &inputdata );
COutputEvent m_OnCreditsDone;
virtual void Precache();
virtual void OnRestore();
private:
void RollOutroCredits();
void RollPortalOutroCredits();
bool m_bRolledOutroCredits;
float m_flLogoLength;
};
LINK_ENTITY_TO_CLASS( env_portal_credits, CPortalCredits );
BEGIN_DATADESC( CPortalCredits )
DEFINE_INPUTFUNC( FIELD_VOID, "RollCredits", InputRollCredits ),
DEFINE_INPUTFUNC( FIELD_VOID, "RollOutroCredits", InputRollOutroCredits ),
DEFINE_INPUTFUNC( FIELD_VOID, "RollPortalOutroCredits", InputRollPortalOutroCredits ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowLogo", InputShowLogo ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLogoLength", InputSetLogoLength ),
DEFINE_OUTPUT( m_OnCreditsDone, "OnCreditsDone"),
DEFINE_FIELD( m_bRolledOutroCredits, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flLogoLength, FIELD_FLOAT )
END_DATADESC()
void CPortalCredits::Spawn( void )
{
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
}
static void CreditsDone_f( void )
{
CPortalCredits *pCredits = (CPortalCredits*)gEntList.FindEntityByClassname( NULL, "env_credits" );
if ( pCredits )
{
pCredits->m_OnCreditsDone.FireOutput( pCredits, pCredits );
}
}
static ConCommand creditsdone("creditsdone", CreditsDone_f );
extern ConVar sv_unlockedchapters;
void CPortalCredits::Precache( void )
{
PrecacheScriptSound( "Portal.song_credits" );
BaseClass::Precache();
}
void CPortalCredits::OnRestore()
{
BaseClass::OnRestore();
if ( m_bRolledOutroCredits )
{
// Roll them again so that the client .dll will send the "creditsdone" message and we'll
// actually get back to the main menu
RollOutroCredits();
}
}
void CPortalCredits::RollOutroCredits()
{
sv_unlockedchapters.SetValue( "15" );
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "CreditsPortalMsg" );
WRITE_BYTE( 3 );
MessageEnd();
}
void CPortalCredits::InputRollOutroCredits( inputdata_t &inputdata )
{
RollOutroCredits();
// In case we save restore
m_bRolledOutroCredits = true;
gamestats->Event_Credits();
}
void CPortalCredits::RollPortalOutroCredits()
{
sv_unlockedchapters.SetValue( "15" );
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "CreditsPortalMsg" );
WRITE_BYTE( 4 );
MessageEnd();
}
void CPortalCredits::InputRollPortalOutroCredits( inputdata_t &inputdata )
{
RollPortalOutroCredits();
// In case we save restore
m_bRolledOutroCredits = true;
gamestats->Event_Credits();
}
void CPortalCredits::InputShowLogo( inputdata_t &inputdata )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
if ( m_flLogoLength )
{
UserMessageBegin( user, "LogoTimeMsg" );
WRITE_FLOAT( m_flLogoLength );
MessageEnd();
}
else
{
UserMessageBegin( user, "CreditsPortalMsg" );
WRITE_BYTE( 1 );
MessageEnd();
}
}
void CPortalCredits::InputSetLogoLength( inputdata_t &inputdata )
{
m_flLogoLength = inputdata.value.Float();
}
void CPortalCredits::InputRollCredits( inputdata_t &inputdata )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "CreditsPortalMsg" );
WRITE_BYTE( 2 );
MessageEnd();
}
@@ -0,0 +1,218 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "env_portal_path_track_shared.h"
#include "beam_shared.h"
//**********( MODULE CONSTANTS )*************
#define TRACK_FX_WIDTH_ON 4.0f
#define TRACK_FX_WIDTH_OFF 4.0f
#define TRACK_FX_BRIGHTNESS_ON 100
#define TRACK_FX_BRIGHTNESS_OFF 10
#define TRACK_FX_COLOR_ON 140,235,255
#define TRACK_FX_COLOR_OFF 235,243,243
#define TRACK_FX_SCROLL 25.6f
ConVar sv_portal_pathtrack_track_width_on ( "sv_portal_pathtrack_track_width_on", "4.0", FCVAR_CHEAT );
//**********( DATA TABLE )*******************
BEGIN_DATADESC( CEnvPortalPathTrack )
// Data members
DEFINE_FIELD( m_bTrackActive, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bEndpointActive, FIELD_BOOLEAN ),
// keyfield data
// DEFINE_KEYFIELD( m_fScaleEndpoint, FIELD_FLOAT, "End_point_scale" ),
// DEFINE_KEYFIELD( m_fScaleTrack, FIELD_FLOAT, "Track_beam_scale" ),
// DEFINE_KEYFIELD( m_fFadeOutEndpoint, FIELD_FLOAT, "End_point_fadeout"),
// DEFINE_KEYFIELD( m_fFadeInEndpoint, FIELD_FLOAT, "End_point_fadein"),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "ActivateTrackFX", InputActivateTrack ),
DEFINE_INPUTFUNC( FIELD_VOID, "ActivateEndPointFX", InputActivateEndpoint ),
DEFINE_INPUTFUNC( FIELD_VOID, "DeactivateTrackFX", InputDeactivateTrack ),
DEFINE_INPUTFUNC( FIELD_VOID, "DeactivateEndPointFX", InputDeactivateEndpoint ),
// Outputs
DEFINE_OUTPUT(m_OnActivatedEndpoint, "OnActivateFX"),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CEnvPortalPathTrack, DT_EnvPortalPathTrack )
SendPropBool( SENDINFO(m_bTrackActive) ),
SendPropBool( SENDINFO(m_bEndpointActive) ),
SendPropInt ( SENDINFO(m_nState) ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( env_portal_path_track, CEnvPortalPathTrack );
//*********( FUNCTION IMPLEMENTATIONS )***************
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
CEnvPortalPathTrack::CEnvPortalPathTrack()
{
m_bTrackActive = false;
m_bEndpointActive = false;
// m_fScaleEndpoint = 1.0f;
// m_fScaleTrack = 1.0f;
}
CEnvPortalPathTrack::~CEnvPortalPathTrack()
{
ShutDownTrackFX(); //Make sure to deallocate the track beam and particle effects
}
//-----------------------------------------------------------------------------
// Precache:
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::Precache()
{
BaseClass::Precache();
PrecacheMaterial( "effects/combinemuzzle2_dark" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::Spawn( void )
{
Precache();
BaseClass::Spawn();
UTIL_SetSize( this, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
// No model but we still need to force this!
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
}
void CEnvPortalPathTrack::Activate( void )
{
BaseClass::Activate(); //Link the happy friends so I know where my next target entity is
InitTrackFX(); //Initialize the FX for the track beam
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the track beam and it's partical effects
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InitTrackFX()
{
m_pBeam = CBeam::BeamCreate( "sprites/track_beam.vmt", TRACK_FX_WIDTH_ON ); //Allocate a mr. beamy, and set his scroll sprite and width
if ( m_pnext )
{
m_pBeam->PointEntInit( GetAbsOrigin(), m_pnext ); //Set up the beam to draw from its center to it's next track.
}
ActivateTrackFX(); //Set prettiness
}
//-----------------------------------------------------------------------------
// Purpose: Kills the track beam and it's partical effects
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::ShutDownTrackFX()
{
if ( m_pBeam )
{
UTIL_Remove( m_pBeam );
m_pBeam = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the endpoint particle effects
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InitEndpointFX()
{
}
//-----------------------------------------------------------------------------
// Purpose: Activates the visual effects on the path track between two endpoints
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InputActivateTrack(inputdata_t &inputdata)
{
ActivateTrackFX();
}
//-----------------------------------------------------------------------------
// Purpose: Activates the visual effects on the endpoint
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InputActivateEndpoint(inputdata_t &inputdata)
{
ActivateEndpointFX();
}
//-----------------------------------------------------------------------------
// Purpose: Activates the visual effects on the path track between two endpoints
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InputDeactivateTrack(inputdata_t &inputdata)
{
DeactivateTrackFX();
}
//-----------------------------------------------------------------------------
// Purpose: Activates the visual effects on the endpoint
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::InputDeactivateEndpoint(inputdata_t &inputdata)
{
DeactivateEndpointFX();
}
//-----------------------------------------------------------------------------
// Purpose: Activate all of the track's beams (at least the ones that are flagged to display)
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::ActivateTrackFX ( void )
{
m_pBeam->SetColor( TRACK_FX_COLOR_ON );
m_pBeam->SetScrollRate( (int)TRACK_FX_SCROLL );
m_pBeam->SetBrightness( TRACK_FX_BRIGHTNESS_ON );
m_pBeam->TurnOff();
m_nState = (int)PORTAL_PATH_TRACK_STATE_ACTIVE;
}
//-----------------------------------------------------------------------------
// Purpose: Activate all of the track's beams (at least the ones that are flagged to display)
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::DeactivateTrackFX ( void )
{
m_pBeam->SetColor( TRACK_FX_COLOR_OFF );
m_pBeam->SetScrollRate( (int)TRACK_FX_SCROLL );
m_pBeam->SetBrightness( TRACK_FX_BRIGHTNESS_OFF );
}
//-----------------------------------------------------------------------------
// Purpose: Activate all of the endpoint's glowy bits that are flagged to display
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::ActivateEndpointFX ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Activate all of the endpoint's glowy bits that are flagged to display
// Input : -
//-----------------------------------------------------------------------------
void CEnvPortalPathTrack::DeactivateEndpointFX ( void )
{
}
+344
View File
@@ -0,0 +1,344 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Rising liquid that acts as a one-way portal
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "func_liquidportal.h"
#include "portal_player.h"
#include "isaverestore.h"
#include "saverestore_utlvector.h"
LINK_ENTITY_TO_CLASS( func_liquidportal, CFunc_LiquidPortal );
BEGIN_DATADESC( CFunc_LiquidPortal )
DEFINE_FIELD( m_hLinkedPortal, FIELD_EHANDLE ),
DEFINE_FIELD( m_bFillInProgress, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fFillStartTime, FIELD_TIME ),
DEFINE_FIELD( m_fFillEndTime, FIELD_TIME ),
DEFINE_FIELD( m_matrixThisToLinked, FIELD_VMATRIX ),
DEFINE_UTLVECTOR( m_hTeleportList, FIELD_EHANDLE ),
DEFINE_UTLVECTOR( m_hLeftToTeleportThisFill, FIELD_EHANDLE ),
DEFINE_KEYFIELD( m_strInitialLinkedPortal, FIELD_STRING, "InitialLinkedPortal" ),
DEFINE_KEYFIELD( m_fFillTime, FIELD_FLOAT, "FillTime" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_STRING, "SetLinkedLiquidPortal", InputSetLinkedLiquidPortal ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFillTime", InputSetFillTime ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartFilling", InputStartFilling ),
DEFINE_INPUTFUNC( FIELD_VOID, "AddActivatorToTeleportList", InputAddActivatorToTeleportList ),
DEFINE_INPUTFUNC( FIELD_VOID, "RemoveActivatorFromTeleportList", InputRemoveActivatorFromTeleportList ),
DEFINE_FUNCTION( CBaseEntity::Think ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CFunc_LiquidPortal, DT_Func_LiquidPortal )
SendPropEHandle( SENDINFO(m_hLinkedPortal) ),
SendPropFloat( SENDINFO(m_fFillStartTime) ),
SendPropFloat( SENDINFO(m_fFillEndTime) ),
END_SEND_TABLE()
CFunc_LiquidPortal::CFunc_LiquidPortal( void )
: m_bFillInProgress( false )
{
m_matrixThisToLinked.Identity(); //Zero space is a bad place. No heroes to face, but we need 1's in this case.
}
void CFunc_LiquidPortal::Spawn( void )
{
BaseClass::Spawn();
SetSolid( SOLID_VPHYSICS );
SetSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_NONE );
SetModel( STRING( GetModelName() ) );
CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, STRING(m_strInitialLinkedPortal) );
Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
SetThink( &CFunc_LiquidPortal::Think );
}
void CFunc_LiquidPortal::Activate( void )
{
BaseClass::Activate();
SetSolid( SOLID_VPHYSICS );
SetSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_NONE );
SetModel( STRING( GetModelName() ) );
ComputeLinkMatrix(); //collision origin may have changed during activation
SetThink( &CFunc_LiquidPortal::Think );
for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
{
CBaseEntity *pEnt = m_hLeftToTeleportThisFill[i].Get();
if( pEnt && pEnt->IsPlayer() )
{
((CPortal_Player *)pEnt)->m_hSurroundingLiquidPortal = this;
}
}
}
int CFunc_LiquidPortal::Save( ISave &save )
{
if( !BaseClass::Save( save ) )
return 0;
save.StartBlock( "LiquidPortal" );
short iTeleportListCount = m_hTeleportList.Count();
save.WriteShort( &iTeleportListCount );
if( iTeleportListCount != 0 )
save.WriteEHandle( m_hTeleportList.Base(), iTeleportListCount );
short iLeftToTeleportThisFillCount = m_hLeftToTeleportThisFill.Count();
save.WriteShort( &iLeftToTeleportThisFillCount );
if( iLeftToTeleportThisFillCount != 0 )
save.WriteEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
save.EndBlock();
return 1;
}
int CFunc_LiquidPortal::Restore( IRestore &restore )
{
m_hTeleportList.RemoveAll();
m_hLeftToTeleportThisFill.RemoveAll();
if( !BaseClass::Restore( restore ) )
return 0;
char szBlockName[SIZE_BLOCK_NAME_BUF];
restore.StartBlock( szBlockName );
if( !FStrEq( szBlockName, "LiquidPortal" ) ) //loading a save without liquid portal save data
return 1;
short iTeleportListCount;
restore.ReadShort( &iTeleportListCount );
if( iTeleportListCount != 0 )
{
m_hTeleportList.SetCount( iTeleportListCount );
restore.ReadEHandle( m_hTeleportList.Base(), iTeleportListCount );
}
short iLeftToTeleportThisFillCount;
restore.ReadShort( &iLeftToTeleportThisFillCount );
if( iLeftToTeleportThisFillCount != 0 )
{
m_hLeftToTeleportThisFill.SetCount( iLeftToTeleportThisFillCount );
restore.ReadEHandle( m_hLeftToTeleportThisFill.Base(), iLeftToTeleportThisFillCount );
}
restore.EndBlock();
return 1;
}
void CFunc_LiquidPortal::InputSetLinkedLiquidPortal( inputdata_t &inputdata )
{
CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, inputdata.value.String() );
Assert( (pBaseEnt == NULL) || (dynamic_cast<CFunc_LiquidPortal *>(pBaseEnt) != NULL) );
SetLinkedLiquidPortal( (CFunc_LiquidPortal *)pBaseEnt );
}
void CFunc_LiquidPortal::InputSetFillTime( inputdata_t &inputdata )
{
m_fFillTime = inputdata.value.Float();
}
void CFunc_LiquidPortal::InputStartFilling( inputdata_t &inputdata )
{
AssertMsg( m_fFillEndTime <= gpGlobals->curtime, "Fill already in progress." );
m_fFillStartTime = gpGlobals->curtime;
m_fFillEndTime = gpGlobals->curtime + m_fFillTime;
m_bFillInProgress = true;
//reset the teleport list for this fill
m_hLeftToTeleportThisFill.RemoveAll();
m_hLeftToTeleportThisFill.AddVectorToTail( m_hTeleportList );
SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
}
void CFunc_LiquidPortal::InputAddActivatorToTeleportList( inputdata_t &inputdata )
{
if( inputdata.pActivator == NULL )
return;
for( int i = m_hTeleportList.Count(); --i >= 0; )
{
if( m_hTeleportList[i].Get() == inputdata.pActivator )
return; //only have 1 reference of each entity
}
m_hTeleportList.AddToTail( inputdata.pActivator );
if( m_bFillInProgress )
m_hLeftToTeleportThisFill.AddToTail( inputdata.pActivator );
if( inputdata.pActivator->IsPlayer() )
((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = this;
}
void CFunc_LiquidPortal::InputRemoveActivatorFromTeleportList( inputdata_t &inputdata )
{
if( inputdata.pActivator == NULL )
return;
for( int i = m_hTeleportList.Count(); --i >= 0; )
{
if( m_hTeleportList[i].Get() == inputdata.pActivator )
{
m_hTeleportList.FastRemove( i );
if( inputdata.pActivator->IsPlayer() && (((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal.Get() == this) )
((CPortal_Player *)inputdata.pActivator)->m_hSurroundingLiquidPortal = NULL;
if( m_bFillInProgress )
{
//remove from the list for this fill as well
for( int j = m_hLeftToTeleportThisFill.Count(); --j >= 0; )
{
if( m_hLeftToTeleportThisFill[j].Get() == inputdata.pActivator )
{
m_hLeftToTeleportThisFill.FastRemove( j );
break;
}
}
}
return;
}
}
}
void CFunc_LiquidPortal::SetLinkedLiquidPortal( CFunc_LiquidPortal *pLinked )
{
CFunc_LiquidPortal *pCurrentLinkedPortal = m_hLinkedPortal.Get();
if( pCurrentLinkedPortal == pLinked )
return;
if( pCurrentLinkedPortal != NULL )
{
m_hLinkedPortal = NULL;
pCurrentLinkedPortal->SetLinkedLiquidPortal( NULL );
}
m_hLinkedPortal = pLinked;
if( pLinked != NULL )
pLinked->SetLinkedLiquidPortal( this );
ComputeLinkMatrix();
}
void CFunc_LiquidPortal::ComputeLinkMatrix( void )
{
CFunc_LiquidPortal *pLinkedPortal = m_hLinkedPortal.Get();
if( pLinkedPortal )
{
VMatrix matLocalToWorld, matLocalToWorldInv, matRemoteToWorld;
matLocalToWorld = EntityToWorldTransform();
matRemoteToWorld = pLinkedPortal->EntityToWorldTransform();
MatrixInverseTR( matLocalToWorld, matLocalToWorldInv );
m_matrixThisToLinked = matRemoteToWorld * matLocalToWorldInv;
MatrixInverseTR( m_matrixThisToLinked, pLinkedPortal->m_matrixThisToLinked );
}
else
{
m_matrixThisToLinked.Identity();
}
}
void CFunc_LiquidPortal::TeleportImmersedEntity( CBaseEntity *pEntity )
{
if( pEntity == NULL )
return;
if( pEntity->IsPlayer() )
{
CPortal_Player *pEntityAsPlayer = (CPortal_Player *)pEntity;
Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
QAngle qNewAngles = TransformAnglesToWorldSpace( pEntityAsPlayer->EyeAngles(), m_matrixThisToLinked.As3x4() );
Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
pEntityAsPlayer->m_hSurroundingLiquidPortal = m_hLinkedPortal;
}
else
{
Vector vNewOrigin = m_matrixThisToLinked * pEntity->GetAbsOrigin();
QAngle qNewAngles = TransformAnglesToWorldSpace( pEntity->GetAbsAngles(), m_matrixThisToLinked.As3x4() );
Vector vNewVelocity = m_matrixThisToLinked.ApplyRotation( pEntity->GetAbsVelocity() );
pEntity->Teleport( &vNewOrigin, &qNewAngles, &vNewVelocity );
}
}
void CFunc_LiquidPortal::Think( void )
{
if( m_bFillInProgress )
{
if( gpGlobals->curtime < m_fFillEndTime )
{
float fInterp = ((gpGlobals->curtime - m_fFillStartTime) / (m_fFillEndTime - m_fFillStartTime));
Vector vMins, vMaxs;
GetCollideable()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
vMaxs.z = vMins.z + ((vMaxs.z - vMins.z) * fInterp);
for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
{
CBaseEntity *pEntity = m_hLeftToTeleportThisFill[i].Get();
if( pEntity == NULL )
continue;
Vector vEntMins, vEntMaxs;
pEntity->GetCollideable()->WorldSpaceSurroundingBounds( &vEntMins, &vEntMaxs );
if( vEntMaxs.z <= vMaxs.z )
{
TeleportImmersedEntity( pEntity );
m_hLeftToTeleportThisFill.FastRemove( i );
}
}
SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
}
else
{
//teleport everything that's left in the list
for( int i = m_hLeftToTeleportThisFill.Count(); --i >= 0; )
{
TeleportImmersedEntity( m_hLeftToTeleportThisFill[i].Get() );
}
m_hLeftToTeleportThisFill.RemoveAll();
m_bFillInProgress = false;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Rising liquid that acts as a one-way portal
//
// $NoKeywords: $
//===========================================================================//
#ifndef FUNC_LIQUIDPORTAL_H
#define FUNC_LIQUIDPORTAL_H
#ifdef _WIN32
#pragma once
#endif
#include "triggers.h"
class CFunc_LiquidPortal : public CBaseEntity
{
public:
DECLARE_CLASS( CFunc_LiquidPortal, CBaseEntity );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
CFunc_LiquidPortal( void );
virtual void Spawn( void );
virtual void Activate( void );
virtual void Think( void );
virtual int UpdateTransmitState( void ) // set transmit filter to transmit always
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
virtual int Save( ISave &save );
virtual int Restore( IRestore &restore );
void InputSetLinkedLiquidPortal( inputdata_t &inputdata );
void InputSetFillTime( inputdata_t &inputdata ); //time it takes to fill the portal volume
void InputStartFilling( inputdata_t &inputdata ); //start filling with portal liquid, will teleport entities as they become completely enveloped
//add/remove teleportables to offload the selection process to triggers, each with their own filters
void InputAddActivatorToTeleportList( inputdata_t &inputdata ); //add an activator entity to the list of entities to teleport when filling
void InputRemoveActivatorFromTeleportList( inputdata_t &inputdata ); //remove an activator entity from the list of entities to teleport when filling
void ComputeLinkMatrix( void );
void SetLinkedLiquidPortal( CFunc_LiquidPortal *pLinked );
void TeleportImmersedEntity( CBaseEntity *pEntity );
CNetworkHandle( CFunc_LiquidPortal, 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
float m_fFillTime; //how long it takes to fill completely
bool m_bFillInProgress;
CNetworkVar( float, m_fFillStartTime ); // time started filling with portal liquid, will teleport entities as they become completely enveloped
CNetworkVar( float, m_fFillEndTime ); // time that filling should be finished and touching entities teleport
CUtlVector<EHANDLE> m_hTeleportList; //list of entities to teleport when filling
CUtlVector<EHANDLE> m_hLeftToTeleportThisFill; //list of entities that have not yet teleported during this fill, they get teleported out when fully immersed in the liquid
string_t m_strInitialLinkedPortal;
};
#endif //#ifndef FUNC_LIQUIDPORTAL_H
+130
View File
@@ -0,0 +1,130 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume in which no portal can be placed. Keeps a global list loaded in from the map
// and provides an interface with which prop_portal can get this list and avoid successfully
// creating portals wholly or partially inside the volume.
//
// $NoKeywords: $
//======================================================================================//
#include "cbase.h"
#include "func_noportal_volume.h"
#include "prop_portal_shared.h"
#include "portal_shareddefs.h"
#include "portal_util_shared.h"
#include "collisionutils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Spawnflags
#define SF_START_INACTIVE 0x01
CEntityClassList<CFuncNoPortalVolume> g_FuncNoPortalVolumeList;
template <> CFuncNoPortalVolume *CEntityClassList<CFuncNoPortalVolume>::m_pClassList = NULL;
CFuncNoPortalVolume* GetNoPortalVolumeList()
{
return g_FuncNoPortalVolumeList.m_pClassList;
}
LINK_ENTITY_TO_CLASS( func_noportal_volume, CFuncNoPortalVolume );
BEGIN_DATADESC( CFuncNoPortalVolume )
DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iListIndex, FIELD_INTEGER ),
// No need to save this, its rebuilt on construct
//DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
DEFINE_FUNCTION( GetIndex ),
DEFINE_FUNCTION( IsActive ),
END_DATADESC()
CFuncNoPortalVolume::CFuncNoPortalVolume()
{
m_bActive = true;
// Add me to the global list
g_FuncNoPortalVolumeList.Insert( this );
}
CFuncNoPortalVolume::~CFuncNoPortalVolume()
{
g_FuncNoPortalVolumeList.Remove( this );
}
void CFuncNoPortalVolume::Spawn()
{
BaseClass::Spawn();
if ( m_spawnflags & SF_START_INACTIVE )
{
m_bActive = false;
}
else
{
m_bActive = true;
}
// Bind to our model, cause we need the extents for bounds checking
SetModel( STRING( GetModelName() ) );
SetRenderMode( kRenderNone ); // Don't draw
SetSolid( SOLID_VPHYSICS ); // we may want slanted walls, so we'll use OBB
AddSolidFlags( FSOLID_NOT_SOLID );
}
void CFuncNoPortalVolume::OnActivate( void )
{
if ( !GetCollideable() )
return;
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
if( iPortalCount != 0 )
{
CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
for( int i = 0; i != iPortalCount; ++i )
{
CProp_Portal *pTempPortal = pPortals[i];
if( pTempPortal->m_bActivated &&
IsOBBIntersectingOBB( pTempPortal->GetAbsOrigin(), pTempPortal->GetAbsAngles(), CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs,
GetAbsOrigin(), GetCollideable()->GetCollisionAngles(), GetCollideable()->OBBMins(), GetCollideable()->OBBMaxs() ) )
{
pTempPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
pTempPortal->Fizzle();
}
}
}
}
void CFuncNoPortalVolume::InputActivate( inputdata_t &inputdata )
{
m_bActive = true;
OnActivate();
}
void CFuncNoPortalVolume::InputDeactivate( inputdata_t &inputdata )
{
m_bActive = false;
}
void CFuncNoPortalVolume::InputToggle( inputdata_t &inputdata )
{
m_bActive = !m_bActive;
if ( m_bActive )
{
OnActivate();
}
}
+56
View File
@@ -0,0 +1,56 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume in which no portal can be placed. Keeps a global list loaded in from the map
// and provides an interface with which prop_portal can get this list and avoid successfully
// creating portals wholly or partially inside the volume.
//
// $NoKeywords: $
//======================================================================================//
#ifndef _FUNC_NOPORTAL_VOLUME_H_
#define _FUNC_NOPORTAL_VOLUME_H_
#ifdef _WIN32
#pragma once
#endif
#include "cbase.h"
class CFuncNoPortalVolume : public CBaseEntity
{
public:
DECLARE_CLASS( CFuncNoPortalVolume, CBaseEntity );
CFuncNoPortalVolume();
~CFuncNoPortalVolume();
// Overloads from base entity
virtual void Spawn( void );
void OnActivate( void );
// Inputs to flip functionality on and off
void InputActivate( inputdata_t &inputdata );
void InputDeactivate( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
// misc public methods
unsigned int GetIndex () { return m_iListIndex; } // returns the list index of this camera
bool IsActive() { return m_bActive; } // is this area currently blocking portals
CFuncNoPortalVolume *m_pNext; // Needed for the template list
DECLARE_DATADESC();
private:
bool m_bActive; // are we currently blocking portals
unsigned int m_iListIndex; // what is my index into the global noportal_volume list
};
// Global interface for getting the list of noportal_volumes
CFuncNoPortalVolume* GetNoPortalVolumeList();
#endif
+103
View File
@@ -0,0 +1,103 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume which bumps portal placement. Keeps a global list loaded in from the map
// and provides an interface with which prop_portal can get this list and avoid successfully
// creating portals partially inside the volume.
//
// $NoKeywords: $
//======================================================================================//
#include "cbase.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Spawnflags
#define SF_START_INACTIVE 0x01
class CFuncPortalBumper : public CBaseEntity
{
public:
DECLARE_CLASS( CFuncPortalBumper, CBaseEntity );
CFuncPortalBumper();
// Overloads from base entity
virtual void Spawn( void );
// Inputs to flip functionality on and off
void InputActivate( inputdata_t &inputdata );
void InputDeactivate( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
// misc public methods
bool IsActive() { return m_bActive; } // is this area currently bumping portals
DECLARE_DATADESC();
private:
bool m_bActive; // are we currently blocking portals
};
LINK_ENTITY_TO_CLASS( func_portal_bumper, CFuncPortalBumper );
BEGIN_DATADESC( CFuncPortalBumper )
DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
DEFINE_FUNCTION( IsActive ),
END_DATADESC()
CFuncPortalBumper::CFuncPortalBumper()
{
m_bActive = true;
}
void CFuncPortalBumper::Spawn()
{
BaseClass::Spawn();
if ( m_spawnflags & SF_START_INACTIVE )
{
m_bActive = false;
}
else
{
m_bActive = true;
}
// Bind to our model, cause we need the extents for bounds checking
SetModel( STRING( GetModelName() ) );
SetRenderMode( kRenderNone ); // Don't draw
SetSolid( SOLID_VPHYSICS ); // we may want slanted walls, so we'll use OBB
AddSolidFlags( FSOLID_NOT_SOLID );
}
void CFuncPortalBumper::InputActivate( inputdata_t &inputdata )
{
m_bActive = true;
}
void CFuncPortalBumper::InputDeactivate( inputdata_t &inputdata )
{
m_bActive = false;
}
void CFuncPortalBumper::InputToggle( inputdata_t &inputdata )
{
m_bActive = !m_bActive;
}
+139
View File
@@ -0,0 +1,139 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume in which no portal can be placed. Keeps a global list loaded in from the map
// and provides an interface with which prop_portal can get this list and avoid successfully
// creating portals wholly or partially inside the volume.
//
// $NoKeywords: $
//======================================================================================//
#include "cbase.h"
#include "func_portal_detector.h"
#include "prop_portal_shared.h"
#include "portal_shareddefs.h"
#include "portal_util_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Spawnflags
#define SF_START_INACTIVE 0x01
LINK_ENTITY_TO_CLASS( func_portal_detector, CFuncPortalDetector );
BEGIN_DATADESC( CFuncPortalDetector )
DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
DEFINE_KEYFIELD( m_iLinkageGroupID, FIELD_INTEGER, "LinkageGroupID" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
DEFINE_OUTPUT( m_OnStartTouchPortal1, "OnStartTouchPortal1" ),
DEFINE_OUTPUT( m_OnStartTouchPortal2, "OnStartTouchPortal2" ),
DEFINE_OUTPUT( m_OnStartTouchLinkedPortal, "OnStartTouchLinkedPortal" ),
DEFINE_OUTPUT( m_OnStartTouchBothLinkedPortals, "OnStartTouchBothLinkedPortals" ),
DEFINE_FUNCTION( IsActive ),
END_DATADESC()
void CFuncPortalDetector::Spawn()
{
BaseClass::Spawn();
if ( m_spawnflags & SF_START_INACTIVE )
{
m_bActive = false;
}
else
{
m_bActive = true;
}
// Bind to our model, cause we need the extents for bounds checking
SetModel( STRING( GetModelName() ) );
SetRenderMode( kRenderNone ); // Don't draw
SetSolid( SOLID_VPHYSICS ); // we may want slanted walls, so we'll use OBB
AddSolidFlags( FSOLID_NOT_SOLID );
}
void CFuncPortalDetector::OnActivate( void )
{
Vector vMin, vMax;
CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
Vector vBoxCenter = ( vMin + vMax ) * 0.5f;
Vector vBoxExtents = ( vMax - vMin ) * 0.5f;
bool bTouchedPortal1 = false;
bool bTouchedPortal2 = false;
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
if( iPortalCount != 0 )
{
CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
for( int i = 0; i != iPortalCount; ++i )
{
CProp_Portal *pTempPortal = pPortals[i];
//require that it's active and/or linked?
if( pTempPortal->GetLinkageGroup() == m_iLinkageGroupID && UTIL_IsBoxIntersectingPortal( vBoxCenter, vBoxExtents, pTempPortal ) )
{
if( pTempPortal->IsPortal2() )
{
m_OnStartTouchPortal2.FireOutput( pTempPortal, this );
if ( pTempPortal->IsActivedAndLinked() )
{
bTouchedPortal2 = true;
m_OnStartTouchLinkedPortal.FireOutput( pTempPortal, this );
}
}
else
{
m_OnStartTouchPortal1.FireOutput( pTempPortal, this );
if ( pTempPortal->IsActivedAndLinked() )
{
bTouchedPortal1 = true;
m_OnStartTouchLinkedPortal.FireOutput( pTempPortal, this );
}
}
}
}
}
if ( bTouchedPortal1 && bTouchedPortal2 )
{
m_OnStartTouchBothLinkedPortals.FireOutput( this, this );
}
}
void CFuncPortalDetector::InputDisable( inputdata_t &inputdata )
{
m_bActive = false;
}
void CFuncPortalDetector::InputEnable( inputdata_t &inputdata )
{
m_bActive = true;
OnActivate();
}
void CFuncPortalDetector::InputToggle( inputdata_t &inputdata )
{
m_bActive = !m_bActive;
if ( m_bActive )
{
OnActivate();
}
}
+49
View File
@@ -0,0 +1,49 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume which fires an output when a portal is placed in it.
//
// $NoKeywords: $
//======================================================================================//
#ifndef _FUNC_PORTAL_DETECTOR_H_
#define _FUNC_PORTAL_DETECTOR_H_
#ifdef _WIN32
#pragma once
#endif
#include "cbase.h"
class CFuncPortalDetector : public CBaseEntity
{
public:
DECLARE_CLASS( CFuncPortalDetector, CBaseEntity );
// Overloads from base entity
virtual void Spawn( void );
void OnActivate( void );
// Inputs to flip functionality on and off
void InputDisable( inputdata_t &inputdata );
void InputEnable( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
// misc public methods
bool IsActive( void ) { return m_bActive; } // is this area currently detecting portals
int GetLinkageGroupID( void ) { return m_iLinkageGroupID; }
COutputEvent m_OnStartTouchPortal1;
COutputEvent m_OnStartTouchPortal2;
COutputEvent m_OnStartTouchLinkedPortal;
COutputEvent m_OnStartTouchBothLinkedPortals;
DECLARE_DATADESC();
private:
bool m_bActive; // are we currently detecting portals
int m_iLinkageGroupID; // what set of portals are we testing for?
};
#endif
@@ -0,0 +1,167 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//======================================================================================//
#include "cbase.h"
#include "func_portal_orientation.h"
#include "prop_portal_shared.h"
#include "portal_shareddefs.h"
#include "portal_util_shared.h"
#include "portal_placement.h"
#include "collisionutils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CEntityClassList<CFuncPortalOrientation> g_FuncPortalOrientationVolumeList;
template <> CFuncPortalOrientation *CEntityClassList<CFuncPortalOrientation>::m_pClassList = NULL;
CFuncPortalOrientation* GetPortalOrientationVolumeList()
{
return g_FuncPortalOrientationVolumeList.m_pClassList;
}
//-----------------------------------------------------------------------------
// Purpose: Test for func_orientation_volume ents which could effect the placement angles of a portal.
// Input : vecCurAngles - Default angles to place (may change)
// vecCurOrigin - origin of the portal on placement
// pPortal - The portal attempting to place
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool UTIL_TestForOrientationVolumes( QAngle& vecCurAngles, const Vector& vecCurOrigin, const CProp_Portal* pPortal )
{
if ( !pPortal )
return false;
// Walk list of orientation volumes, obb test each with candidate portal
CFuncPortalOrientation *pList = g_FuncPortalOrientationVolumeList.m_pClassList;
while ( pList )
{
if ( !pList->IsActive() )
{
pList = pList->m_pNext;
continue;
}
if ( IsOBBIntersectingOBB( vecCurOrigin, vecCurAngles, CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs,
pList->GetAbsOrigin(), pList->GetCollideable()->GetCollisionAngles(), pList->GetCollideable()->OBBMins(), pList->GetCollideable()->OBBMaxs() ) )
{
QAngle vecGoalAngles;
// Ent is marked to match angles of it's linked partner
if ( pList->m_bMatchLinkedAngles )
{
// This feature requires a linked portal on a floor or ceiling. Bail without effecting
// the placement angles if we fail those requirements.
CProp_Portal* pLinked = pPortal->m_hLinkedPortal.Get();
if ( !pLinked || !(AnglesAreEqual( vecCurAngles.x, -90.0f, 0.1f ) || AnglesAreEqual( vecCurAngles.x, 90.0f, 0.1f )) )
return false;
vecGoalAngles = pLinked->GetAbsAngles();
vecCurAngles.y = 0.0f;
vecCurAngles.z = vecGoalAngles.z;
}
// Match the angles loaded in from the map
else
{
vecGoalAngles = pList->m_vecAnglesToFace;
vecCurAngles = vecGoalAngles;
}
return true;
}
pList = pList->m_pNext;
}
return false;
}
LINK_ENTITY_TO_CLASS( func_portal_orientation, CFuncPortalOrientation );
BEGIN_DATADESC( CFuncPortalOrientation )
DEFINE_FIELD( m_iListIndex, FIELD_INTEGER ),
//DEFINE_FIELD ( m_pNext, CFuncPortalOrientation ),
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_KEYFIELD ( m_bMatchLinkedAngles, FIELD_BOOLEAN, "MatchLinkedAngles" ),
DEFINE_KEYFIELD ( m_vecAnglesToFace, FIELD_VECTOR, "AnglesToFace" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
END_DATADESC()
CFuncPortalOrientation::CFuncPortalOrientation()
{
g_FuncPortalOrientationVolumeList.Insert( this );
}
CFuncPortalOrientation::~CFuncPortalOrientation()
{
g_FuncPortalOrientationVolumeList.Remove( this );
}
void CFuncPortalOrientation::Spawn()
{
BaseClass::Spawn();
// Bind to our model, cause we need the extents for bounds checking
SetModel( STRING( GetModelName() ) );
SetRenderMode( kRenderNone ); // Don't draw
SetSolid( SOLID_VPHYSICS ); // we may want slanted walls, so we'll use OBB
AddSolidFlags( FSOLID_NOT_SOLID );
}
//-----------------------------------------------------------------------------
// Purpose: Test for portals inside our volume when we switch on, and forcibly rotate them
//-----------------------------------------------------------------------------
void CFuncPortalOrientation::OnActivate( void )
{
if ( !GetCollideable() || m_bDisabled )
return;
int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
if( iPortalCount != 0 )
{
CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
for( int i = 0; i != iPortalCount; ++i )
{
CProp_Portal *pTempPortal = pPortals[i];
if( IsOBBIntersectingOBB( pTempPortal->GetAbsOrigin(), pTempPortal->GetAbsAngles(), CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs,
GetAbsOrigin(), GetCollideable()->GetCollisionAngles(), GetCollideable()->OBBMins(), GetCollideable()->OBBMaxs() ) )
{
QAngle angNewAngles;
if ( m_bMatchLinkedAngles )
{
CProp_Portal* pLinked = pTempPortal->m_hLinkedPortal.Get();
if ( !pLinked )
return;
angNewAngles = pTempPortal->m_hLinkedPortal->GetAbsAngles();
}
else
{
angNewAngles = m_vecAnglesToFace;
}
pTempPortal->PlacePortal( pTempPortal->GetAbsOrigin(), angNewAngles, PORTAL_ANALOG_SUCCESS_NO_BUMP );
}
}
}
}
void CFuncPortalOrientation::InputEnable( inputdata_t &inputdata )
{
m_bDisabled = false;
OnActivate();
}
void CFuncPortalOrientation::InputDisable( inputdata_t &inputdata )
{
m_bDisabled = true;
}
@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Volume entity which overrides the placement angles of a portal placed within its bounds.
//
// $NoKeywords: $
//======================================================================================//
#ifndef _FUNC_PORTAL_ORIENTATION_H_
#define _FUNC_PORTAL_ORIENTATION_H_
#ifdef _WIN32
#pragma once
#endif
#include "cbase.h"
class CFuncPortalOrientation : public CBaseEntity
{
public:
DECLARE_CLASS( CFuncPortalOrientation, CBaseEntity );
DECLARE_DATADESC();
CFuncPortalOrientation();
~CFuncPortalOrientation();
// Overloads from base entity
virtual void Spawn( void );
void OnActivate ( void );
// Inputs to flip functionality on and off
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
bool IsActive() { return !m_bDisabled; } // is this area causing portals to lock orientation
bool m_bMatchLinkedAngles;
QAngle m_vecAnglesToFace;
CFuncPortalOrientation *m_pNext; // Needed for the template list
unsigned int m_iListIndex;
private:
bool m_bDisabled; // are we currently locking portal orientations
};
CFuncPortalOrientation* GetPortalOrientationVolumeList();
// Upon portal placement, test for orientation changing volumes
bool UTIL_TestForOrientationVolumes( QAngle& vecCurAngles, const Vector& vecCurOrigin, const CProp_Portal* pPortal );
#endif //_FUNC_PORTAL_ORIENTATION_H_
+306
View File
@@ -0,0 +1,306 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the big scary boom-boom machine Antlions fear.
//
//=============================================================================//
#include "cbase.h"
#include "EnvMessage.h"
#include "fmtstr.h"
#include "vguiscreen.h"
#include "filesystem.h"
struct SlideKeywordList_t
{
char szSlideKeyword[64];
};
class CNeurotoxinCountdown : public CBaseEntity
{
public:
DECLARE_CLASS( CNeurotoxinCountdown, CBaseEntity );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual ~CNeurotoxinCountdown();
virtual bool KeyValue( const char *szKeyName, const char *szValue );
virtual int UpdateTransmitState();
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
virtual void Spawn( void );
virtual void Precache( void );
virtual void OnRestore( void );
void ScreenVisible( bool bVisible );
void Disable( void );
void Enable( void );
void InputDisable( inputdata_t &inputdata );
void InputEnable( inputdata_t &inputdata );
private:
// Control panel
void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName );
void SpawnControlPanels( void );
void RestoreControlPanels( void );
private:
CNetworkVar( bool, m_bEnabled );
int m_iScreenWidth;
int m_iScreenHeight;
typedef CHandle<CVGuiScreen> ScreenHandle_t;
CUtlVector<ScreenHandle_t> m_hScreens;
};
LINK_ENTITY_TO_CLASS( vgui_neurotoxin_countdown, CNeurotoxinCountdown );
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CNeurotoxinCountdown )
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
DEFINE_KEYFIELD( m_iScreenWidth, FIELD_INTEGER, "width" ),
DEFINE_KEYFIELD( m_iScreenHeight, FIELD_INTEGER, "height" ),
//DEFINE_UTLVECTOR( m_hScreens, FIELD_EHANDLE ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CNeurotoxinCountdown, DT_NeurotoxinCountdown )
SendPropBool( SENDINFO(m_bEnabled) ),
END_SEND_TABLE()
CNeurotoxinCountdown::~CNeurotoxinCountdown()
{
int i;
// Kill the control panels
for ( i = m_hScreens.Count(); --i >= 0; )
{
DestroyVGuiScreen( m_hScreens[i].Get() );
}
m_hScreens.RemoveAll();
}
//-----------------------------------------------------------------------------
// Read in worldcraft data...
//-----------------------------------------------------------------------------
bool CNeurotoxinCountdown::KeyValue( const char *szKeyName, const char *szValue )
{
//!! temp hack, until worldcraft is fixed
// strip the # tokens from (duplicate) key names
char *s = (char *)strchr( szKeyName, '#' );
if ( s )
{
*s = '\0';
}
// NOTE: Have to do these separate because they set two values instead of one
if( FStrEq( szKeyName, "angles" ) )
{
Assert( GetMoveParent() == NULL );
QAngle angles;
UTIL_StringToVector( angles.Base(), szValue );
// Because the vgui screen basis is strange (z is front, y is up, x is right)
// we need to rotate the typical basis before applying it
VMatrix mat, rotation, tmp;
MatrixFromAngles( angles, mat );
MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 );
MatrixMultiply( mat, rotation, tmp );
MatrixBuildRotateZ( rotation, 90 );
MatrixMultiply( tmp, rotation, mat );
MatrixToAngles( mat, angles );
SetAbsAngles( angles );
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
int CNeurotoxinCountdown::UpdateTransmitState()
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
void CNeurotoxinCountdown::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
{
// Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
return;
BaseClass::SetTransmit( pInfo, bAlways );
// Force our screens to be sent too.
for ( int i=0; i < m_hScreens.Count(); i++ )
{
CVGuiScreen *pScreen = m_hScreens[i].Get();
pScreen->SetTransmit( pInfo, bAlways );
}
}
void CNeurotoxinCountdown::Spawn( void )
{
Precache();
BaseClass::Spawn();
m_bEnabled = false;
SpawnControlPanels();
ScreenVisible( m_bEnabled );
}
void CNeurotoxinCountdown::Precache( void )
{
BaseClass::Precache();
PrecacheVGuiScreen( "neurotoxin_countdown_screen" );
}
void CNeurotoxinCountdown::OnRestore( void )
{
BaseClass::OnRestore();
RestoreControlPanels();
ScreenVisible( m_bEnabled );
}
void CNeurotoxinCountdown::ScreenVisible( bool bVisible )
{
for ( int iScreen = 0; iScreen < m_hScreens.Count(); ++iScreen )
{
CVGuiScreen *pScreen = m_hScreens[ iScreen ].Get();
if ( bVisible )
pScreen->RemoveEffects( EF_NODRAW );
else
pScreen->AddEffects( EF_NODRAW );
}
}
void CNeurotoxinCountdown::Disable( void )
{
if ( !m_bEnabled )
return;
m_bEnabled = false;
ScreenVisible( false );
}
void CNeurotoxinCountdown::Enable( void )
{
if ( m_bEnabled )
return;
m_bEnabled = true;
ScreenVisible( true );
}
void CNeurotoxinCountdown::InputDisable( inputdata_t &inputdata )
{
Disable();
}
void CNeurotoxinCountdown::InputEnable( inputdata_t &inputdata )
{
Enable();
}
void CNeurotoxinCountdown::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
pPanelName = "neurotoxin_countdown_screen";
}
void CNeurotoxinCountdown::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
{
pPanelName = "vgui_screen";
}
//-----------------------------------------------------------------------------
// This is called by the base object when it's time to spawn the control panels
//-----------------------------------------------------------------------------
void CNeurotoxinCountdown::SpawnControlPanels()
{
int nPanel;
for ( nPanel = 0; true; ++nPanel )
{
const char *pScreenName;
GetControlPanelInfo( nPanel, pScreenName );
if (!pScreenName)
continue;
const char *pScreenClassname;
GetControlPanelClassName( nPanel, pScreenClassname );
if ( !pScreenClassname )
continue;
float flWidth = m_iScreenWidth;
float flHeight = m_iScreenHeight;
CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, -1 );
pScreen->ChangeTeam( GetTeamNumber() );
pScreen->SetActualSize( flWidth, flHeight );
pScreen->SetActive( true );
pScreen->MakeVisibleOnlyToTeammates( false );
pScreen->SetTransparency( true );
int nScreen = m_hScreens.AddToTail( );
m_hScreens[nScreen].Set( pScreen );
return;
}
}
void CNeurotoxinCountdown::RestoreControlPanels( void )
{
int nPanel;
for ( nPanel = 0; true; ++nPanel )
{
const char *pScreenName;
GetControlPanelInfo( nPanel, pScreenName );
if (!pScreenName)
continue;
const char *pScreenClassname;
GetControlPanelClassName( nPanel, pScreenClassname );
if ( !pScreenClassname )
continue;
CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname );
while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 )
{
pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname );
}
if ( pScreen )
{
int nScreen = m_hScreens.AddToTail( );
m_hScreens[nScreen].Set( pScreen );
pScreen->SetActive( true );
}
return;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,255 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Combine gun turret that emerges from a trapdoor in the ground.
//
//=============================================================================//
#include "cbase.h"
#include "npc_turret_ground.h"
#include "ammodef.h"
#include "IEffects.h"
#include "te_effect_dispatch.h"
extern ConVar ai_newgroundturret;
ConVar turret_ground_damage_multiplier( "turret_ground_damage_multiplier", "8.0", FCVAR_CHEAT );
class CNPC_Portal_GroundTurret : public CNPC_GroundTurret
{
DECLARE_CLASS( CNPC_Portal_GroundTurret, CNPC_GroundTurret );
DECLARE_DATADESC();
private:
float m_fViewconeDegrees;
public:
virtual void Spawn( void );
virtual float GetAttackDamageScale( CBaseEntity *pVictim );
virtual void Shoot();
virtual void Scan();
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
};
BEGIN_DATADESC( CNPC_Portal_GroundTurret )
DEFINE_KEYFIELD( m_fViewconeDegrees, FIELD_FLOAT, "ConeOfFire" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_portal_turret_ground, CNPC_Portal_GroundTurret );
void CNPC_Portal_GroundTurret::Spawn( void )
{
Precache();
UTIL_SetModel( this, "models/combine_turrets/ground_turret.mdl" );
SetNavType( NAV_FLY );
SetSolid( SOLID_VPHYSICS );
SetBloodColor( DONT_BLEED );
m_iHealth = 125;
m_flFieldOfView = cos( ((m_fViewconeDegrees / 2.0f) * M_PI / 180.0f) );
m_NPCState = NPC_STATE_NONE;
m_vecSpread.x = 0.5;
m_vecSpread.y = 0.5;
m_vecSpread.z = 0.5;
CapabilitiesClear();
AddEFlags( EFL_NO_DISSOLVE );
NPCInit();
CapabilitiesAdd( bits_CAP_SIMPLE_RADIUS_DAMAGE );
m_iAmmoType = GetAmmoDef()->Index( "PISTOL" );
m_pSmoke = NULL;
m_bHasExploded = false;
m_bEnabled = false;
if( ai_newgroundturret.GetBool() )
{
m_flSensingDist = 384;
SetDistLook( m_flSensingDist );
}
else
{
m_flSensingDist = 2048;
}
if( !GetParent() )
{
DevMsg("ERROR! npc_ground_turret with no parent!\n");
UTIL_Remove(this);
return;
}
m_flTimeNextShoot = gpGlobals->curtime;
m_flTimeNextPing = gpGlobals->curtime;
m_vecClosedPos = GetAbsOrigin();
StudioFrameAdvance();
Vector vecPos;
GetAttachment( "eyes", vecPos );
SetViewOffset( vecPos - GetAbsOrigin() );
GetAttachment( "light", vecPos );
m_vecLightOffset = vecPos - GetAbsOrigin();
}
float CNPC_Portal_GroundTurret::GetAttackDamageScale( CBaseEntity *pVictim )
{
CBaseCombatCharacter *pBCC = pVictim->MyCombatCharacterPointer();
// Do extra damage to antlions & combine
if ( pBCC )
{
if ( pBCC->Classify() == CLASS_PLAYER )
return turret_ground_damage_multiplier.GetFloat();
}
return BaseClass::GetAttackDamageScale( pVictim );
}
void CNPC_Portal_GroundTurret::Shoot()
{
FireBulletsInfo_t info;
Vector vecSrc = EyePosition();
Vector vecDir;
GetVectors( &vecDir, NULL, NULL );
for( int i = 0 ; i < 1 ; i++ )
{
info.m_vecSrc = vecSrc;
if( i > 0 || !GetEnemy()->IsPlayer() )
{
// Subsequent shots or shots at non-players random
GetVectors( &info.m_vecDirShooting, NULL, NULL );
info.m_vecSpread = m_vecSpread;
}
else
{
// First shot is at the enemy.
info.m_vecDirShooting = GetActualShootTrajectory( vecSrc );
info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
}
info.m_iTracerFreq = 1;
info.m_iShots = 1;
info.m_pAttacker = this;
info.m_flDistance = MAX_COORD_RANGE;
info.m_iAmmoType = m_iAmmoType;
FireBullets( info );
trace_t tr;
CTraceFilterSkipTwoEntities traceFilter( this, info.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE );
Vector vecEnd = info.m_vecSrc + vecDir * info.m_flDistance;
AI_TraceLine( info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr );
if ( tr.m_pEnt && !tr.m_pEnt->IsPlayer() && ( vecDir * info.m_flDistance * tr.fraction ).Length() < 16.0f )
{
CTakeDamageInfo damageInfo;
damageInfo.SetAttacker( this );
damageInfo.SetDamageType( DMG_BULLET );
damageInfo.SetDamage( 20.0f );
TakeDamage( damageInfo );
EmitSound( "NPC_FloorTurret.DryFire" );
}
}
// Do the AR2 muzzle flash
CEffectData data;
data.m_nEntIndex = entindex();
data.m_nAttachmentIndex = LookupAttachment( "eyes" );
data.m_flScale = 1.0f;
data.m_fFlags = MUZZLEFLASH_COMBINE;
DispatchEffect( "MuzzleFlash", data );
EmitSound( "NPC_FloorTurret.ShotSounds" );
m_flTimeNextShoot = gpGlobals->curtime + 0.09;
}
void CNPC_Portal_GroundTurret::Scan( void )
{
if( m_bSeeEnemy )
{
// Using a bool for this check because the condition gets wiped out by changing schedules.
return;
}
if( IsOpeningOrClosing() )
{
// Moving.
return;
}
if( !IsOpen() )
{
// Closed
return;
}
if( !UTIL_FindClientInPVS(edict()) )
{
return;
}
if( gpGlobals->curtime >= m_flTimeNextPing )
{
EmitSound( "NPC_FloorTurret.Ping" );
m_flTimeNextPing = gpGlobals->curtime + 1.0f;
}
QAngle scanAngle;
Vector forward;
Vector vecEye = GetAbsOrigin() + m_vecLightOffset;
// Draw the outer extents
scanAngle = GetAbsAngles();
scanAngle.y += (m_fViewconeDegrees / 2.0f);
AngleVectors( scanAngle, &forward, NULL, NULL );
ProjectBeam( vecEye, forward, 1, 30, 0.1 );
scanAngle = GetAbsAngles();
scanAngle.y -= (m_fViewconeDegrees / 2.0f);
AngleVectors( scanAngle, &forward, NULL, NULL );
ProjectBeam( vecEye, forward, 1, 30, 0.1 );
// Draw a sweeping beam
scanAngle = GetAbsAngles();
scanAngle.y += (m_fViewconeDegrees / 2.0f) * sin( gpGlobals->curtime * 6.0f );
AngleVectors( scanAngle, &forward, NULL, NULL );
ProjectBeam( vecEye, forward, 1, 30, 0.3 );
}
int CNPC_Portal_GroundTurret::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
// Taking damage from myself, make sure it's fatal.
CTakeDamageInfo infoCopy = info;
if ( infoCopy.GetDamageType() == DMG_CRUSH )
{
infoCopy.SetDamage( GetHealth() );
infoCopy.SetDamageType( DMG_REMOVENORAGDOLL | DMG_GENERIC );
}
return BaseClass::BaseClass::OnTakeDamage_Alive( infoCopy );
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
//========= Copyright 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;
};
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
+176
View File
@@ -0,0 +1,176 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
/*
===== portal_client.cpp ========================================================
Portal client/server game specific stuff
*/
#include "cbase.h"
#include "portal_player.h"
#include "portal_gamerules.h"
#include "gamerules.h"
#include "teamplay_gamerules.h"
#include "entitylist.h"
#include "physics.h"
#include "game.h"
#include "player_resource.h"
#include "engine/IEngineSound.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void Host_Say( edict_t *pEdict, bool teamonly );
extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname );
extern bool g_fGameOver;
/*
===========
ClientPutInServer
called each time a player is spawned into the game
============
*/
void ClientPutInServer( edict_t *pEdict, const char *playername )
{
// Allocate a CBasePlayer for pev, and call spawn
CPortal_Player *pPlayer = CPortal_Player::CreatePlayer( "player", pEdict );
pPlayer->PlayerData()->netname = AllocPooledString( playername );
}
void ClientActive( edict_t *pEdict, bool bLoadGame )
{
CPortal_Player *pPlayer = dynamic_cast< CPortal_Player* >( CBaseEntity::Instance( pEdict ) );
Assert( pPlayer );
pPlayer->InitialSpawn();
if ( !bLoadGame )
{
pPlayer->Spawn();
}
}
/*
===============
const char *GetGameDescription()
Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
===============
*/
const char *GetGameDescription()
{
if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
return g_pGameRules->GetGameDescription();
else
return "Half-Life 2";
}
//-----------------------------------------------------------------------------
// Purpose: Given a player and optional name returns the entity of that
// classname that the player is nearest facing
//
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity* FindEntity( edict_t *pEdict, char *classname)
{
// If no name was given set bits based on the picked
if (FStrEq(classname,""))
{
return (FindPickerEntityClass( static_cast<CBasePlayer*>(GetContainingEntity(pEdict)), classname ));
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Precache game-specific models & sounds
//-----------------------------------------------------------------------------
void ClientGamePrecache( void )
{
CBaseEntity::PrecacheModel("models/player.mdl");
CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" );
CBaseEntity::PrecacheModel("models/weapons/v_hands.mdl");
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowAmmo" );
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowHealth" );
CBaseEntity::PrecacheScriptSound( "Missile.ShotDown" );
CBaseEntity::PrecacheScriptSound( "Bullets.DefaultNearmiss" );
CBaseEntity::PrecacheScriptSound( "Bullets.GunshipNearmiss" );
CBaseEntity::PrecacheScriptSound( "Bullets.StriderNearmiss" );
CBaseEntity::PrecacheScriptSound( "Geiger.BeepHigh" );
CBaseEntity::PrecacheScriptSound( "Geiger.BeepLow" );
CBaseEntity::PrecacheModel( "models/portals/portal1.mdl" );
CBaseEntity::PrecacheModel( "models/portals/portal2.mdl" );
}
// called by ClientKill and DeadThink
void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
{
if (gpGlobals->coop || gpGlobals->deathmatch)
{
if ( fCopyCorpse )
{
// make a copy of the dead body for appearances sake
((CPortal_Player *)pEdict)->CreateCorpse();
}
// respawn player
pEdict->Spawn();
}
else
{ // restart the entire server
engine->ServerCommand("reload\n");
}
}
void GameStartFrame( void )
{
VPROF("GameStartFrame()");
if ( g_fGameOver )
return;
gpGlobals->teamplay = (teamplay.GetInt() != 0);
}
//=========================================================
// instantiate the proper game rules object
//=========================================================
void InstallGameRules()
{
if ( !gpGlobals->deathmatch )
{
CreateGameRulesObject( "CPortalGameRules" );
return;
}
else
{
if ( teamplay.GetInt() > 0 )
{
// teamplay
CreateGameRulesObject( "CTeamplayRules" );
}
else
{
// vanilla deathmatch
CreateGameRulesObject( "CMultiplayRules" );
}
}
}
+683
View File
@@ -0,0 +1,683 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "portal_gamestats.h"
#include "tier1/utlbuffer.h"
#include "portal_player.h"
#define PORTALSTATS_TRIMEVENT( varName, varType )\
if( varName->Count() > varType::TRIMSIZE )\
varName->RemoveMultiple( 0, (varName->Count() - varType::TRIMSIZE) );
#define PORTALSTATS_PREPCHUNK( structType, bufName, SizePositionVarNameToUse )\
SaveBuffer.PutUnsignedShort( structType::CHUNKID );\
int SizePositionVarNameToUse = bufName.TellPut();\
bufName.PutUnsignedInt( 0 );
#define PORTALSTATS_WRITECHUNKSIZE( bufName, SizePositionVariable ) \
{\
int SizePositionVariable ## _askdjbhas = bufName.TellPut() - SizePositionVariable;\
bufName.SeekPut( CUtlBuffer::SEEK_HEAD, SizePositionVariable );\
bufName.PutUnsignedInt( SizePositionVariable ## _askdjbhas );\
bufName.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );\
}
static Portal_Gamestats_LevelStats_t s_DummyStats;
CPortalGameStats g_PortalGameStats;
class CPortalGameStatsSingleton //used to remove the constructor destructor from the general class
{
public:
CPortalGameStatsSingleton( void )
{
gamestats = (CBaseGameStats *)&g_PortalGameStats;
CreateLevelStatPointers( &s_DummyStats );
}
~CPortalGameStatsSingleton( void )
{
AssertMsg( (CBaseGameStats::StatTrackingAllowed() == false) ||
((s_DummyStats.m_pDeaths->Count() == 0) &&
(s_DummyStats.m_pPlacements->Count() == 0) &&
(s_DummyStats.m_pUseEvents->Count() == 0) &&
(s_DummyStats.m_pStuckSpots->Count() == 0) &&
(s_DummyStats.m_pJumps->Count() == 0) &&
(s_DummyStats.m_pTimeSpentInVisLeafs->Count() == 0)),
"Some stats were deferred to the dummy entry." );
DestroyLevelStatPointers( &s_DummyStats );
}
};
CPortalGameStatsSingleton s_CPGSS_ThisJustSitsInMemory;
CPortalGameStats::CPortalGameStats( void )
: m_pCurrentMapStats( &s_DummyStats )
{
}
CPortalGameStats::~CPortalGameStats( void )
{
Clear();
}
void Portal_Gamestats_LevelStats_t::AppendSubChunksToBuffer( CUtlBuffer &SaveBuffer )
{
if( m_pDeaths->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pDeaths, PlayerDeaths_t );
PORTALSTATS_PREPCHUNK( PlayerDeaths_t, SaveBuffer, iSubChunkSizePosition );
int iDeathStatsCount = m_pDeaths->Count();
SaveBuffer.PutUnsignedInt( iDeathStatsCount );
for( int i = 0; i != iDeathStatsCount; ++i )
{
PlayerDeaths_t &DeathStat = m_pDeaths->Element( i );
SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.x );
SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.y );
SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.z );
SaveBuffer.PutInt( DeathStat.iDamageType );
SaveBuffer.PutString( DeathStat.szAttackerClassName );
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
if( m_pPlacements->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pPlacements, PortalPlacement_t );
PORTALSTATS_PREPCHUNK( PortalPlacement_t, SaveBuffer, iSubChunkSizePosition );
int iPlacementStatsCount = m_pPlacements->Count();
SaveBuffer.PutUnsignedInt( iPlacementStatsCount );
for( int i = 0; i != iPlacementStatsCount; ++i )
{
PortalPlacement_t &PortalPlacementStat = m_pPlacements->Element( i );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.x );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.y );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.z );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.x );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.y );
SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.z );
SaveBuffer.PutChar( PortalPlacementStat.iSuccessCode );
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
if( m_pUseEvents->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pUseEvents, PlayerUse_t );
PORTALSTATS_PREPCHUNK( PlayerUse_t, SaveBuffer, iSubChunkSizePosition );
int iUseEventCount = m_pUseEvents->Count();
SaveBuffer.PutUnsignedInt( iUseEventCount );
for( int i = 0; i != iUseEventCount; ++i )
{
PlayerUse_t &UseEvent = m_pUseEvents->Element( i );
SaveBuffer.PutFloat( UseEvent.ptTraceStart.x );
SaveBuffer.PutFloat( UseEvent.ptTraceStart.y );
SaveBuffer.PutFloat( UseEvent.ptTraceStart.z );
SaveBuffer.PutFloat( UseEvent.vTraceDelta.x );
SaveBuffer.PutFloat( UseEvent.vTraceDelta.y );
SaveBuffer.PutFloat( UseEvent.vTraceDelta.z );
SaveBuffer.PutString( UseEvent.szUseEntityClassName );
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
if( m_pStuckSpots->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pStuckSpots, StuckEvent_t );
PORTALSTATS_PREPCHUNK( StuckEvent_t, SaveBuffer, iSubChunkSizePosition );
int iStuckStatsCount = m_pStuckSpots->Count();
SaveBuffer.PutUnsignedInt( iStuckStatsCount );
for( int i = 0; i != iStuckStatsCount; ++i )
{
StuckEvent_t &StuckStat = m_pStuckSpots->Element( i );
SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.x );
SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.y );
SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.z );
SaveBuffer.PutFloat( StuckStat.qPlayerAngles.x );
SaveBuffer.PutFloat( StuckStat.qPlayerAngles.y );
SaveBuffer.PutFloat( StuckStat.qPlayerAngles.z );
unsigned char bitFlags = 0;
if( StuckStat.bNearPortal )
bitFlags |= (1 << 0);
if( StuckStat.bDucking )
bitFlags |= (1 << 1);
SaveBuffer.PutUnsignedChar( bitFlags );
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
if( m_pJumps->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pJumps, JumpEvent_t );
PORTALSTATS_PREPCHUNK( JumpEvent_t, SaveBuffer, iSubChunkSizePosition );
int iJumpStatsCount = m_pJumps->Count();
SaveBuffer.PutUnsignedInt( iJumpStatsCount );
for( int i = 0; i != iJumpStatsCount; ++i )
{
JumpEvent_t &JumpStat = m_pJumps->Element( i );
SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.x );
SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.y );
SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.z );
SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.x );
SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.y );
SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.z );
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
if( m_pTimeSpentInVisLeafs->Count() != 0 )
{
PORTALSTATS_TRIMEVENT( m_pTimeSpentInVisLeafs, LeafTimes_t );
PORTALSTATS_PREPCHUNK( LeafTimes_t, SaveBuffer, iSubChunkSizePosition );
int iLeafTimeStatsCount = m_pTimeSpentInVisLeafs->Count();
SaveBuffer.PutUnsignedInt( iLeafTimeStatsCount );
for( int i = 0; i != iLeafTimeStatsCount; ++i )
{
LeafTimes_t &LeafTimeStat = m_pTimeSpentInVisLeafs->Element( i );
SaveBuffer.PutFloat( LeafTimeStat.fTimeSpentInVisLeaf ); //assumes visleafs will be the same when this data is loaded again, or that there will be a way to invalidate the data
}
PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition );
}
}
void Portal_Gamestats_LevelStats_t::LoadSubChunksFromBuffer( CUtlBuffer &LoadBuffer, unsigned int iChunkEndPosition )
{
Clear();
while( ((unsigned int)LoadBuffer.TellGet()) != iChunkEndPosition )
{
Assert( (iChunkEndPosition - LoadBuffer.TellGet()) > (sizeof( unsigned short ) + sizeof( unsigned int )) ); //at least an empty chunk left
unsigned short iChunkID = LoadBuffer.GetUnsignedShort();
unsigned int iChunkSize = LoadBuffer.GetUnsignedInt() - sizeof( unsigned int ); //chunk size includes the chunk size data itself
#ifdef _DEBUG
unsigned int iChunkEndPosition = LoadBuffer.TellGet() + iChunkSize; //used in an assert later
#endif
switch( iChunkID )
{
#ifdef PORTAL_GAMESTATS_VERBOSE //don't bother loading verbose chunks if we're not going to save them back out
case PlayerDeaths_t::CHUNKID:
{
unsigned int iDeathStatCount = LoadBuffer.GetUnsignedInt();
for( unsigned int i = 0; i != iDeathStatCount; ++i )
{
int index = m_pDeaths->AddToTail();
PlayerDeaths_t &DeathStat = m_pDeaths->Element( index );
DeathStat.ptPositionOfDeath.x = LoadBuffer.GetFloat();
DeathStat.ptPositionOfDeath.y = LoadBuffer.GetFloat();
DeathStat.ptPositionOfDeath.z = LoadBuffer.GetFloat();
DeathStat.iDamageType = LoadBuffer.GetInt();
LoadBuffer.GetString( DeathStat.szAttackerClassName );
}
break;
}
case PortalPlacement_t::CHUNKID:
{
unsigned int iPlacementStatCount = LoadBuffer.GetUnsignedInt();
for( unsigned int i = 0; i != iPlacementStatCount; ++i )
{
int index = m_pPlacements->AddToTail();
PortalPlacement_t &PlacementStat = m_pPlacements->Element( index );
PlacementStat.ptPlayerFiredFrom.x = LoadBuffer.GetFloat();
PlacementStat.ptPlayerFiredFrom.y = LoadBuffer.GetFloat();
PlacementStat.ptPlayerFiredFrom.z = LoadBuffer.GetFloat();
PlacementStat.ptPlacementPosition.x = LoadBuffer.GetFloat();
PlacementStat.ptPlacementPosition.y = LoadBuffer.GetFloat();
PlacementStat.ptPlacementPosition.z = LoadBuffer.GetFloat();
PlacementStat.iSuccessCode = LoadBuffer.GetChar();
}
break;
}
case PlayerUse_t::CHUNKID:
{
int iUseEventCount = LoadBuffer.GetUnsignedInt();
for( int i = 0; i != iUseEventCount; ++i )
{
int index = m_pUseEvents->AddToTail();
PlayerUse_t &UseEvent = m_pUseEvents->Element( index );
UseEvent.ptTraceStart.x = LoadBuffer.GetFloat();
UseEvent.ptTraceStart.y = LoadBuffer.GetFloat();
UseEvent.ptTraceStart.z = LoadBuffer.GetFloat();
UseEvent.vTraceDelta.x = LoadBuffer.GetFloat();
UseEvent.vTraceDelta.y = LoadBuffer.GetFloat();
UseEvent.vTraceDelta.z = LoadBuffer.GetFloat();
LoadBuffer.GetString( UseEvent.szUseEntityClassName );
}
break;
}
case StuckEvent_t::CHUNKID:
{
unsigned int iStuckEventCount = LoadBuffer.GetUnsignedInt();
for( unsigned int i = 0; i != iStuckEventCount; ++i )
{
int index = m_pStuckSpots->AddToTail();
StuckEvent_t &StuckEvent = m_pStuckSpots->Element( index );
StuckEvent.ptPlayerPosition.x = LoadBuffer.GetFloat();
StuckEvent.ptPlayerPosition.y = LoadBuffer.GetFloat();
StuckEvent.ptPlayerPosition.z = LoadBuffer.GetFloat();
StuckEvent.qPlayerAngles.x = LoadBuffer.GetFloat();
StuckEvent.qPlayerAngles.y = LoadBuffer.GetFloat();
StuckEvent.qPlayerAngles.z = LoadBuffer.GetFloat();
unsigned char bitFlags = LoadBuffer.GetUnsignedChar();
StuckEvent.bNearPortal = ( (bitFlags & (1 << 0)) != 0 );
StuckEvent.bDucking = ( (bitFlags & (1 << 1)) != 0 );
}
break;
}
case JumpEvent_t::CHUNKID:
{
unsigned int iJumpEventCount = LoadBuffer.GetUnsignedInt();
for( unsigned int i = 0; i != iJumpEventCount; ++i )
{
int index = m_pJumps->AddToTail();
JumpEvent_t &JumpEvent = m_pJumps->Element( index );
JumpEvent.ptPlayerPositionAtJumpStart.x = LoadBuffer.GetFloat();
JumpEvent.ptPlayerPositionAtJumpStart.y = LoadBuffer.GetFloat();
JumpEvent.ptPlayerPositionAtJumpStart.z = LoadBuffer.GetFloat();
JumpEvent.vPlayerVelocityAtJumpStart.x = LoadBuffer.GetFloat();
JumpEvent.vPlayerVelocityAtJumpStart.y = LoadBuffer.GetFloat();
JumpEvent.vPlayerVelocityAtJumpStart.z = LoadBuffer.GetFloat();
}
break;
}
case LeafTimes_t::CHUNKID:
{
//IMPORTANT TODO
//TODO: Detect if the leaves have changed and invalidate these counts
unsigned int iLeafCount = LoadBuffer.GetUnsignedInt();
for( unsigned int i = 0; i != iLeafCount; ++i )
{
int index = m_pTimeSpentInVisLeafs->AddToTail();
LeafTimes_t &LeafTime = m_pTimeSpentInVisLeafs->Element( index );
LeafTime.fTimeSpentInVisLeaf = LoadBuffer.GetFloat();
}
break;
}
#else
case PlayerDeaths_t::CHUNKID: //warning workaround
#endif
default:
{
//an unknown chunk, skip it
LoadBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, iChunkSize );
}
};
Assert( ((unsigned int)LoadBuffer.TellGet()) == iChunkEndPosition );
};
}
void Portal_Gamestats_LevelStats_t::Clear( void )
{
m_pDeaths->RemoveAll();
m_pPlacements->RemoveAll();
m_pStuckSpots->RemoveAll();
m_pJumps->RemoveAll();
m_pTimeSpentInVisLeafs->RemoveAll();
}
void CPortalGameStats::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer )
{
#ifdef PORTAL_GAMESTATS_VERBOSE //we only have verbose chunks for now, so only write custom data if we're verbosely tracking
SaveBuffer.PutUnsignedShort( PORTAL_GAMESTATS_VERSION );
//no headers allowed, chunks were chosen for their flexibility in loading even when parts of the data are unknown
//you can simulate a header by enclosing the entirety of the custom data in a chunk that starts with a header, but you'll kill loading in old versions
if( m_CustomMapStats.Count() != 0 ) //we have some map stats
{
//put out a map chunk for each map
for ( int i = m_CustomMapStats.First(); i != m_CustomMapStats.InvalidIndex(); i = m_CustomMapStats.Next( i ) )
{
SaveBuffer.PutShort( Portal_Gamestats_LevelStats_t::CHUNKID );
//we can trivially find the chunk size after the chunk is written, but chunk size needs to be at the beginning, reserve the space for chunk size now
int iChunkSizePosition = SaveBuffer.TellPut();
SaveBuffer.PutUnsignedInt( 0 );
char const *szMapName = m_CustomMapStats.GetElementName( i );
Portal_Gamestats_LevelStats_t &mapStats = m_CustomMapStats[ i ];
SaveBuffer.PutString( szMapName );
mapStats.AppendSubChunksToBuffer( SaveBuffer );
//write out the map stats chunk size
{
int iChunkSize = SaveBuffer.TellPut() - iChunkSizePosition;
Assert( iChunkSize >= sizeof( int ) ); //minimum sizeof( int )
#ifdef _DEBUG
int iOldTellPut = SaveBuffer.TellPut(); //needed for an assert below
#endif
SaveBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, iChunkSizePosition );
SaveBuffer.PutUnsignedInt( iChunkSize );
SaveBuffer.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
Assert( iOldTellPut == SaveBuffer.TellPut() ); //writing the chunk size should have overwritten, not inserted
}
}
}
#endif //#ifdef PORTAL_GAMESTATS_VERBOSE
}
void CPortalGameStats::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer )
{
#ifdef _DEBUG
unsigned short iSaveStatsVersion = LoadBuffer.GetUnsignedShort();
AssertOnce( iSaveStatsVersion <= PORTAL_GAMESTATS_VERSION ); //useful to know, but shouldn't be a failure case
#else
LoadBuffer.GetUnsignedShort(); //don't really need the version
#endif
int iEndPosition = LoadBuffer.TellPut();
while( LoadBuffer.TellGet() != iEndPosition )
{
Assert( (iEndPosition - LoadBuffer.TellGet()) > (sizeof( unsigned short ) + sizeof( unsigned int )) ); //at least an empty chunk left
unsigned short iChunkID = LoadBuffer.GetUnsignedShort();
unsigned int iChunkSize = LoadBuffer.GetUnsignedInt() - sizeof( unsigned int ); //chunk size includes the chunk size data itself
#ifdef PORTAL_GAMESTATS_VERBOSE
unsigned int iChunkEndPosition = LoadBuffer.TellGet() + iChunkSize; //used in an assert later
#endif
switch( iChunkID )
{
#ifdef PORTAL_GAMESTATS_VERBOSE //levelstats only have verbose data for the time being, so only bother to load verboseness if we're still tracking it
case Portal_Gamestats_LevelStats_t::CHUNKID:
{
//map chunk
char szMapName[256];
LoadBuffer.GetString( szMapName );
Portal_Gamestats_LevelStats_t *mapStats = FindOrAddMapStats( szMapName );
mapStats->LoadSubChunksFromBuffer( LoadBuffer, iChunkEndPosition );
break;
}
#else
case Portal_Gamestats_LevelStats_t::CHUNKID: //warning workaround
#endif
default:
{
//an unknown chunk, skip it
LoadBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, iChunkSize );
}
};
#ifdef PORTAL_GAMESTATS_VERBOSE
Assert( ((unsigned int)LoadBuffer.TellGet()) == iChunkEndPosition );
#endif
}
}
void CPortalGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
{
#ifdef PORTAL_GAMESTATS_VERBOSE
if( CBaseGameStats::StatTrackingAllowed() == false )
return;
int index = m_pCurrentMapStats->m_pDeaths->AddToTail();
Portal_Gamestats_LevelStats_t::PlayerDeaths_t &DeathStat = m_pCurrentMapStats->m_pDeaths->Element( index );
DeathStat.ptPositionOfDeath = pPlayer->GetAbsOrigin();
DeathStat.iDamageType = info.GetDamageType();
DeathStat.szAttackerClassName[0] = '\0';
CBaseEntity *pInflictor = info.GetInflictor();
if( pInflictor )
Q_strncpy( DeathStat.szAttackerClassName, pInflictor->GetClassname(), sizeof( DeathStat.szAttackerClassName ) );
#endif
}
void CPortalGameStats::Event_PlayerJump( const Vector &ptStartPosition, const Vector &vStartVelocity )
{
#ifdef PORTAL_GAMESTATS_VERBOSE
if( CBaseGameStats::StatTrackingAllowed() == false )
return;
int index = m_pCurrentMapStats->m_pJumps->AddToTail();
Portal_Gamestats_LevelStats_t::JumpEvent_t &JumpStat = m_pCurrentMapStats->m_pJumps->Element( index );
JumpStat.ptPlayerPositionAtJumpStart = ptStartPosition;
JumpStat.vPlayerVelocityAtJumpStart = vStartVelocity;
#endif
}
void CPortalGameStats::Event_PortalPlacement( const Vector &ptPlayerFiredFrom, const Vector &ptAttemptedPosition, char iSuccessCode )
{
#ifdef PORTAL_GAMESTATS_VERBOSE
if( CBaseGameStats::StatTrackingAllowed() == false )
return;
int index = m_pCurrentMapStats->m_pPlacements->AddToTail();
Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( index );
PlacementStat.ptPlacementPosition = ptAttemptedPosition;
PlacementStat.ptPlayerFiredFrom = ptPlayerFiredFrom;
PlacementStat.iSuccessCode = iSuccessCode;
#endif
}
void CPortalGameStats::Event_PlayerUsed( const Vector &ptTraceStart, const Vector &vTraceDelta, CBaseEntity *pUsedEntity )
{
#ifdef PORTAL_GAMESTATS_VERBOSE
if( CBaseGameStats::StatTrackingAllowed() == false )
return;
static float fLastUseTime = 0.0f;
if( fLastUseTime > gpGlobals->curtime ) //I'm not positive, but I think curtime resets between levels, cheap to do this
fLastUseTime = 0.0f;
if( (gpGlobals->curtime - fLastUseTime) < 0.25f ) //use events cluster
return;
fLastUseTime = gpGlobals->curtime;
int index = m_pCurrentMapStats->m_pUseEvents->AddToTail();
Portal_Gamestats_LevelStats_t::PlayerUse_t &UseEvent = m_pCurrentMapStats->m_pUseEvents->Element( index );
UseEvent.ptTraceStart = ptTraceStart;
UseEvent.vTraceDelta = vTraceDelta;
UseEvent.szUseEntityClassName[0] = '\0';
if( pUsedEntity )
Q_strncpy( UseEvent.szUseEntityClassName, pUsedEntity->GetClassname(), sizeof( UseEvent.szUseEntityClassName ) );
#endif
}
void CPortalGameStats::Event_PlayerStuck( CPortal_Player *pPlayer )
{
#ifdef PORTAL_GAMESTATS_VERBOSE
if( CBaseGameStats::StatTrackingAllowed() == false )
return;
static float fLastStuckTime = 0.0f;
if( fLastStuckTime > gpGlobals->curtime ) //I'm not positive, but I think curtime resets between levels, cheap to do this
fLastStuckTime = 0.0f;
if( (gpGlobals->curtime - fLastStuckTime) < 10.0f ) //only log one stuck spot per 10 second interval (in case it oscillates)
return;
fLastStuckTime = gpGlobals->curtime;
int index = m_pCurrentMapStats->m_pStuckSpots->AddToTail();
Portal_Gamestats_LevelStats_t::StuckEvent_t &StuckSpot = m_pCurrentMapStats->m_pStuckSpots->Element( index );
StuckSpot.ptPlayerPosition = pPlayer->GetAbsOrigin();
StuckSpot.qPlayerAngles = pPlayer->GetAbsAngles();
StuckSpot.bNearPortal = (pPlayer->m_hPortalEnvironment.Get() != NULL);
StuckSpot.bDucking = ((pPlayer->m_nButtons & IN_DUCK) != 0);
#endif
}
void CPortalGameStats::Event_LevelInit( void )
{
BaseClass::Event_LevelInit();
m_pCurrentMapStats = FindOrAddMapStats( STRING( gpGlobals->mapname ) );
}
void CPortalGameStats::Event_MapChange( const char *szOldMapName, const char *szNewMapName )
{
BaseClass::Event_MapChange( szOldMapName, szNewMapName );
m_pCurrentMapStats = FindOrAddMapStats( szNewMapName );
}
void CPortalGameStats::Clear( void )
{
for( int i = m_CustomMapStats.First(); i != m_CustomMapStats.InvalidIndex(); i = m_CustomMapStats.Next( i ) )
{
DestroyLevelStatPointers( &m_CustomMapStats[i] );
}
m_CustomMapStats.RemoveAll();
}
Portal_Gamestats_LevelStats_t *CPortalGameStats::FindOrAddMapStats( const char *szMapName )
{
int idx = m_CustomMapStats.Find( szMapName );
if( idx == m_CustomMapStats.InvalidIndex() )
{
idx = m_CustomMapStats.Insert( szMapName );
CreateLevelStatPointers( &m_CustomMapStats[idx] );
}
return &m_CustomMapStats[ idx ];
}
void CreateLevelStatPointers( Portal_Gamestats_LevelStats_t *pFillIn )
{
pFillIn->m_pDeaths = new CUtlVector<Portal_Gamestats_LevelStats_t::PlayerDeaths_t>;
pFillIn->m_pPlacements = new CUtlVector<Portal_Gamestats_LevelStats_t::PortalPlacement_t>;
pFillIn->m_pUseEvents = new CUtlVector<Portal_Gamestats_LevelStats_t::PlayerUse_t>;
pFillIn->m_pStuckSpots = new CUtlVector<Portal_Gamestats_LevelStats_t::StuckEvent_t>;
pFillIn->m_pJumps = new CUtlVector<Portal_Gamestats_LevelStats_t::JumpEvent_t>;
pFillIn->m_pTimeSpentInVisLeafs = new CUtlVector<Portal_Gamestats_LevelStats_t::LeafTimes_t>;
}
void DestroyLevelStatPointers( Portal_Gamestats_LevelStats_t *pDestroyFrom )
{
delete pDestroyFrom->m_pDeaths;
delete pDestroyFrom->m_pPlacements;
delete pDestroyFrom->m_pUseEvents;
delete pDestroyFrom->m_pStuckSpots;
delete pDestroyFrom->m_pJumps;
delete pDestroyFrom->m_pTimeSpentInVisLeafs;
}
static char const *portalMaps[] =
{
"testchmb_a_00",
"testchmb_a_01",
"testchmb_a_02",
"testchmb_a_03",
"testchmb_a_04",
"testchmb_a_05",
"testchmb_a_06",
"testchmb_a_07",
"testchmb_a_08",
"testchmb_a_09",
"testchmb_a_10",
"testchmb_a_11",
//"testchmb_a_12", //12 got deleted/skipped
"testchmb_a_13",
"testchmb_a_14",
"testchmb_a_15",
"escape_00",
"escape_01",
"escape_02"
};
bool CPortalGameStats::UserPlayedAllTheMaps( void )
{
int c = ARRAYSIZE( portalMaps );
for ( int i = 0; i < c; ++i )
{
int idx = m_BasicStats.m_MapTotals.Find( portalMaps[ i ] );
if( idx == m_BasicStats.m_MapTotals.InvalidIndex() )
return false;
}
return true;
}
+139
View File
@@ -0,0 +1,139 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef PORTAL_GAMESTATS_H
#define PORTAL_GAMESTATS_H
#ifdef _WIN32
#pragma once
#endif
#include "gamestats.h"
#define PORTAL_GAMESTATS_VERSION 001
//#define PORTAL_GAMESTATS_VERBOSE //verbose logging of extra stats, only a good idea during internal tests, centralized here for easy on/off
//NEVER REMOVE A STRUCTURE OR CHANGE A CHUNKID. If you need to change how a chunk works, make a new structure with a new ID
struct Portal_Gamestats_LevelStats_t //most of this is only tracked in verbose mode
{
static const unsigned short CHUNKID = 1;
//substructures
struct PlayerDeaths_t
{
static const unsigned short CHUNKID = 1; //subchunks start over with id's
static const unsigned short TRIMSIZE = 200; //trim logs if more than this many entries exist for a single map
Vector ptPositionOfDeath;
int iDamageType;
char szAttackerClassName[32];
};
struct PortalPlacement_t
{
static const unsigned short CHUNKID = 2;
static const unsigned short TRIMSIZE = 1000; //trim logs if more than this many entries exist for a single map
Vector ptPlayerFiredFrom;
Vector ptPlacementPosition;
char iSuccessCode;
};
struct PlayerUse_t
{
static const unsigned short CHUNKID = 3;
static const unsigned short TRIMSIZE = 500; //trim logs if more than this many entries exist for a single map
Vector ptTraceStart;
Vector vTraceDelta;
char szUseEntityClassName[32];
};
struct StuckEvent_t
{
static const unsigned short CHUNKID = 4;
static const unsigned short TRIMSIZE = 100; //trim logs if more than this many entries exist for a single map
Vector ptPlayerPosition;
QAngle qPlayerAngles;
bool bNearPortal;
bool bDucking;
};
struct JumpEvent_t
{
static const unsigned short CHUNKID = 5;
static const unsigned short TRIMSIZE = 1000; //trim logs if more than this many entries exist for a single map
Vector ptPlayerPositionAtJumpStart;
Vector vPlayerVelocityAtJumpStart;
};
struct LeafTimes_t
{
static const unsigned short CHUNKID = 6;
static const unsigned short TRIMSIZE = 10000; //trim logs if more than this many entries exist for a single map
float fTimeSpentInVisLeaf;
LeafTimes_t( void ) : fTimeSpentInVisLeaf( 0.0f ) { };
};
//these are created/destroyed by parent CPortalGameStats to avoid create/copy/destroy confusion
CUtlVector<PlayerDeaths_t> *m_pDeaths;
CUtlVector<PortalPlacement_t> *m_pPlacements;
CUtlVector<PlayerUse_t> *m_pUseEvents;
CUtlVector<StuckEvent_t> *m_pStuckSpots;
CUtlVector<JumpEvent_t> *m_pJumps;
CUtlVector<LeafTimes_t> *m_pTimeSpentInVisLeafs;
void AppendSubChunksToBuffer( CUtlBuffer &SaveBuffer );
void LoadSubChunksFromBuffer( CUtlBuffer &LoadBuffer, unsigned int iChunkEndPosition );
void Clear( void );
};
class CPortal_Player;
class CPortalGameStats : CBaseGameStats
{
typedef CBaseGameStats BaseClass;
public:
~CPortalGameStats( void );
CPortalGameStats( void );
void Clear( void );
virtual void Event_LevelInit( void );
virtual void Event_MapChange( const char *szOldMapName, const char *szNewMapName );
virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
void Event_PortalPlacement( const Vector &ptPlayerFiredFrom, const Vector &ptAttemptedPosition, char iSuccessCode );
void Event_PlayerJump( const Vector &ptStartPosition, const Vector &vStartVelocity );
void Event_PlayerUsed( const Vector &ptTraceStart, const Vector &vTraceDelta, CBaseEntity *pUsedEntity );
void Event_PlayerStuck( CPortal_Player *pPlayer );
virtual bool StatTrackingEnabledForMod( void ) { return true; }
virtual bool UserPlayedAllTheMaps( void );
#ifdef _DEBUG
virtual bool AutoUpload_OnShutdown( void ) { return false; } //don't upload while we're debugging
#endif
virtual void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer );
virtual void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer );
Portal_Gamestats_LevelStats_t *m_pCurrentMapStats;
protected:
CUtlDict< Portal_Gamestats_LevelStats_t, unsigned short > m_CustomMapStats;
Portal_Gamestats_LevelStats_t *FindOrAddMapStats( const char *szMapName );
};
extern CPortalGameStats g_PortalGameStats;
void CreateLevelStatPointers( Portal_Gamestats_LevelStats_t *pFillIn );
void DestroyLevelStatPointers( Portal_Gamestats_LevelStats_t *pDestroyFrom );
#endif // PORTAL_GAMESTATS_H
+199
View File
@@ -0,0 +1,199 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
/*
===== portal_client.cpp ========================================================
Portal client/server game specific stuff
*/
#include "cbase.h"
#include "portal_player.h"
#include "portal_gamerules.h"
#include "gamerules.h"
#include "teamplay_gamerules.h"
#include "EntityList.h"
#include "physics.h"
#include "game.h"
#include "player_resource.h"
#include "engine/IEngineSound.h"
#include "team.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void Host_Say( edict_t *pEdict, bool teamonly );
extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname );
extern bool g_fGameOver;
void FinishClientPutInServer( CPortal_Player *pPlayer )
{
pPlayer->InitialSpawn();
pPlayer->Spawn();
char sName[128];
Q_strncpy( sName, pPlayer->GetPlayerName(), sizeof( sName ) );
// First parse the name and remove any %'s
for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ )
{
// Replace it with a space
if ( *pApersand == '%' )
*pApersand = ' ';
}
// notify other clients of player joining the game
UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Game_connected", sName[0] != 0 ? sName : "<unconnected>" );
if ( PortalMPGameRules()->IsTeamplay() == true )
{
ClientPrint( pPlayer, HUD_PRINTTALK, "You are on team %s1\n", pPlayer->GetTeam()->GetName() );
}
const ConVar *hostname = cvar->FindVar( "hostname" );
const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY";
KeyValues *data = new KeyValues("data");
data->SetString( "title", title ); // info panel title
data->SetString( "type", "1" ); // show userdata from stringtable entry
data->SetString( "msg", "motd" ); // use this stringtable entry
//pPlayer->ShowViewPortPanel( PANEL_INFO, true, data );
data->deleteThis();
}
/*
===========
ClientPutInServer
called each time a player is spawned into the game
============
*/
void ClientPutInServer( edict_t *pEdict, const char *playername )
{
// Allocate a CBasePlayer for pev, and call spawn
CPortal_Player *pPlayer = CPortal_Player::CreatePlayer( "player", pEdict );
pPlayer->PlayerData()->netname = AllocPooledString( playername );
}
void ClientActive( edict_t *pEdict, bool bLoadGame )
{
Assert( !bLoadGame );
CPortal_Player *pPlayer = dynamic_cast< CPortal_Player* >( CBaseEntity::Instance( pEdict ) );
Assert( pPlayer );
FinishClientPutInServer( pPlayer );
}
/*
===============
const char *GetGameDescription()
Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
===============
*/
const char *GetGameDescription()
{
if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
return g_pGameRules->GetGameDescription();
else
return "Half-Life 2";
}
//-----------------------------------------------------------------------------
// Purpose: Given a player and optional name returns the entity of that
// classname that the player is nearest facing
//
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity* FindEntity( edict_t *pEdict, char *classname)
{
// If no name was given set bits based on the picked
if (FStrEq(classname,""))
{
return (FindPickerEntityClass( static_cast<CBasePlayer*>(GetContainingEntity(pEdict)), classname ));
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Precache game-specific models & sounds
//-----------------------------------------------------------------------------
void ClientGamePrecache( void )
{
CBaseEntity::PrecacheModel("models/player.mdl");
CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" );
CBaseEntity::PrecacheModel("models/weapons/v_hands.mdl");
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowAmmo" );
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowHealth" );
CBaseEntity::PrecacheScriptSound( "FX_AntlionImpact.ShellImpact" );
CBaseEntity::PrecacheScriptSound( "Missile.ShotDown" );
CBaseEntity::PrecacheScriptSound( "Bullets.DefaultNearmiss" );
CBaseEntity::PrecacheScriptSound( "Bullets.GunshipNearmiss" );
CBaseEntity::PrecacheScriptSound( "Bullets.StriderNearmiss" );
CBaseEntity::PrecacheScriptSound( "Geiger.BeepHigh" );
CBaseEntity::PrecacheScriptSound( "Geiger.BeepLow" );
CBaseEntity::PrecacheModel( "models/portals/portal1.mdl" );
CBaseEntity::PrecacheModel( "models/portals/portal2.mdl" );
}
// called by ClientKill and DeadThink
void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
{
if (gpGlobals->coop || gpGlobals->deathmatch)
{
if ( fCopyCorpse )
{
// make a copy of the dead body for appearances sake
((CPortal_Player *)pEdict)->CreateCorpse();
}
// respawn player
pEdict->Spawn();
}
else
{ // restart the entire server
engine->ServerCommand("reload\n");
}
}
void GameStartFrame( void )
{
VPROF("GameStartFrame()");
if ( g_fGameOver )
return;
gpGlobals->teamplay = (teamplay.GetInt() != 0);
}
//=========================================================
// instantiate the proper game rules object
//=========================================================
void InstallGameRules()
{
CreateGameRulesObject( "CPortalMPGameRules" );
}
@@ -0,0 +1,486 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "portal_physics_collisionevent.h"
#include "physicsshadowclone.h"
#include "prop_combine_ball.h"
#include "prop_portal.h"
#include "portal_player.h"
#include "portal/weapon_physcannon.h" //grab controller
int CPortal_CollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
{
if ( !pGameData0 || !pGameData1 )
return 1;
AssertOnce( pObj0 && pObj1 );
bool bShadowClonesInvolved = ((pObj0->GetGameFlags() | pObj1->GetGameFlags()) & FVPHYSICS_IS_SHADOWCLONE) != 0;
if( bShadowClonesInvolved )
{
//at least one shadow clone
if( (pObj0->GetGameFlags() & pObj1->GetGameFlags()) & FVPHYSICS_IS_SHADOWCLONE )
return 0; //both are shadow clones
if( (pObj0->GetGameFlags() | pObj1->GetGameFlags()) & 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
CBaseEntity *pEntities[2] = { (CBaseEntity *)pGameData0, (CBaseEntity *)pGameData1 };
IPhysicsObject *pPhysObjects[2] = { pObj0, pObj1 };
bool bStatic[2] = { pObj0->IsStatic(), pObj1->IsStatic() };
CPortalSimulator *pSimulators[2];
for( int i = 0; i != 2; ++i )
pSimulators[i] = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntities[i] );
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;
#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( pSource );
Assert( (pSimulators[i]->m_DataAccess.Simulation.Dynamic.EntFlags[pClone->entindex()] & PSEF_IS_IN_PORTAL_HOLE) == (pSourceSimulator->m_DataAccess.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]->m_DataAccess.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]->m_DataAccess.Simulation.Dynamic.EntFlags[pEntities[0]->entindex()] |
pSimulators[1]->m_DataAccess.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 && (pSimulator_Static != pSimulator_Entity) )
return 0; //static collideable is from a different simulator
}
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]->m_DataAccess.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 );
}
int CPortal_CollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
{
if( (pGameData0 == NULL) || (pGameData1 == NULL) )
return 0;
if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( (CBaseEntity *)pGameData0 ) ||
CPSCollisionEntity::IsPortalSimulatorCollisionEntity( (CBaseEntity *)pGameData1 ) )
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 )
{
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 ( dynamic_cast<CPropCombineBall *>(pUnshadowedEntities[j]) != NULL && pUnshadowedEntities[i] != NULL )
{
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( dynamic_cast<CPropCombineBall *>(pUnshadowedEntities[j]) != NULL )
{
// [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 );
}
@@ -0,0 +1,32 @@
//========= Copyright 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 );
};
#endif //#ifndef PORTAL_PHYSICS_COLLISIONEVENT_H
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef PORTAL_PLACEMENT_H
#define PORTAL_PLACEMENT_H
#ifdef _WIN32
#pragma once
#endif
struct CPortalCornerFitData;
bool FitPortalOnSurface( const CProp_Portal *pIgnorePortal, Vector &vOrigin, const Vector &vForward, const Vector &vRight,
const Vector &vTopEdge, const Vector &vBottomEdge, const Vector &vRightEdge, const Vector &vLeftEdge,
int iPlacedBy, ITraceFilter *pTraceFilterPortalShot,
int iRecursions = 0, const CPortalCornerFitData *pPortalCornerFitData = 0, const int *p_piIntersectionIndex = 0, const int *piIntersectionCount = 0 );
bool IsPortalIntersectingNoPortalVolume( const Vector &vOrigin, const QAngle &qAngles, const Vector &vForward );
bool IsPortalOverlappingOtherPortals( const CProp_Portal *pIgnorePortal, const Vector &vOrigin, const QAngle &qAngles, bool bFizzle = false );
float VerifyPortalPlacement( const CProp_Portal *pIgnorePortal, Vector &vOrigin, QAngle &qAngles, int iPlacedBy, bool bTest = false );
#endif // PORTAL_PLACEMENT_H
File diff suppressed because it is too large Load Diff
+253
View File
@@ -0,0 +1,253 @@
//========= Copyright 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 "hl2_playerlocaldata.h"
#include "hl2_player.h"
#include "simtimer.h"
#include "soundenvelope.h"
#include "portal_player_shared.h"
#include "prop_portal.h"
#include "weapon_portalbase.h"
#include "in_buttons.h"
#include "func_liquidportal.h"
#include "ai_speech.h" // For expresser host
struct PortalPlayerStatistics_t
{
int iNumPortalsPlaced;
int iNumStepsTaken;
float fNumSecondsTaken;
};
//=============================================================================
// >> Portal_Player
//=============================================================================
class CPortal_Player : public CAI_ExpresserHost<CHL2_Player>
{
public:
DECLARE_CLASS( CPortal_Player, CHL2_Player );
CPortal_Player();
~CPortal_Player( void );
static CPortal_Player *CreatePlayer( const char *className, edict_t *ed )
{
CPortal_Player::s_PlayerEdict = ed;
return (CPortal_Player*)CreateEntityByName( className );
}
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
virtual void Precache( void );
virtual void CreateSounds( void );
virtual void StopLoopingSounds( void );
virtual void Spawn( void );
virtual void OnRestore( void );
virtual void Activate( void );
virtual void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
virtual void PostThink( void );
virtual void PreThink( void );
virtual void PlayerDeathThink( void );
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 );
Activity TranslateTeamActivity( Activity ActToTranslate );
virtual void SetAnimation( PLAYER_ANIM playerAnim );
virtual CAI_Expresser* GetExpresser( void );
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 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 bool BumpWeapon( CBaseCombatWeapon *pWeapon );
virtual void ShutdownUseEntity( void );
virtual const Vector& WorldSpaceCenter( ) const;
virtual void VPhysicsShadowUpdate( IPhysicsObject *pPhysics );
//virtual bool StartReplayMode( float fDelay, float fDuration, int iEntity );
//virtual void StopReplayMode();
virtual void Event_Killed( const CTakeDamageInfo &info );
virtual void Jump( void );
bool UseFoundEntity( CBaseEntity *pUseEntity );
CBaseEntity* FindUseEntity( void );
CBaseEntity* FindUseEntityThroughPortal( 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 SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
virtual void UpdatePortalViewAreaBits( unsigned char *pvs, int pvssize );
bool ValidatePlayerModel( const char *pModel );
QAngle GetAnimEyeAngles( void ) { return m_angEyeAngles.Get(); }
Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL );
void CheatImpulseCommands( int iImpulse );
void CreateRagdollEntity( const CTakeDamageInfo &info );
void GiveAllItems( void );
void GiveDefaultItems( void );
void NoteWeaponFired( void );
void ResetAnimation( void );
void SetPlayerModel( void );
void UpdateExpression ( void );
void ClearExpression ( void );
int GetPlayerModelType( void ) { return m_iPlayerSoundType; }
void ForceDuckThisFrame( void );
void UnDuck ( void );
inline void ForceJumpThisFrame( void ) { ForceButtons( IN_JUMP ); }
void DoAnimationEvent( PlayerAnimEvent_t event, int nData );
void SetupBones( matrix3x4_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; }
CProp_Portal *GetHeldObjectPortal( void ) { return m_pHeldObjectPortal; }
void SetHeldObjectPortal( CProp_Portal *pPortal ) { m_pHeldObjectPortal = pPortal; }
void SetStuckOnPortalCollisionObject( void ) { m_bStuckOnPortalCollisionObject = true; }
CWeaponPortalBase* GetActivePortalWeapon() const;
void IncrementPortalsPlaced( void );
void IncrementStepsTaken( 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; }
void SetNeuroToxinDamageTime( float fCountdownSeconds ) { m_fNeuroToxinDamageTime = gpGlobals->curtime + fCountdownSeconds; }
void IncNumCamerasDetatched( void ) { ++m_iNumCamerasDetatched; }
int GetNumCamerasDetatched( void ) const { return m_iNumCamerasDetatched; }
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
void SuppressCrosshair( bool bState ) { m_bSuppressingCrosshair = bState; }
private:
virtual CAI_Expresser* CreateExpresser( void );
CSoundPatch *m_pWooshSound;
CNetworkQAngle( m_angEyeAngles );
CPortalPlayerAnimState* m_PlayerAnimState;
int m_iLastWeaponFireUsercmd;
CNetworkVar( int, m_iSpawnInterpCounter );
CNetworkVar( int, m_iPlayerSoundType );
CNetworkVar( bool, m_bSuppressingCrosshair );
CNetworkVar( bool, m_bHeldObjectOnOppositeSideOfPortal );
CNetworkHandle( CProp_Portal, m_pHeldObjectPortal ); // networked entity handle
bool m_bIntersectingPortalPlane;
bool m_bStuckOnPortalCollisionObject;
float m_fTimeLastHurt;
bool m_bIsRegenerating; // Is the player currently regaining health
float m_fNeuroToxinDamageTime;
PortalPlayerStatistics_t m_StatsThisLevel;
float m_fTimeLastNumSecondsUpdate;
int m_iNumCamerasDetatched;
QAngle m_qPrePortalledViewAngles;
bool m_bFixEyeAnglesFromPortalling;
VMatrix m_matLastPortalled;
CAI_Expresser *m_pExpresser;
string_t m_iszExpressionScene;
EHANDLE m_hExpressionSceneEnt;
float m_flExpressionLoopTime;
mutable Vector m_vWorldSpaceCenterHolder; //WorldSpaceCenter() returns a reference, need an actual value somewhere
public:
CNetworkVar( bool, m_bPitchReorientation );
CNetworkHandle( CProp_Portal, m_hPortalEnvironment ); //if the player is in a portal environment, this is the associated portal
CNetworkHandle( CFunc_LiquidPortal, m_hSurroundingLiquidPortal ); //if the player is standing in a liquid portal, this will point to it
friend class CProp_Portal;
#ifdef PORTAL_MP
public:
virtual CBaseEntity* EntSelectSpawnPoint( void );
void PickTeam( void );
#endif
};
inline CPortal_Player *ToPortalPlayer( CBaseEntity *pEntity )
{
if ( !pEntity || !pEntity->IsPlayer() )
return NULL;
return dynamic_cast<CPortal_Player*>( pEntity );
}
inline CPortal_Player *GetPortalPlayer( int iPlayerIndex )
{
return static_cast<CPortal_Player*>( UTIL_PlayerByIndex( iPlayerIndex ) );
}
#endif //PORTAL_PLAYER_H
+618
View File
@@ -0,0 +1,618 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
//=====================================================================================//
#include "cbase.h" // for pch
#include "props.h"
#include "filters.h"
#include "achievementmgr.h"
extern CAchievementMgr g_AchievementMgrPortal;
#define RADIO_MODEL_NAME "models/props/radio_reference.mdl"
//#define RADIO_DEBUG_SERVER
class CDinosaurSignal : public CBaseEntity
{
public:
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
DECLARE_CLASS( CDinosaurSignal, CBaseEntity );
void Spawn();
int UpdateTransmitState();
#if RADIO_DEBUG_SERVER
int DrawDebugTextOverlays( void );
#endif
CNetworkString( m_szSoundName, 128 );
CNetworkVar( float, m_flInnerRadius );
CNetworkVar( float, m_flOuterRadius );
CNetworkVar( int, m_nSignalID );
};
LINK_ENTITY_TO_CLASS( updateitem1, CDinosaurSignal );
BEGIN_DATADESC( CDinosaurSignal )
DEFINE_AUTO_ARRAY( m_szSoundName, FIELD_CHARACTER ),
DEFINE_FIELD( m_flOuterRadius, FIELD_FLOAT ),
DEFINE_FIELD( m_flInnerRadius, FIELD_FLOAT ),
DEFINE_FIELD( m_nSignalID, FIELD_INTEGER ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CDinosaurSignal, DT_DinosaurSignal )
SendPropString( SENDINFO(m_szSoundName) ),
SendPropFloat( SENDINFO(m_flOuterRadius) ),
SendPropFloat( SENDINFO(m_flInnerRadius) ),
SendPropInt( SENDINFO(m_nSignalID) ),
END_SEND_TABLE()
void CDinosaurSignal::Spawn()
{
PrecacheScriptSound( m_szSoundName.Get() );
BaseClass::Spawn();
SetTransmitState( FL_EDICT_ALWAYS );
}
int CDinosaurSignal::UpdateTransmitState()
{
// ALWAYS transmit to all clients.
return SetTransmitState( FL_EDICT_ALWAYS );
}
#if RADIO_DEBUG_SERVER
int CDinosaurSignal::DrawDebugTextOverlays( void )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
NDebugOverlay::Sphere( GetAbsOrigin(), GetAbsAngles(), m_flInnerRadius, 255, 0, 0, 64, false, 0.1f );
NDebugOverlay::Sphere( GetAbsOrigin(), GetAbsAngles(), m_flOuterRadius, 0, 255, 0, 64, false, 0.1f );
}
return text_offset;
}
#endif
class CPortal_Dinosaur : public CPhysicsProp
{
public:
DECLARE_CLASS( CPortal_Dinosaur, CPhysicsProp );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void Spawn();
virtual void Precache();
virtual QAngle PreferredCarryAngles( void ) { return QAngle( 0, 180, 0 ); }
virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; }
virtual void Activate();
CNetworkHandle( CDinosaurSignal, m_hDinosaur_Signal );
CNetworkVar( bool, m_bAlreadyDiscovered );
};
LINK_ENTITY_TO_CLASS( updateitem2, CPortal_Dinosaur );
BEGIN_DATADESC( CPortal_Dinosaur )
DEFINE_FIELD( m_hDinosaur_Signal, FIELD_EHANDLE ),
DEFINE_FIELD( m_bAlreadyDiscovered, FIELD_BOOLEAN ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CPortal_Dinosaur, DT_PropDinosaur )
SendPropEHandle( SENDINFO( m_hDinosaur_Signal ) ),
SendPropBool( SENDINFO( m_bAlreadyDiscovered ) ),
END_SEND_TABLE()
void CPortal_Dinosaur::Precache()
{
PrecacheModel( RADIO_MODEL_NAME );
PrecacheScriptSound( "Portal.room1_radio" );
PrecacheScriptSound( "UpdateItem.Static" );
PrecacheScriptSound( "UpdateItem.Dinosaur01" );
PrecacheScriptSound( "UpdateItem.Dinosaur02" );
PrecacheScriptSound( "UpdateItem.Dinosaur03" );
PrecacheScriptSound( "UpdateItem.Dinosaur04" );
PrecacheScriptSound( "UpdateItem.Dinosaur05" );
PrecacheScriptSound( "UpdateItem.Dinosaur06" );
PrecacheScriptSound( "UpdateItem.Dinosaur07" );
PrecacheScriptSound( "UpdateItem.Dinosaur08" );
PrecacheScriptSound( "UpdateItem.Dinosaur09" );
PrecacheScriptSound( "UpdateItem.Dinosaur10" );
PrecacheScriptSound( "UpdateItem.Dinosaur11" );
PrecacheScriptSound( "UpdateItem.Dinosaur12" );
PrecacheScriptSound( "UpdateItem.Dinosaur13" );
PrecacheScriptSound( "UpdateItem.Dinosaur14" );
PrecacheScriptSound( "UpdateItem.Dinosaur15" );
PrecacheScriptSound( "UpdateItem.Dinosaur16" );
PrecacheScriptSound( "UpdateItem.Dinosaur17" );
PrecacheScriptSound( "UpdateItem.Dinosaur18" );
PrecacheScriptSound( "UpdateItem.Dinosaur19" );
PrecacheScriptSound( "UpdateItem.Dinosaur20" );
PrecacheScriptSound( "UpdateItem.Dinosaur21" );
PrecacheScriptSound( "UpdateItem.Dinosaur22" );
PrecacheScriptSound( "UpdateItem.Dinosaur23" );
PrecacheScriptSound( "UpdateItem.Dinosaur24" );
PrecacheScriptSound( "UpdateItem.Dinosaur25" );
PrecacheScriptSound( "UpdateItem.Dinosaur26" );
PrecacheScriptSound( "UpdateItem.Fizzle" );
}
void CPortal_Dinosaur::Spawn()
{
Precache();
KeyValue( "model", RADIO_MODEL_NAME );
m_spawnflags |= SF_PHYSPROP_START_ASLEEP;
BaseClass::Spawn();
}
void CPortal_Dinosaur::Activate( void )
{
// Find the current completion status of the dinosaurs
uint64 fStateFlags = 0;
CBaseAchievement *pTransmissionRecvd = dynamic_cast<CBaseAchievement *>(g_AchievementMgrPortal.GetAchievementByName("PORTAL_TRANSMISSION_RECEIVED"));
if ( pTransmissionRecvd )
{
fStateFlags = pTransmissionRecvd->GetComponentBits();
}
if ( m_hDinosaur_Signal != NULL )
{
uint64 nId = m_hDinosaur_Signal.Get()->m_nSignalID;
// See if we're already tripped
if ( fStateFlags & ((uint64)1<<nId) )
{
m_bAlreadyDiscovered = true;
}
}
BaseClass::Activate();
}
struct radiolocs
{
const char *mapname;
const char *soundname;
int id;
float radiopos[3];
float radioang[3];
float soundpos[3];
float soundouterrad;
float soundinnerrad;
};
static const radiolocs s_radiolocs[] =
{
{
"testchmb_a_00",
"UpdateItem.Dinosaur01",
0,
{ 0, 0, 0 },
{ 0, 0, 0 },
{ -506, -924, 161 },
200,
64
},
{
"testchmb_a_00",
"UpdateItem.Dinosaur02",
1,
{ -960, -634, 783 },
{ 0, 90, 0 },
{ -926.435, -256.323, 583 },
200,
64,
},
{
"testchmb_a_01",
"UpdateItem.Dinosaur03",
2,
{ 233, 393, 130 },
{ 0, 225, 0 },
{ 96, 160, -108 },
224,
128,
},
{
"testchmb_a_01",
"UpdateItem.Dinosaur04",
3,
{ -1439.89, 1076.04, 779.102 },
{ 0, 270, 0 },
{ -731, 735, 888 },
400,
64,
},
{
// new entry
"testchmb_a_02",
"UpdateItem.Dinosaur05",
4,
{ 2, 65, 390 },
{ 0, 270, 0 },
{ -864, 192, 64},
192,
96,
},
{
"testchmb_a_02",
"UpdateItem.Dinosaur06",
21,
{ 111, 832, 577 },
{ 0, 0, 0 },
{ 918, 831, 512},
192,
96,
},
{
"testchmb_a_03",
"UpdateItem.Dinosaur07",
5,
{ -53.2337, 78.181, 236 },
{ 0, 225, 0 },
{ 304, 0, -96 },
256,
128
},
// new entry
{
"testchmb_a_03",
"UpdateItem.Dinosaur08",
6,
{ 428.112, 0.22326, 1201 },
{ 0, 180, 0 },
{ -581.096, 193.694, 1351 },
165,
128
},
{
"testchmb_a_04",
"UpdateItem.Dinosaur09",
7,
{ 118, -56.6, -38.8 },
{ 0, 180, 0 },
{ -640, 256, 8 },
512,
128
},
// new entry
{
"testchmb_a_05",
"UpdateItem.Dinosaur10",
8,
{ 64, 144, 160 },
{ 0, 270, 0 },
{ 64, 740, 7 },
350,
128
},
{
"testchmb_a_06",
"UpdateItem.Dinosaur11",
9,
{ 529, 315, 320 },
{ 0, 270, 0 },
{ 608, 128, -184 },
384,
160
},
{
"testchmb_a_07",
"UpdateItem.Dinosaur12",
10,
{ 192, -1546, 1425 },
{ 0, 113, 0 },
{ 272, -496, 1328 },
432,
88
},
// new entry
{
"testchmb_a_07",
"UpdateItem.Dinosaur13",
11,
{ -144, -768, 256 },
{ 0, 90, 0 },
{ -192, -384, 176 },
256,
128
},
{
"testchmb_a_08",
"UpdateItem.Dinosaur14",
12,
{ 267, -378, 256 },
{ 0, 90, 0 },
{ -560, 96, 320 },
288,
128,
},
{
"testchmb_a_09",
"UpdateItem.Dinosaur15",
13,
{ 634, 1308, 256 },
{ 0, 180, 0 },
{ 386.699, 1792.43, 7},
548,
64
},
{
"testchmb_a_10",
"UpdateItem.Dinosaur16",
14,
{ -1420, -2752, 76 },
{ 0, 0, 0 },
{ -1968, -2880, -334 },
448,
196,
},
// new entry
{
"testchmb_a_10",
"UpdateItem.Dinosaur17",
15,
{ 112, 1392, -63 },
{ 0, 260, 0 },
{ -189, 1220, 65 },
192,
128,
},
{
"testchmb_a_11",
"UpdateItem.Dinosaur18",
16,
{0,0,0},
{0,0,0},
{-512,644,64},
192,
96,
},
{
"testchmb_a_13",
"UpdateItem.Dinosaur19",
17,
{955,931,-267},
{-90,0,0},
{1472,-191,-12},
256,
128,
},
{
"testchmb_a_14",
"UpdateItem.Dinosaur20",
18,
{0,0,0},
{0,0,0},
{144,192,1288},
807,
128,
},
{
"testchmb_a_14",
"UpdateItem.Dinosaur21",
22,
{1285, 1344, 1412},
{0,0,0},
{2712, 894, 1011},
200,
120,
},
{
"testchmb_a_14",
"UpdateItem.Dinosaur22",
23,
{-952, 336, -256},
{0,0,0},
{-1144, -249, 3336},
400,
128,
},
{
"testchmb_a_15",
"UpdateItem.Dinosaur23",
19,
{-1529,293,-283},
{0,90,0},
{761,443,810},
256,
128,
},
{
"escape_00",
"UpdateItem.Dinosaur24",
24,
{192, -1344, -832},
{0, 135, 0},
{891, 322, -184},
285,
150,
},
{
"escape_01",
"UpdateItem.Dinosaur25",
20,
{0,0,0},
{0,0,0},
{-624, 1440, -464},
512,
128,
},
{
"escape_02",
"UpdateItem.Dinosaur26",
25,
{5504, 131, -1422},
{0, 90, 0},
{4218, 674, 8},
300,
100,
},
};
class CSpawnDinosaurHack : CAutoGameSystem
{
public:
virtual void LevelInitPreEntity();
virtual void LevelInitPostEntity();
CPortal_Dinosaur *SpawnDinosaur( radiolocs& loc );
CDinosaurSignal *SpawnSignal( radiolocs& loc );
void ApplyMapSpecificHacks();
};
static CSpawnDinosaurHack g_SpawnRadioHack;
void CSpawnDinosaurHack::LevelInitPreEntity()
{
UTIL_PrecacheOther( "updateitem2", RADIO_MODEL_NAME );
ApplyMapSpecificHacks();
}
// Spawn all the Dinosaurs and sstv images
void CSpawnDinosaurHack::LevelInitPostEntity()
{
if ( gpGlobals->eLoadType == MapLoad_LoadGame )
{
#if defined ( RADIO_DEBUG_SERVER )
Msg( "Not spawning any Dinosaurs: Detected a map load\n" );
#endif
return;
}
IAchievement *pHeartbreaker = g_AchievementMgrPortal.GetAchievementByName("PORTAL_BEAT_GAME");
if ( pHeartbreaker == NULL || pHeartbreaker->IsAchieved() == false )
{
#if defined ( RADIO_DEBUG_SERVER )
Msg( "Not spawning any Dinosaurs: Player has not beat the game, or failed to get heartbreaker achievement from mgr\n" );
#endif
return;
}
for ( int i = 0; i < ARRAYSIZE( s_radiolocs ); ++i )
{
radiolocs loc = s_radiolocs[i];
if ( V_strcmp( STRING(gpGlobals->mapname), loc.mapname ) == 0 )
{
#if defined ( RADIO_DEBUG_SERVER )
Msg( "Found Dinosaur and signal info for %s, spawning.\n", loc.mapname );
Msg( "Dinosaur pos: %f %f %f, ang: %f %f %f\n", loc.radiopos[0], loc.radiopos[1], loc.radiopos[2], loc.radioang[0], loc.radioang[1], loc.radioang[2] );
Msg( "Signal pos: %f %f %f, inner rad: %f, outter rad: %f\n", loc.soundpos[0], loc.soundpos[1], loc.soundpos[2], loc.soundinnerrad, loc.soundouterrad );
#endif
CPortal_Dinosaur *pDinosaur = SpawnDinosaur( loc );
CDinosaurSignal *pSignal = SpawnSignal( loc );
Assert ( pDinosaur && pSignal );
if ( pDinosaur && pSignal )
{
#if defined ( RADIO_DEBUG_SERVER )
Msg( "SUCCESS: Spawned Dinosaur and signal and linked them.\n" );
#endif
// OK, so these really could have been the same class... not worth changing it now though.
pDinosaur->m_hDinosaur_Signal.Set( pSignal );
pDinosaur->Activate();
}
}
}
}
CPortal_Dinosaur *CSpawnDinosaurHack::SpawnDinosaur( radiolocs& loc )
{
Vector vSpawnPos ( loc.radiopos[0], loc.radiopos[1], loc.radiopos[2] );
QAngle vSpawnAng ( loc.radioang[0], loc.radioang[1], loc.radioang[2] );
// origin and angles of zero means skip this Dinosaur creation and look for an existing radio
if ( loc.radiopos[0] == 0 &&
loc.radiopos[1] == 0 &&
loc.radiopos[2] == 0 &&
loc.radioang[0] == 0 &&
loc.radioang[1] == 0 &&
loc.radioang[2] == 0 )
{
#if defined ( RADIO_DEBUG_SERVER )
Msg( "Dinosaur found with zero angles and origin. Replacing existing radio.\n" );
#endif
// Find existing Dinosaur, kill it and spawn at its position
CPhysicsProp *pOldDinosaur = (CPhysicsProp*)gEntList.FindEntityByClassname( NULL, "prop_physics" );
while ( pOldDinosaur )
{
if ( V_strcmp( STRING( pOldDinosaur->GetModelName() ), RADIO_MODEL_NAME ) == 0 )
{
vSpawnPos = pOldDinosaur->GetAbsOrigin();
vSpawnAng = pOldDinosaur->GetAbsAngles();
UTIL_Remove( pOldDinosaur );
#if defined ( RADIO_DEBUG_SERVER )
Msg( "Found Dinosaur exiting in level, replacing with %f, %f %f and %f %f %f.\n", XYZ(vSpawnPos), XYZ(vSpawnAng) );
#endif
break;
}
pOldDinosaur = (CPhysicsProp*)gEntList.FindEntityByClassname( pOldDinosaur, "prop_physics" );
}
}
Assert( vSpawnPos != vec3_origin );
CPortal_Dinosaur *pDinosaur = (CPortal_Dinosaur*)CreateEntityByName( "updateitem2" );
Assert ( pDinosaur );
if ( pDinosaur )
{
pDinosaur->SetAbsOrigin( vSpawnPos );
pDinosaur->SetAbsAngles( vSpawnAng );
DispatchSpawn( pDinosaur );
}
return pDinosaur;
}
CDinosaurSignal *CSpawnDinosaurHack::SpawnSignal( radiolocs& loc )
{
CDinosaurSignal *pSignal = (CDinosaurSignal*)CreateEntityByName( "updateitem1" );
Assert ( pSignal );
if ( pSignal )
{
#if defined ( RADIO_DEBUG_SERVER )
if ( loc.soundinnerrad > loc.soundouterrad )
{
Assert( 0 );
Warning( "Dinosaur BUG: Inner radius is greater than outer radius. Will swap them.\n" );
swap( loc.soundinnerrad, loc.soundouterrad );
}
#endif
pSignal->SetAbsOrigin( Vector( loc.soundpos[0], loc.soundpos[1], loc.soundpos[2] ) );
pSignal->m_flInnerRadius = loc.soundinnerrad;
pSignal->m_flOuterRadius = loc.soundouterrad;
V_strncpy( pSignal->m_szSoundName.GetForModify(), loc.soundname, 128 );
pSignal->m_nSignalID = loc.id;
DispatchSpawn( pSignal );
}
return pSignal;
}
void CSpawnDinosaurHack::ApplyMapSpecificHacks()
{
if ( V_strcmp( STRING(gpGlobals->mapname), "testchmb_a_02" ) == 0 )
{
CBaseEntity *pFilter = CreateEntityByName( "filter_activator_name" );
Assert( pFilter );
if ( pFilter )
{
pFilter->KeyValue( "filtername", "box_2" );
pFilter->KeyValue( "targetname", "filter_weight_box" );
DispatchSpawn( pFilter );
}
}
}
+620
View File
@@ -0,0 +1,620 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a combine ball and a combine ball launcher which have certain properties
// overwritten to make use of them in portal game play.
//
//=====================================================================================//
#include "cbase.h" // for pch
#include "prop_combine_ball.h" // for base class
#include "te_effect_dispatch.h" // for the explosion/impact effects
#include "prop_portal.h" // Special case code for passing through portals. We need the class definition.
#include "soundenvelope.h"
#include "physicsshadowclone.h"
// resource file names
#define IMPACT_DECAL_NAME "decals/smscorch1model"
// context think
#define UPDATE_THINK_CONTEXT "UpdateThinkContext"
class CPropEnergyBall : public CPropCombineBall
{
public:
DECLARE_CLASS( CPropEnergyBall, CPropCombineBall );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void Precache();
virtual void CreateSounds( void );
virtual void StopLoopingSounds( void );
virtual void Spawn();
virtual void Activate( void );
// Overload for unlimited bounces and predictable movement
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
// Overload for less sound, no shake.
virtual void ExplodeThink( void );
// Update in a time till death update
virtual void Think ( void );
virtual void EndTouch( CBaseEntity *pOther );
virtual void StartTouch( CBaseEntity *pOther );
virtual void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
CHandle<CProp_Portal> m_hTouchedPortal; // Pointer to the portal we are touched most recently
bool m_bTouchingPortal1; // Are we touching portal 1
bool m_bTouchingPortal2; // Are we touching portal 2
// Remember the last known direction of travel, incase our velocity is cleared.
Vector m_vLastKnownDirection;
// After portal teleports, we force the life to be at least this number.
float m_fMinLifeAfterPortal;
CNetworkVar( bool, m_bIsInfiniteLife );
CNetworkVar( float, m_fTimeTillDeath );
CSoundPatch *m_pAmbientSound;
};
LINK_ENTITY_TO_CLASS( prop_energy_ball, CPropEnergyBall );
BEGIN_DATADESC( CPropEnergyBall )
DEFINE_FIELD( m_hTouchedPortal, FIELD_EHANDLE ),
DEFINE_FIELD( m_bTouchingPortal1, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bTouchingPortal2, FIELD_BOOLEAN ),
DEFINE_FIELD( m_vLastKnownDirection, FIELD_VECTOR ),
DEFINE_FIELD( m_fMinLifeAfterPortal, FIELD_FLOAT ),
DEFINE_FIELD( m_bIsInfiniteLife, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fTimeTillDeath, FIELD_FLOAT ),
DEFINE_SOUNDPATCH( m_pAmbientSound ),
DEFINE_THINKFUNC( Think ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CPropEnergyBall, DT_PropEnergyBall )
SendPropBool( SENDINFO( m_bIsInfiniteLife ) ),
SendPropFloat ( SENDINFO( m_fTimeTillDeath ) ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose: Precache
// Input : -
//-----------------------------------------------------------------------------
void CPropEnergyBall::Precache()
{
BaseClass::Precache();
PrecacheScriptSound( "EnergyBall.Explosion" );
PrecacheScriptSound( "EnergyBall.Launch" );
PrecacheScriptSound( "EnergyBall.Impact" );
PrecacheScriptSound( "EnergyBall.AmbientLoop" );
UTIL_PrecacheDecal( IMPACT_DECAL_NAME, false );
}
void CPropEnergyBall::CreateSounds()
{
if (!m_pAmbientSound)
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
CPASAttenuationFilter filter( this );
m_pAmbientSound = controller.SoundCreate( filter, entindex(), "EnergyBall.AmbientLoop" );
controller.Play( m_pAmbientSound, 1.0, 100 );
}
}
void CPropEnergyBall::StopLoopingSounds()
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundDestroy( m_pAmbientSound );
m_pAmbientSound = NULL;
BaseClass::StopLoopingSounds();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
void CPropEnergyBall::Spawn()
{
Precache();
BaseClass::Spawn();
m_bTouchingPortal1 = false;
m_bTouchingPortal2 = false;
m_bIsInfiniteLife = false;
m_fTimeTillDeath = -1;
m_fMinLifeAfterPortal = 5;
// Init last known direction to our initial direction
GetVelocity( &m_vLastKnownDirection, NULL );
}
void CPropEnergyBall::Activate( void )
{
BaseClass::Activate();
CreateSounds();
}
//-----------------------------------------------------------------------------
// Purpose: Keep a constant velocity despite collisions, make impact sounds and effects
//-----------------------------------------------------------------------------
void CPropEnergyBall::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
// Skip combine ball's collision, but do everything below it.
BaseClass::BaseClass::VPhysicsCollision( index, pEvent );
Vector preVelocity = pEvent->preVelocity[index];
// float flSpeed = VectorNormalize( preVelocity );
// It's ok to change direction, but maintain speed = m_flSpeed.
Vector vecFinalVelocity = pEvent->postVelocity[index];
VectorNormalize( vecFinalVelocity );
if ( m_bTouchingPortal2 || m_bTouchingPortal1 )
{
AssertMsg ( m_hTouchedPortal.Get(), "Touching a portal, but recorded an invalid handle." );
}
// Used for deciding if we play our impact effects/sounds
bool bIsEnteringPortalAndLockingAxisForward = false;
// Fixed bounce axis when in a portal environment
if ( (m_bTouchingPortal2 || m_bTouchingPortal1) && m_hTouchedPortal.Get() )
{
// Force our velocity to be either towards or away from the portal, no bouncing at odd angles allowed
CProp_Portal* pPortal = m_hTouchedPortal.Get();
// Only lock to the portal's forward axis if we're in it's world bounds
// We use a tolerance of four, because the render bounds thickness for a portal is 4, and this function
// intersects with a plane.
bool bHitPortal = UTIL_IsBoxIntersectingPortal( GetAbsOrigin(), WorldAlignSize(), pPortal, 4.0f );
// We definitely hit a portal
if ( bHitPortal && pPortal && pPortal->IsActivedAndLinked() )
{
Vector vecTouchedPortalFace;
pPortal->GetVectors( &vecTouchedPortalFace, NULL, NULL );
vecTouchedPortalFace.NormalizeInPlace();
float fDot = vecTouchedPortalFace.Dot( vecFinalVelocity );
// closer to 'towards' the portal, force it to go that direction
if ( fDot < 0 )
{
vecFinalVelocity = -vecTouchedPortalFace;
// Since we're going 'through', don't do surfaceprop based collision effects
// because the it will look like we didn't hit anything.
pEvent->surfaceProps[0] = pEvent->surfaceProps[1] = physprops->GetSurfaceIndex( "default" );
bIsEnteringPortalAndLockingAxisForward = true;
}
else // Closer to 'away from' the portal. Force the energy ball to go that direction
{
vecFinalVelocity = vecTouchedPortalFace;
}
}
}
// Plant a decal on any solid brushes we hit
if ( !bIsEnteringPortalAndLockingAxisForward )
{
trace_t tr;
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + 60*preVelocity, MASK_SHOT,
this, COLLISION_GROUP_NONE, &tr);
// Only place decals and draw effects if we hit something valid
if ( tr.m_pEnt )
{
// Cball impact effect (using same trace as the decal placement above)
CEffectData data;
data.m_flRadius = 16;
data.m_vNormal = tr.plane.normal;
data.m_vOrigin = tr.endpos + tr.plane.normal * 1.0f;
DispatchEffect( "cball_bounce", data );
if ( tr.m_pEnt )
{
UTIL_DecalTrace( &tr, "EnergyBall.Impact" );
}
}
EmitSound( "EnergyBall.Impact" );
}
// Record our direction so our fixed direction hacks know we have changed direction immediately
m_vLastKnownDirection = vecFinalVelocity;
// Scale new velocity to our fixed speed
vecFinalVelocity *= GetSpeed();
// Try to update the velocity now, however I'm told this rarely works.
// We will spam updates in our think function to help get us in the direction we want to go.
PhysCallbackSetVelocity( pEvent->pObjects[index], vecFinalVelocity );
}
void CPropEnergyBall::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
{
// On teleport, we record a pointer to the portal we are arriving at
if ( eventType == NOTIFY_EVENT_TELEPORT )
{
CProp_Portal *pEnteredPortal = dynamic_cast<CProp_Portal*>( pNotify );
if( pEnteredPortal )
{
m_vLastKnownDirection = pEnteredPortal->m_matrixThisToLinked.ApplyRotation( m_vLastKnownDirection );
m_vLastKnownDirection.NormalizeInPlace();
IPhysicsObject *pPhysObject = VPhysicsGetObject();
if( pPhysObject )
{
Vector vNewVelocity = m_vLastKnownDirection * GetSpeed();
pPhysObject->SetVelocityInstantaneous( &vNewVelocity, NULL );
}
// Record the new portal for the purposes of locking our movement
m_hTouchedPortal = pEnteredPortal->m_hLinkedPortal;
}
else
{
m_hTouchedPortal = NULL;
}
// If an energy ball passes a portal (teleports), add a make sure its life is >= sk_energy_ball_min_life_after_portal
float fCurTimeTillDeath = GetNextThink( "ExplodeTimerContext" );
// If we are set to die, then refresh that time if it is below a set threshold
if ( fCurTimeTillDeath > 0 )
{
float fTimeLeft = fCurTimeTillDeath - gpGlobals->curtime;
float fMinLife = m_fMinLifeAfterPortal;
float fTimeToDie = (fTimeLeft > fMinLife) ? (fTimeLeft) : (fMinLife);
SetContextThink( &CPropCombineBall::ExplodeThink, gpGlobals->curtime + fTimeToDie, "ExplodeTimerContext" );
}
}
//BaseClass::NotifySystemEvent( pNotify, eventType, params );
}
//-----------------------------------------------------------------------------
// Purpose: Send down the time till death to the client code to help indicate when the ball will detonate
// Input : -
//-----------------------------------------------------------------------------
void CPropEnergyBall::Think()
{
// Finite life energy balls send the time till death down to the client for display purposes
if ( !m_bIsInfiniteLife )
{
m_fTimeTillDeath = GetNextThink( "ExplodeTimerContext" ) - gpGlobals->curtime;
SetNextThink ( gpGlobals->curtime + 0.5f );
}
// Force our movement to be at desired speed
IPhysicsObject* pMyObject = VPhysicsGetObject();
if ( pMyObject )
{
// get our current speed
Vector vCurVelocity, vNewVelocity;
pMyObject->GetVelocity( &vCurVelocity, NULL );
float fCurSpeed = vCurVelocity.Length();
if ( fCurSpeed < GetSpeed() )
{
m_vLastKnownDirection.NormalizeInPlace();
vNewVelocity = m_vLastKnownDirection * GetSpeed();
pMyObject->SetVelocityInstantaneous( &vNewVelocity, NULL );
}
}
SetNextThink( gpGlobals->curtime + 0.1 );
}
//-----------------------------------------------------------------------------
// Purpose: Make a sound/effect for the removal of the energy ball, and switch to the cleanup think
// Input : -
//-----------------------------------------------------------------------------
void CPropEnergyBall::ExplodeThink( )
{
// Tell the respawner to make a new one
if ( GetSpawner() )
{
GetSpawner()->RespawnBallPostExplosion();
}
//Destruction effect
CBroadcastRecipientFilter filter2;
CEffectData data;
data.m_vOrigin = GetAbsOrigin();
DispatchEffect( "ManhackSparks", data );
EmitSound( "EnergyBall.Explosion" );
// Turn us off and wait because we need our trails to finish up properly
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
AddSolidFlags( FSOLID_NOT_SOLID );
SetEmitState( false );
SetContextThink( &CPropCombineBall::SUB_Remove, gpGlobals->curtime + 0.5f, "RemoveContext" );
StopLoopingSounds();
}
void CPropEnergyBall::StartTouch( CBaseEntity *pOther )
{
Assert( pOther );
if( CPhysicsShadowClone::IsShadowClone( pOther ) )
{
CBaseEntity *pCloned = ((CPhysicsShadowClone *)pOther)->GetClonedEntity();
if( pCloned )
pOther = pCloned;
}
// Kill the player on hit.
if ( pOther->IsPlayer() )
{
CTakeDamageInfo info( this, GetOwnerEntity(), GetAbsVelocity(), GetAbsOrigin(), 1500.0f, DMG_DISSOLVE );
pOther->OnTakeDamage( info );
// Destruct when we hit the player
SetContextThink( &CPropCombineBall::ExplodeThink, gpGlobals->curtime, "ExplodeTimerContext" );
}
CProp_Portal* pPortal = dynamic_cast<CProp_Portal*>(pOther);
// If toucher is a prop portal
if ( pPortal )
{
// Record the touched portal for locking collision movements.
// The forward direction we want to follow is the forward vector of the portal we've touched most recently
m_hTouchedPortal = pPortal;
// record that we touched this portal
if ( pPortal->m_bIsPortal2 == false )
{
m_bTouchingPortal1 = true;
}
else //if ( pPortal->m_bIsPortal2 == true )
{
m_bTouchingPortal2 = true;
}
}
BaseClass::StartTouch( pOther );
}
void CPropEnergyBall::EndTouch( CBaseEntity *pOther )
{
CProp_Portal* pPortal = dynamic_cast<CProp_Portal*>(pOther);
if ( pPortal )
{
// We are no longer touching this portal
if ( pPortal->m_bIsPortal2 == false )
{
m_bTouchingPortal1 = false;
}
else //if ( pPortal->m_bIsPortal2 == true )
{
m_bTouchingPortal2 = false;
}
}
BaseClass::EndTouch( pOther );
}
class CEnergyBallLauncher : public CPointCombineBallLauncher
{
public:
DECLARE_CLASS( CEnergyBallLauncher, CPointCombineBallLauncher );
DECLARE_DATADESC();
virtual void SpawnBall();
virtual void Precache();
virtual void Spawn();
private:
float m_fBallLifetime;
float m_fMinBallLifeAfterPortal;
COutputEvent m_OnPostSpawnBall;
};
LINK_ENTITY_TO_CLASS( point_energy_ball_launcher, CEnergyBallLauncher );
BEGIN_DATADESC( CEnergyBallLauncher )
DEFINE_KEYFIELD( m_fBallLifetime, FIELD_FLOAT, "BallLifetime" ),
DEFINE_KEYFIELD( m_fMinBallLifeAfterPortal, FIELD_FLOAT, "MinLifeAfterPortal" ),
DEFINE_OUTPUT ( m_OnPostSpawnBall, "OnPostSpawnBall" ),
END_DATADESC()
void CEnergyBallLauncher::Precache()
{
BaseClass::Precache();
UTIL_PrecacheDecal( IMPACT_DECAL_NAME, false );
}
void CEnergyBallLauncher::Spawn()
{
Precache();
BaseClass::Spawn();
}
void CEnergyBallLauncher::SpawnBall()
{
CPropEnergyBall *pBall = static_cast<CPropEnergyBall*>( CreateEntityByName( "prop_energy_ball" ) );
if ( pBall == NULL )
return;
pBall->SetRadius( m_flBallRadius );
Vector vecAbsOrigin = GetAbsOrigin();
Vector zaxis;
pBall->SetAbsOrigin( vecAbsOrigin );
pBall->SetSpawner( this );
pBall->SetSpeed( m_flMaxSpeed );
float flSpeed = m_flMaxSpeed;
Vector vDirection;
QAngle qAngle = GetAbsAngles();
AngleVectors( qAngle, &vDirection, NULL, NULL );
vDirection *= flSpeed;
pBall->SetAbsVelocity( vDirection );
DispatchSpawn(pBall);
pBall->Activate();
pBall->SetState( CPropCombineBall::STATE_LAUNCHED );
pBall->SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
pBall->m_fMinLifeAfterPortal = m_fMinBallLifeAfterPortal;
// Additional setup of the physics object for energy ball uses
IPhysicsObject *pBallObj = pBall->VPhysicsGetObject();
if ( pBallObj )
{
// Make sure we dont use air drag
pBallObj->EnableDrag( false );
// Remove damping
float speed, rot;
speed = rot = 0.0f;
pBallObj->SetDamping( &speed, &rot );
// HUGE rotational inertia, don't allow the ball to have any spin
Vector vInertia( 1e30, 1e30, 1e30 );
pBallObj->SetInertia( vInertia );
// Low mass to let it bounce off of obstructions for certain puzzles.
pBallObj->SetMass( 1.0f );
}
// Only expire if the lifetme field is positive
if ( m_fBallLifetime >=0 )
{
pBall->StartLifetime( m_fBallLifetime );
pBall->m_bIsInfiniteLife = false;
}
else
{
pBall->m_bIsInfiniteLife = true;
}
// Think function, used to update time till death and avoid sleeping
pBall->SetNextThink ( gpGlobals->curtime + 0.1f );
EmitSound( "EnergyBall.Launch" );
m_OnPostSpawnBall.FireOutput( this, this );
}
static void fire_energy_ball_f( void )
{
if( sv_cheats->GetBool() == false ) //heavy handed version since setting the concommand with FCVAR_CHEATS isn't working like I thought
return;
CBasePlayer *pPlayer = (CBasePlayer *)UTIL_GetCommandClient();
Vector ptEyes, vForward;
ptEyes = pPlayer->EyePosition();
pPlayer->EyeVectors( &vForward );
{
CPropEnergyBall *pBall = static_cast<CPropEnergyBall*>( CreateEntityByName( "prop_energy_ball" ) );
if ( pBall == NULL )
return;
pBall->SetRadius( 12.0f );
pBall->SetAbsOrigin( ptEyes + (vForward * 50.0f) );
pBall->SetSpawner( NULL );
pBall->SetSpeed( 400.0f );
pBall->SetAbsVelocity( vForward * 400.0f );
DispatchSpawn(pBall);
pBall->Activate();
pBall->SetState( CPropCombineBall::STATE_LAUNCHED );
pBall->SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
pBall->m_fMinLifeAfterPortal = 5.0f;
// Additional setup of the physics object for energy ball uses
IPhysicsObject *pBallObj = pBall->VPhysicsGetObject();
if ( pBallObj )
{
// Make sure we dont use air drag
pBallObj->EnableDrag( false );
// Remove damping
float speed, rot;
speed = rot = 0.0f;
pBallObj->SetDamping( &speed, &rot );
// HUGE rotational inertia, don't allow the ball to have any spin
Vector vInertia( 1e30, 1e30, 1e30 );
pBallObj->SetInertia( vInertia );
// Low mass to let it bounce off of obstructions for certain puzzles.
pBallObj->SetMass( 1.0f );
}
pBall->StartLifetime( 10.0f );
pBall->m_bIsInfiniteLife = false;
// Think function, used to update time till death and avoid sleeping
pBall->SetNextThink ( gpGlobals->curtime + 0.1f );
}
}
ConCommand fire_energy_ball( "fire_energy_ball", fire_energy_ball_f, "Fires a test energy ball out of your face", FCVAR_CHEAT );
+480
View File
@@ -0,0 +1,480 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Core of the GlaDOS computer.
//
//=====================================================================================//
#include "cbase.h"
#include "baseentity.h"
#include "te_effect_dispatch.h" // Sprite effect
#include "props.h" // CPhysicsProp base class
#include "saverestore_utlvector.h"
#define GLADOS_CORE_MODEL_NAME "models/props_bts/glados_ball_reference.mdl"
static const char *s_pAnimateThinkContext = "Animate";
#define DEFAULT_LOOK_ANINAME "look_01"
#define CURIOUS_LOOK_ANINAME "look_02"
#define AGGRESSIVE_LOOK_ANINAME "look_03"
#define CRAZY_LOOK_ANINAME "look_04"
#define DEFAULT_SKIN 0
#define CURIOUS_SKIN 1
#define AGGRESSIVE_SKIN 2
#define CRAZY_SKIN 3
class CPropGladosCore : public CPhysicsProp
{
public:
DECLARE_CLASS( CPropGladosCore, CPhysicsProp );
DECLARE_DATADESC();
CPropGladosCore();
~CPropGladosCore();
typedef enum
{
CORETYPE_CURIOUS,
CORETYPE_AGGRESSIVE,
CORETYPE_CRAZY,
CORETYPE_NONE,
CORETYPE_TOTAL,
} CORETYPE;
virtual void Spawn( void );
virtual void Precache( void );
virtual QAngle PreferredCarryAngles( void ) { return QAngle( 180, -90, 180 ); }
virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; }
void InputPanic( inputdata_t &inputdata );
void InputStartTalking( inputdata_t &inputdata );
void StartPanic ( void );
void StartTalking ( float flDelay );
void TalkingThink ( void );
void PanicThink ( void );
void AnimateThink ( void );
void SetupVOList ( void );
void OnPhysGunPickup( CBasePlayer* pPhysGunUser, PhysGunPickup_t reason );
private:
int m_iTotalLines;
int m_iEyeballAttachment;
float m_flBetweenVOPadding; // Spacing (in seconds) between VOs
bool m_bFirstPickup;
// Names of sound scripts for this core's personality
CUtlVector<string_t> m_speechEvents;
int m_iSpeechIter;
string_t m_iszPanicSoundScriptName;
string_t m_iszDeathSoundScriptName;
string_t m_iszLookAnimationName; // Different animations for each personality
CORETYPE m_iCoreType;
};
LINK_ENTITY_TO_CLASS( prop_glados_core, CPropGladosCore );
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CPropGladosCore )
DEFINE_FIELD( m_iEyeballAttachment, FIELD_INTEGER ),
DEFINE_FIELD( m_iTotalLines, FIELD_INTEGER ),
DEFINE_FIELD( m_iSpeechIter, FIELD_INTEGER ),
DEFINE_FIELD( m_iszDeathSoundScriptName, FIELD_STRING ),
DEFINE_FIELD( m_iszPanicSoundScriptName, FIELD_STRING ),
DEFINE_FIELD( m_iszLookAnimationName, FIELD_STRING ),
DEFINE_UTLVECTOR( m_speechEvents, FIELD_STRING ),
DEFINE_FIELD( m_bFirstPickup, FIELD_BOOLEAN ),
DEFINE_KEYFIELD( m_iCoreType, FIELD_INTEGER, "CoreType" ),
DEFINE_KEYFIELD( m_flBetweenVOPadding, FIELD_FLOAT, "DelayBetweenLines" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Panic", InputPanic ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartTalking", InputStartTalking ),
DEFINE_THINKFUNC( TalkingThink ),
DEFINE_THINKFUNC( PanicThink ),
DEFINE_THINKFUNC( AnimateThink ),
END_DATADESC()
CPropGladosCore::CPropGladosCore()
{
m_iTotalLines = m_iSpeechIter = 0;
m_iszLookAnimationName = m_iszPanicSoundScriptName = m_iszDeathSoundScriptName = NULL_STRING;
m_flBetweenVOPadding = 2.5f;
m_bFirstPickup = true;
}
CPropGladosCore::~CPropGladosCore()
{
m_speechEvents.Purge();
}
void CPropGladosCore::Spawn( void )
{
SetupVOList();
Precache();
KeyValue( "model", GLADOS_CORE_MODEL_NAME );
BaseClass::Spawn();
//Default to 'dropped' animation
ResetSequence(LookupSequence("drop"));
SetCycle( 1.0f );
DisableAutoFade();
m_iEyeballAttachment = LookupAttachment( "eyeball" );
SetContextThink( &CPropGladosCore::AnimateThink, gpGlobals->curtime + 0.1f, s_pAnimateThinkContext );
}
void CPropGladosCore::Precache( void )
{
BaseClass::Precache();
// Personality VOs -- Curiosity
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_1" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_2" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_3" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_4" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_5" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_6" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_7" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_8" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_9" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_10" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_11" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_12" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_13" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_15" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_16" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_17" );
PrecacheScriptSound ( "Portal.Glados_core.Curiosity_18" );
// Aggressive
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_00" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_01" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_02" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_03" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_04" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_05" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_06" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_07" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_08" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_09" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_10" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_11" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_12" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_13" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_14" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_15" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_16" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_17" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_18" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_19" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_20" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_21" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_panic_01" );
PrecacheScriptSound ( "Portal.Glados_core.Aggressive_panic_02" );
// Crazy
PrecacheScriptSound ( "Portal.Glados_core.Crazy_01" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_02" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_03" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_04" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_05" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_06" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_07" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_08" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_09" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_10" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_11" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_12" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_13" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_14" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_15" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_16" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_17" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_18" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_19" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_20" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_21" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_22" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_23" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_24" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_25" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_26" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_27" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_28" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_29" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_30" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_31" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_32" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_33" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_34" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_35" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_36" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_37" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_38" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_39" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_40" );
PrecacheScriptSound ( "Portal.Glados_core.Crazy_41" );
PrecacheModel( GLADOS_CORE_MODEL_NAME );
}
//-----------------------------------------------------------------------------
// Purpose: Switch to panic think, play panic vo and animations
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CPropGladosCore::InputPanic( inputdata_t &inputdata )
{
StartPanic();
}
void CPropGladosCore::StartPanic( void )
{
ResetSequence( LookupSequence( STRING(m_iszLookAnimationName) ) );
SetThink( &CPropGladosCore::PanicThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Play panic vo and animations, then return to talking
// Output :
//-----------------------------------------------------------------------------
void CPropGladosCore::PanicThink ( void )
{
if ( m_speechEvents.Count() <= 0 || !m_speechEvents.IsValidIndex( m_iSpeechIter ) || m_iszPanicSoundScriptName == NULL_STRING )
{
SetThink ( NULL );
SetNextThink( gpGlobals->curtime );
return;
}
StopSound( m_speechEvents[m_iSpeechIter].ToCStr() );
EmitSound( m_iszPanicSoundScriptName.ToCStr() );
float flCurDuration = GetSoundDuration( m_iszPanicSoundScriptName.ToCStr(), GLADOS_CORE_MODEL_NAME );
SetThink( &CPropGladosCore::TalkingThink );
SetNextThink( gpGlobals->curtime + m_flBetweenVOPadding + flCurDuration );
}
//-----------------------------------------------------------------------------
// Purpose: Start playing personality VO list
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CPropGladosCore::InputStartTalking ( inputdata_t &inputdata )
{
StartTalking( 0.0f );
}
void CPropGladosCore::StartTalking( float flDelay )
{
if ( m_speechEvents.IsValidIndex( m_iSpeechIter ) && m_speechEvents.Count() > 0 )
{
StopSound( m_speechEvents[m_iSpeechIter].ToCStr() );
}
m_iSpeechIter = 0;
SetThink( &CPropGladosCore::TalkingThink );
SetNextThink( gpGlobals->curtime + m_flBetweenVOPadding + flDelay );
}
//-----------------------------------------------------------------------------
// Purpose: Start playing personality VO list
//-----------------------------------------------------------------------------
void CPropGladosCore::TalkingThink( void )
{
if ( m_speechEvents.Count() <= 0 || !m_speechEvents.IsValidIndex( m_iSpeechIter ) )
{
SetThink ( NULL );
SetNextThink( gpGlobals->curtime );
return;
}
// Loop the 'look around' animation after the first line.
int iCurSequence = GetSequence();
int iLookSequence = LookupSequence( STRING(m_iszLookAnimationName) );
if ( iCurSequence != iLookSequence && m_iSpeechIter > 0 )
{
ResetSequence( iLookSequence );
}
int iPrevIter = m_iSpeechIter-1;
if ( iPrevIter < 0 )
iPrevIter = 0;
StopSound( m_speechEvents[iPrevIter].ToCStr() );
float flCurDuration = GetSoundDuration( m_speechEvents[m_iSpeechIter].ToCStr(), GLADOS_CORE_MODEL_NAME );
EmitSound( m_speechEvents[m_iSpeechIter].ToCStr() );
SetNextThink( gpGlobals->curtime + m_flBetweenVOPadding + flCurDuration );
// wrap if we hit the end of the list
m_iSpeechIter = (m_iSpeechIter+1)%m_speechEvents.Count();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
void CPropGladosCore::AnimateThink()
{
StudioFrameAdvance();
SetContextThink( &CPropGladosCore::AnimateThink, gpGlobals->curtime + 0.1f, s_pAnimateThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose: Setup list of lines based on core personality
//-----------------------------------------------------------------------------
void CPropGladosCore::SetupVOList( void )
{
m_speechEvents.RemoveAll();
switch ( m_iCoreType )
{
case CORETYPE_CURIOUS:
{
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_1" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_2" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_3" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_4" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_5" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_6" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_7" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_8" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_9" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_10" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_11" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_12" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_13" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_16" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Curiosity_17" ) );
m_iszPanicSoundScriptName = AllocPooledString( "Portal.Glados_core.Curiosity_15" );
m_iszLookAnimationName = AllocPooledString( CURIOUS_LOOK_ANINAME );
m_nSkin = CURIOUS_SKIN;
}
break;
case CORETYPE_AGGRESSIVE:
{
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_01" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_02" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_03" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_04" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_05" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_06" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_07" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_08" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_09" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_10" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_11" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_12" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_13" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_14" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_15" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_16" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_17" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_18" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_19" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_20" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Aggressive_21" ) );
m_iszPanicSoundScriptName = AllocPooledString( "Portal.Glados_core.Aggressive_panic_01" );
m_iszLookAnimationName = AllocPooledString( AGGRESSIVE_LOOK_ANINAME );
m_nSkin = AGGRESSIVE_SKIN;
}
break;
case CORETYPE_CRAZY:
{
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_01" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_02" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_03" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_04" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_05" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_06" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_07" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_08" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_09" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_10" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_11" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_12" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_13" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_14" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_15" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_16" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_17" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_18" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_19" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_20" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_21" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_22" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_23" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_24" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_25" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_26" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_27" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_28" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_29" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_30" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_31" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_32" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_33" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_34" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_35" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_36" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_37" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_38" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_39" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_40" ) );
m_speechEvents.AddToTail( AllocPooledString( "Portal.Glados_core.Crazy_41" ) );
m_iszLookAnimationName = AllocPooledString( CRAZY_LOOK_ANINAME );
m_nSkin = CRAZY_SKIN;
}
break;
default:
{
m_iszLookAnimationName = AllocPooledString( DEFAULT_LOOK_ANINAME );
m_nSkin = DEFAULT_SKIN;
}
break;
};
m_iszDeathSoundScriptName = AllocPooledString( "Portal.Glados_core.Death" );
m_iTotalLines = m_speechEvents.Count();
m_iSpeechIter = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Cores play a special animation when picked up and dropped
// Input : pPhysGunUser - player picking up object
// reason - type of pickup
//-----------------------------------------------------------------------------
void CPropGladosCore::OnPhysGunPickup( CBasePlayer* pPhysGunUser, PhysGunPickup_t reason )
{
if ( m_bFirstPickup )
{
float flTalkingDelay = (CORETYPE_CURIOUS == m_iCoreType) ? (2.0f) : (0.0f);
StartTalking ( flTalkingDelay );
}
m_bFirstPickup = false;
ResetSequence(LookupSequence("turn"));
// +use always enables motion on these props
EnableMotion();
BaseClass::OnPhysGunPickup ( pPhysGunUser, reason );
}
File diff suppressed because it is too large Load Diff
+166
View File
@@ -0,0 +1,166 @@
//========= Copyright 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"
// FIX ME
#include "portal_shareddefs.h"
static const char *s_pDelayedPlacementContext = "DelayedPlacementContext";
static const char *s_pTestRestingSurfaceContext = "TestRestingSurfaceContext";
static const char *s_pFizzleThink = "FizzleThink";
class CPhysicsCloneArea;
class CProp_Portal : public CBaseAnimating, public CPortalSimulatorEventCallbacks
{
public:
DECLARE_CLASS( CProp_Portal, CBaseAnimating );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
CProp_Portal( void );
virtual ~CProp_Portal( void );
CNetworkHandle( CProp_Portal, 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_bActivated ); //a portal can exist and not be active
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
CSoundPatch *m_pAmbientSound;
Vector m_vAudioOrigin;
Vector m_vDelayedPosition;
QAngle m_qDelayedAngles;
int m_iDelayedFailure;
EHANDLE m_hPlacedBy;
COutputEvent m_OnPlacedSuccessfully; // Output in hammer for when this portal was successfully placed (not attempted and fizzed).
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 Precache( void );
virtual void CreateSounds( void );
virtual void StopLoopingSounds( void );
virtual void Spawn( void );
virtual void Activate( void );
virtual void OnRestore( void );
virtual void UpdateOnRemove( void );
void DelayedPlacementThink( void );
void TestRestingSurfaceThink ( void );
void FizzleThink( void );
bool IsActivedAndLinked( void ) 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
void PlacePortal( const Vector &vOrigin, const QAngle &qAngles, float fPlacementSuccess, bool bDelay = false );
void NewLocation( const Vector &vOrigin, const QAngle &qAngles );
void ResetModel( void ); //sets the model and bounding box
void DoFizzleEffect( int iEffect, bool bDelayedPos = true ); //display cool visual effect
void Fizzle( void ); //go inactive
void PunchPenetratingPlayer( CBaseEntity *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 );
void InputSetActivatedState( inputdata_t &inputdata );
void InputFizzle( inputdata_t &inputdata );
void InputNewLocation( inputdata_t &inputdata );
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];
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 );
private:
unsigned char m_iLinkageGroupID; //a group ID specifying which portals this one can possibly link to
CPhysCollide *m_pCollisionShape;
void RemovePortalMicAndSpeaker(); // Cleans up the portal's internal audio members
void UpdateCorners( void ); // Updates the four corners of this portal on spawn and placement
public:
inline unsigned char GetLinkageGroup( void ) const { return m_iLinkageGroupID; };
void ChangeLinkageGroup( unsigned char iLinkageGroupID );
//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 );
};
//-----------------------------------------------------------------------------
// inline state querying methods
//-----------------------------------------------------------------------------
inline bool CProp_Portal::IsPortal2() const
{
return m_bIsPortal2;
}
inline void CProp_Portal::SetIsPortal2( bool bIsPortal2 )
{
m_bIsPortal2 = bIsPortal2;
}
inline const VMatrix& CProp_Portal::MatrixThisToLinked() const
{
return m_matrixThisToLinked;
}
#endif //#ifndef PROP_PORTAL_H
@@ -0,0 +1,478 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the big scary boom-boom machine Antlions fear.
//
//=============================================================================//
#include "cbase.h"
#include "baseanimating.h"
#include "portal_player.h"
#include "EnvMessage.h"
#include "fmtstr.h"
#include "vguiscreen.h"
#include "point_bonusmaps_accessor.h"
#include "portal_shareddefs.h"
#define PORTAL_STATS_DISPLAY_MODEL_NAME "models/props/Round_elevator_body.mdl"
class CPropPortalStatsDisplay : public CBaseAnimating
{
public:
DECLARE_CLASS( CPropPortalStatsDisplay, CBaseAnimating );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual ~CPropPortalStatsDisplay();
virtual int UpdateTransmitState();
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
virtual void Spawn( void );
virtual void Precache( void );
virtual void OnRestore( void );
void ScreenVisible( bool bVisible );
void Disable( void );
void Enable( void );
void InputDisable( inputdata_t &inputdata );
void InputEnable( inputdata_t &inputdata );
void InputUpdateStats( inputdata_t &inputdata );
void InputResetPlayerStats( inputdata_t &inputdata );
private:
CNetworkVar( bool, m_bEnabled );
CNetworkVar( int, m_iNumPortalsPlaced );
CNetworkVar( int, m_iNumStepsTaken );
CNetworkVar( float, m_fNumSecondsTaken );
CNetworkVar( int, m_iBronzeObjective );
CNetworkVar( int, m_iSilverObjective );
CNetworkVar( int, m_iGoldObjective );
CNetworkString( szChallengeFileName, 128 );
CNetworkString( szChallengeMapName, 32 );
CNetworkString( szChallengeName, 32 );
CNetworkVar( int, m_iDisplayObjective );
COutputEvent m_OnMetBronzeObjective;
COutputEvent m_OnMetSilverObjective;
COutputEvent m_OnMetGoldObjective;
COutputEvent m_OnFailedAllObjectives;
private:
// Control panel
void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName );
void SpawnControlPanels( void );
void RestoreControlPanels( void );
typedef CHandle<CVGuiScreen> ScreenHandle_t;
CUtlVector<ScreenHandle_t> m_hScreens;
};
LINK_ENTITY_TO_CLASS( prop_portal_stats_display, CPropPortalStatsDisplay );
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CPropPortalStatsDisplay )
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iNumPortalsPlaced, FIELD_INTEGER ),
DEFINE_FIELD( m_iNumStepsTaken, FIELD_INTEGER ),
DEFINE_FIELD( m_fNumSecondsTaken, FIELD_FLOAT ),
DEFINE_FIELD( m_iBronzeObjective, FIELD_INTEGER ),
DEFINE_FIELD( m_iSilverObjective, FIELD_INTEGER ),
DEFINE_FIELD( m_iGoldObjective, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( szChallengeFileName, FIELD_CHARACTER ),
DEFINE_AUTO_ARRAY( szChallengeMapName, FIELD_CHARACTER ),
DEFINE_AUTO_ARRAY( szChallengeName, FIELD_CHARACTER ),
DEFINE_FIELD( m_iDisplayObjective, FIELD_INTEGER ),
//DEFINE_UTLVECTOR( m_hScreens, FIELD_EHANDLE ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "UpdateStats", InputUpdateStats ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetPlayerStats", InputResetPlayerStats ),
DEFINE_OUTPUT ( m_OnMetBronzeObjective, "OnMetBronzeObjective" ),
DEFINE_OUTPUT ( m_OnMetSilverObjective, "OnMetSilverObjective" ),
DEFINE_OUTPUT ( m_OnMetGoldObjective, "OnMetGoldObjective" ),
DEFINE_OUTPUT ( m_OnFailedAllObjectives, "OnFailedAllObjectives" ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CPropPortalStatsDisplay, DT_PropPortalStatsDisplay )
SendPropBool( SENDINFO(m_bEnabled) ),
SendPropInt( SENDINFO(m_iNumPortalsPlaced) ),
SendPropInt( SENDINFO(m_iNumStepsTaken) ),
SendPropFloat( SENDINFO(m_fNumSecondsTaken) ),
SendPropInt( SENDINFO(m_iBronzeObjective) ),
SendPropInt( SENDINFO(m_iSilverObjective) ),
SendPropInt( SENDINFO(m_iGoldObjective) ),
SendPropString( SENDINFO( szChallengeFileName ) ),
SendPropString( SENDINFO( szChallengeMapName ) ),
SendPropString( SENDINFO( szChallengeName ) ),
SendPropInt( SENDINFO(m_iDisplayObjective) ),
END_SEND_TABLE()
CPropPortalStatsDisplay::~CPropPortalStatsDisplay()
{
int i;
// Kill the control panels
for ( i = m_hScreens.Count(); --i >= 0; )
{
DestroyVGuiScreen( m_hScreens[i].Get() );
}
m_hScreens.RemoveAll();
}
int CPropPortalStatsDisplay::UpdateTransmitState()
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
void CPropPortalStatsDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
{
// Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
return;
BaseClass::SetTransmit( pInfo, bAlways );
// Force our screens to be sent too.
for ( int i=0; i < m_hScreens.Count(); i++ )
{
CVGuiScreen *pScreen = m_hScreens[i].Get();
pScreen->SetTransmit( pInfo, bAlways );
}
}
void CPropPortalStatsDisplay::Spawn( void )
{
char *szModel = (char *)STRING( GetModelName() );
if (!szModel || !*szModel)
{
szModel = PORTAL_STATS_DISPLAY_MODEL_NAME;
SetModelName( AllocPooledString(szModel) );
}
Precache();
SetModel( szModel );
SetSolid( SOLID_VPHYSICS );
VPhysicsInitStatic();
BaseClass::Spawn();
int iBronze, iSilver, iGold;
BonusMapChallengeObjectives( iBronze, iSilver, iGold );
m_iBronzeObjective = iBronze;
m_iSilverObjective = iSilver;
m_iGoldObjective = iGold;
BonusMapChallengeNames( szChallengeFileName.GetForModify(), szChallengeMapName.GetForModify(), szChallengeName.GetForModify() );
m_bEnabled = false;
int iSequence = SelectHeaviestSequence ( ACT_IDLE );
if ( iSequence != ACT_INVALID )
{
SetSequence( iSequence );
ResetSequenceInfo();
//Do this so we get the nice ramp-up effect.
m_flPlaybackRate = random->RandomFloat( 0.0f, 1.0f );
}
SpawnControlPanels();
ScreenVisible( m_bEnabled );
}
void CPropPortalStatsDisplay::Precache( void )
{
BaseClass::Precache();
PrecacheModel( STRING( GetModelName() ) );
PrecacheVGuiScreen( "portal_stats_display_screen" );
}
void CPropPortalStatsDisplay::OnRestore( void )
{
BaseClass::OnRestore();
RestoreControlPanels();
ScreenVisible( m_bEnabled );
}
void CPropPortalStatsDisplay::ScreenVisible( bool bVisible )
{
for ( int iScreen = 0; iScreen < m_hScreens.Count(); ++iScreen )
{
CVGuiScreen *pScreen = m_hScreens[ iScreen ].Get();
if ( bVisible )
pScreen->RemoveEffects( EF_NODRAW );
else
pScreen->AddEffects( EF_NODRAW );
}
}
void CPropPortalStatsDisplay::Disable( void )
{
if ( !m_bEnabled )
return;
m_bEnabled = false;
ScreenVisible( false );
}
void CPropPortalStatsDisplay::Enable( void )
{
if ( m_bEnabled )
return;
// Don't show stats display in non challenge mode!
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( pPlayer && pPlayer->GetBonusChallenge() == 0 )
return;
m_bEnabled = true;
m_iDisplayObjective = pPlayer->GetBonusChallenge() - 1;
// Check if they beat objectives
if ( pPlayer->GetBonusProgress() <= m_iBronzeObjective )
m_OnMetBronzeObjective.FireOutput( this, this );
else if ( pPlayer->GetBonusProgress() <= m_iSilverObjective )
m_OnMetSilverObjective.FireOutput( this, this );
else if ( pPlayer->GetBonusProgress() <= m_iGoldObjective )
m_OnMetGoldObjective.FireOutput( this, this );
else
m_OnFailedAllObjectives.FireOutput( this, this );
ScreenVisible( true );
}
void CPropPortalStatsDisplay::InputDisable( inputdata_t &inputdata )
{
Disable();
}
void CPropPortalStatsDisplay::InputEnable( inputdata_t &inputdata )
{
Enable();
}
void CPropPortalStatsDisplay::InputUpdateStats( inputdata_t &inputdata )
{
CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
if( pPlayer == NULL )
pPlayer = GetPortalPlayer( 1 ); //last ditch effort
if( pPlayer )
{
m_iNumPortalsPlaced = pPlayer->NumPortalsPlaced();
m_iNumStepsTaken = pPlayer->NumStepsTaken();
m_fNumSecondsTaken = pPlayer->NumSecondsTaken();
// Now that we've recorded it, don't let it change
pPlayer->PauseBonusProgress();
}
}
void CPropPortalStatsDisplay::InputResetPlayerStats( inputdata_t &inputdata )
{
CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
if( pPlayer == NULL )
pPlayer = GetPortalPlayer( 1 ); //last ditch effort
if( pPlayer )
{
pPlayer->ResetThisLevelStats();
}
}
void CPropPortalStatsDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
pPanelName = "portal_stats_display_screen";
}
void CPropPortalStatsDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
{
pPanelName = "vgui_screen";
}
//-----------------------------------------------------------------------------
// This is called by the base object when it's time to spawn the control panels
//-----------------------------------------------------------------------------
void CPropPortalStatsDisplay::SpawnControlPanels()
{
char buf[64];
// FIXME: Deal with dynamically resizing control panels?
// If we're attached to an entity, spawn control panels on it instead of use
CBaseAnimating *pEntityToSpawnOn = this;
char *pOrgLL = "statPanel%d_bl";
char *pOrgUR = "statPanel%d_tr";
char *pAttachmentNameLL = pOrgLL;
char *pAttachmentNameUR = pOrgUR;
Assert( pEntityToSpawnOn );
// Lookup the attachment point...
int nPanel;
for ( nPanel = 0; true; ++nPanel )
{
Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
{
// Try and use my panels then
pEntityToSpawnOn = this;
Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
return;
}
Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
{
// Try and use my panels then
Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
return;
}
const char *pScreenName;
GetControlPanelInfo( nPanel, pScreenName );
if (!pScreenName)
continue;
const char *pScreenClassname;
GetControlPanelClassName( nPanel, pScreenClassname );
if ( !pScreenClassname )
continue;
// Compute the screen size from the attachment points...
matrix3x4_t panelToWorld;
pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
matrix3x4_t worldToPanel;
MatrixInvert( panelToWorld, worldToPanel );
// Now get the lower right position + transform into panel space
Vector lr, lrlocal;
pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
MatrixGetColumn( panelToWorld, 3, lr );
VectorTransform( lr, worldToPanel, lrlocal );
float flWidth = lrlocal.x;
float flHeight = lrlocal.y;
CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
pScreen->ChangeTeam( GetTeamNumber() );
pScreen->SetActualSize( flWidth, flHeight );
pScreen->SetActive( true );
pScreen->MakeVisibleOnlyToTeammates( false );
pScreen->SetTransparency( true );
int nScreen = m_hScreens.AddToTail( );
m_hScreens[nScreen].Set( pScreen );
}
}
void CPropPortalStatsDisplay::RestoreControlPanels( void )
{
char buf[64];
// FIXME: Deal with dynamically resizing control panels?
// If we're attached to an entity, spawn control panels on it instead of use
CBaseAnimating *pEntityToSpawnOn = this;
char *pOrgLL = "statPanel%d_bl";
char *pOrgUR = "statPanel%d_tr";
char *pAttachmentNameLL = pOrgLL;
char *pAttachmentNameUR = pOrgUR;
Assert( pEntityToSpawnOn );
// Lookup the attachment point...
int nPanel;
for ( nPanel = 0; true; ++nPanel )
{
Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
{
// Try and use my panels then
pEntityToSpawnOn = this;
Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
return;
}
Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
{
// Try and use my panels then
Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
return;
}
const char *pScreenName;
GetControlPanelInfo( nPanel, pScreenName );
if (!pScreenName)
continue;
const char *pScreenClassname;
GetControlPanelClassName( nPanel, pScreenClassname );
if ( !pScreenClassname )
continue;
CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname );
while ( pScreen && pScreen->GetOwnerEntity() != this && Q_strcmp( pScreen->GetPanelName(), pScreenName ) == 0 )
{
pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname );
}
if ( pScreen )
{
int nScreen = m_hScreens.AddToTail( );
m_hScreens[nScreen].Set( pScreen );
}
}
}
+538
View File
@@ -0,0 +1,538 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the big scary boom-boom machine Antlions fear.
//
//=============================================================================//
#include "cbase.h"
#include "baseentity.h"
#include "rotorwash.h"
#include "soundenvelope.h"
#include "engine/IEngineSound.h"
#include "te_effect_dispatch.h"
#include "point_posecontroller.h"
#include "prop_portal_shared.h"
#define TELESCOPE_ENABLE_TIME 1.0f
#define TELESCOPE_DISABLE_TIME 2.0f
#define TELESCOPE_ROTATEX_TIME 0.5f
#define TELESCOPE_ROTATEY_TIME 0.5f
#define TELESCOPING_ARM_MODEL_NAME "models/props/telescopic_arm.mdl"
#define DEBUG_TELESCOPIC_ARM_AIM 1
class CPropTelescopicArm : public CBaseAnimating
{
public:
DECLARE_CLASS( CPropTelescopicArm, CBaseAnimating );
DECLARE_DATADESC();
virtual void UpdateOnRemove( void );
virtual void Spawn( void );
virtual void Precache( void );
virtual void Activate ( void );
void DisabledThink( void );
void EnabledThink( void );
void AimAt( Vector vTarget );
bool TestLOS( const Vector& vAimPoint );
void SetTarget( const char *pTargetName );
void SetTarget( CBaseEntity *pTarget );
void InputDisable( inputdata_t &inputdata );
void InputEnable( inputdata_t &inputdata );
void InputSetTarget( inputdata_t &inputdata );
void InputTargetPlayer( inputdata_t &inputdata );
private:
Vector FindTargetAimPoint( void );
Vector FindAimPointThroughPortal ( const CProp_Portal* pPortal );
bool m_bEnabled;
bool m_bCanSeeTarget;
int m_iFrontMarkerAttachment;
EHANDLE m_hRotXPoseController;
EHANDLE m_hRotYPoseController;
EHANDLE m_hTelescopicPoseController;
EHANDLE m_hAimTarget;
COutputEvent m_OnLostTarget;
COutputEvent m_OnFoundTarget;
};
LINK_ENTITY_TO_CLASS( prop_telescopic_arm, CPropTelescopicArm );
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CPropTelescopicArm )
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bCanSeeTarget, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iFrontMarkerAttachment, FIELD_INTEGER ),
DEFINE_FIELD( m_hRotXPoseController, FIELD_EHANDLE ),
DEFINE_FIELD( m_hRotYPoseController, FIELD_EHANDLE ),
DEFINE_FIELD( m_hTelescopicPoseController, FIELD_EHANDLE ),
DEFINE_FIELD( m_hAimTarget, FIELD_EHANDLE ),
DEFINE_THINKFUNC( DisabledThink ),
DEFINE_THINKFUNC( EnabledThink ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ),
DEFINE_INPUTFUNC( FIELD_VOID, "TargetPlayer", InputTargetPlayer ),
DEFINE_OUTPUT ( m_OnLostTarget, "OnLostTarget" ),
DEFINE_OUTPUT ( m_OnFoundTarget, "OnFoundTarget" ),
END_DATADESC()
void CPropTelescopicArm::UpdateOnRemove( void )
{
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
if ( pPoseController )
UTIL_Remove( pPoseController );
m_hRotXPoseController = 0;
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
if ( pPoseController )
UTIL_Remove( pPoseController );
m_hRotYPoseController = 0;
pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
if ( pPoseController )
UTIL_Remove( pPoseController );
m_hTelescopicPoseController = 0;
}
void CPropTelescopicArm::Spawn( void )
{
char *szModel = (char *)STRING( GetModelName() );
if (!szModel || !*szModel)
{
szModel = TELESCOPING_ARM_MODEL_NAME;
SetModelName( AllocPooledString(szModel) );
}
Precache();
SetModel( szModel );
SetSolid( SOLID_VPHYSICS );
SetMoveType( MOVETYPE_PUSH );
VPhysicsInitStatic();
BaseClass::Spawn();
m_bEnabled = false;
m_bCanSeeTarget = false;
SetThink( &CPropTelescopicArm::DisabledThink );
SetNextThink( gpGlobals->curtime + 1.0f );
int iSequence = SelectHeaviestSequence ( ACT_IDLE );
if ( iSequence != ACT_INVALID )
{
SetSequence( iSequence );
ResetSequenceInfo();
//Do this so we get the nice ramp-up effect.
m_flPlaybackRate = random->RandomFloat( 0.0f, 1.0f );
}
m_iFrontMarkerAttachment = LookupAttachment( "Front_marker" );
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
DispatchSpawn( pPoseController );
if ( pPoseController )
{
pPoseController->SetProp( this );
pPoseController->SetInterpolationWrap( true );
pPoseController->SetPoseParameterName( "rot_x" );
m_hRotXPoseController = pPoseController;
}
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
DispatchSpawn( pPoseController );
if ( pPoseController )
{
pPoseController->SetProp( this );
pPoseController->SetInterpolationWrap( true );
pPoseController->SetPoseParameterName( "rot_y" );
m_hRotYPoseController = pPoseController;
}
pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
DispatchSpawn( pPoseController );
if ( pPoseController )
{
pPoseController->SetProp( this );
pPoseController->SetPoseParameterName( "telescopic" );
m_hTelescopicPoseController = pPoseController;
}
}
void CPropTelescopicArm::Precache( void )
{
BaseClass::Precache();
PrecacheModel( STRING( GetModelName() ) );
PrecacheScriptSound( "coast.thumper_hit" );
PrecacheScriptSound( "coast.thumper_ambient" );
PrecacheScriptSound( "coast.thumper_dust" );
PrecacheScriptSound( "coast.thumper_startup" );
PrecacheScriptSound( "coast.thumper_shutdown" );
PrecacheScriptSound( "coast.thumper_large_hit" );
}
void CPropTelescopicArm::Activate( void )
{
BaseClass::Activate();
}
void CPropTelescopicArm::DisabledThink( void )
{
SetNextThink( gpGlobals->curtime + 1.0 );
}
void CPropTelescopicArm::EnabledThink( void )
{
CBaseEntity *pTarget = m_hAimTarget.Get();
if ( !pTarget )
{
//SetTarget ( UTIL_PlayerByIndex( 1 ) );
// Default to targeting a player
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() )
{
pTarget = pPlayer;
break;
}
}
if( pTarget == NULL )
{
//search again, but don't require the player to be visible
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && pPlayer->IsAlive() )
{
pTarget = pPlayer;
break;
}
}
if( pTarget == NULL )
{
//search again, but don't require the player to be visible or alive
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer )
{
pTarget = pPlayer;
break;
}
}
}
}
if( pTarget )
SetTarget( pTarget );
}
if ( pTarget )
{
// Aim at the center of the abs box
Vector vAimPoint = FindTargetAimPoint();
Assert ( vAimPoint != vec3_invalid );
AimAt( vAimPoint );
// We have direct line of sight to our target
if ( TestLOS ( vAimPoint ) )
{
// Just aquired LOS
if ( !m_bCanSeeTarget )
{
m_OnFoundTarget.FireOutput( m_hAimTarget, this );
}
m_bCanSeeTarget = true;
}
// No LOS to target
else
{
// Just lost LOS
if ( m_bCanSeeTarget )
{
m_OnLostTarget.FireOutput( m_hAimTarget, this );
}
m_bCanSeeTarget = false;
}
}
SetNextThink( gpGlobals->curtime + 0.1 );
}
//-----------------------------------------------------------------------------
// Purpose: Finds the point to aim at in order to see the target entity.
// Note: Also considers aim paths through portals.
// Output : Point of the target
//-----------------------------------------------------------------------------
Vector CPropTelescopicArm::FindTargetAimPoint( void )
{
CBaseEntity *pTarget = m_hAimTarget.Get();
if ( !pTarget )
{
// No target to aim at, can't return meaningful info
Warning( "CPropTelescopicArm::FindTargetAimPoint called with no valid target entity." );
return vec3_invalid;
}
else
{
Vector vFrontPoint;
GetAttachment( m_iFrontMarkerAttachment, vFrontPoint, NULL, NULL, NULL );
// Aim at the target through the world
Vector vAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f;
//float fDistToPoint = vFrontPoint.DistToSqr( vAimPoint );
CProp_Portal *pShortestDistPortal = NULL;
UTIL_Portal_ShortestDistance( vFrontPoint, vAimPoint, &pShortestDistPortal, true );
Vector ptShortestAimPoint;
if( pShortestDistPortal )
{
ptShortestAimPoint = FindAimPointThroughPortal( pShortestDistPortal );
if( ptShortestAimPoint == vec3_invalid )
ptShortestAimPoint = vAimPoint;
}
else
{
ptShortestAimPoint = vAimPoint;
}
return ptShortestAimPoint;
}
}
//-----------------------------------------------------------------------------
// Purpose: Find the center of the target entity as seen through the specified portal
// Input : pPortal - The portal to look through
// Output : Vector& output point in world space where the target *appears* to be as seen through the portal
//-----------------------------------------------------------------------------
Vector CPropTelescopicArm::FindAimPointThroughPortal( const CProp_Portal* pPortal )
{
if ( pPortal && pPortal->m_bActivated )
{
CProp_Portal* pLinked = pPortal->m_hLinkedPortal.Get();
CBaseEntity* pTarget = m_hAimTarget.Get();
if ( pLinked && pLinked->m_bActivated && pTarget )
{
VMatrix matToPortalView = pLinked->m_matrixThisToLinked;
Vector vTargetAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f;
return matToPortalView * vTargetAimPoint;
}
}
// Bad portal pointer, not linked, no target or otherwise failed
return vec3_invalid;
}
//-----------------------------------------------------------------------------
// Purpose: Tests if this prop's front point has direct line of sight to it's target entity
// Input : vAimPoint - The point to aim at
// Output : Returns true if target is in direct line of sight, false otherwise.
//-----------------------------------------------------------------------------
bool CPropTelescopicArm::TestLOS( const Vector& vAimPoint )
{
// Test for LOS and fire outputs if the sight condition changes
Vector vFaceOrigin;
trace_t tr;
GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL );
Ray_t ray;
ray.Init( vFaceOrigin, vAimPoint );
ray.m_IsRay = true;
// This aim point does hit target, now make sure there are no blocking objects in the way
CTraceFilterWorldAndPropsOnly filter;
UTIL_Portal_TraceRay( ray, MASK_SHOT, &filter, &tr );
return !(tr.fraction < 1.0f);
}
void CPropTelescopicArm::AimAt( Vector vTarget )
{
Vector vFaceOrigin;
GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL );
Vector vNormalToTarget = vTarget - vFaceOrigin;
VectorNormalize( vNormalToTarget );
VMatrix vWorldToLocalRotation = EntityToWorldTransform();
vNormalToTarget = vWorldToLocalRotation.InverseTR().ApplyRotation( vNormalToTarget );
Vector vUp;
GetVectors( NULL, NULL, &vUp );
QAngle qAnglesToTarget;
VectorAngles( vNormalToTarget, vUp, qAnglesToTarget );
float fNewX = ( qAnglesToTarget.x + 90.0f ) / 360.0f;
float fNewY = qAnglesToTarget.y / 360.0f;
if ( fNewY < 0.0f )
fNewY += 1.0f;
CPoseController *pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_ROTATEX_TIME );
pPoseController->SetPoseValue( fNewX );
}
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_ROTATEY_TIME );
pPoseController->SetPoseValue( fNewY );
}
}
void CPropTelescopicArm::SetTarget( const char *pchTargetName )
{
CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pchTargetName, NULL, NULL );
//if ( pTarget == NULL )
// pTarget = UTIL_PlayerByIndex( 1 );
return SetTarget( pTarget );
}
void CPropTelescopicArm::SetTarget( CBaseEntity *pTarget )
{
m_hAimTarget = pTarget;
}
void CPropTelescopicArm::InputDisable( inputdata_t &inputdata )
{
if ( m_bEnabled )
{
m_bEnabled = false;
EmitSound( "coast.thumper_shutdown" );
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f );
pPoseController->SetPoseValue( 0.0f );
}
pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f );
pPoseController->SetPoseValue( 0.0f );
}
pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME );
pPoseController->SetPoseValue( 0.0f );
}
SetThink( &CPropTelescopicArm::DisabledThink );
SetNextThink( gpGlobals->curtime + TELESCOPE_DISABLE_TIME );
}
}
void CPropTelescopicArm::InputEnable( inputdata_t &inputdata )
{
if ( !m_bEnabled )
{
m_bEnabled = true;
EmitSound( "coast.thumper_startup" );
CPoseController *pPoseController;
pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
if ( pPoseController )
{
pPoseController->SetInterpolationTime( TELESCOPE_ENABLE_TIME );
pPoseController->SetPoseValue( 1.0f );
}
SetThink( &CPropTelescopicArm::EnabledThink );
SetNextThink( gpGlobals->curtime + TELESCOPE_ENABLE_TIME );
}
}
void CPropTelescopicArm::InputSetTarget( inputdata_t &inputdata )
{
SetTarget( inputdata.value.String() );
}
void CPropTelescopicArm::InputTargetPlayer( inputdata_t &inputdata )
{
//SetTarget( UTIL_PlayerByIndex( 1 ) );
CBaseEntity *pTarget = NULL;
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() )
{
pTarget = pPlayer;
break;
}
}
if( pTarget == NULL )
{
//search again, but don't require the player to be visible
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && pPlayer->IsAlive() )
{
pTarget = pPlayer;
break;
}
}
if( pTarget == NULL )
{
//search again, but don't require the player to be visible or alive
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer )
{
pTarget = pPlayer;
break;
}
}
}
}
if( pTarget )
SetTarget( pTarget );
}
@@ -0,0 +1,269 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A volume which bumps portal placement. Keeps a global list loaded in from the map
// and provides an interface with which prop_portal can get this list and avoid successfully
// creating portals partially inside the volume.
//
// $NoKeywords: $
//======================================================================================//
#include "cbase.h"
#include "triggers.h"
#include "portal_player.h"
#include "weapon_portalgun.h"
#include "prop_portal_shared.h"
#include "portal_shareddefs.h"
#include "physobj.h"
#include "portal/weapon_physcannon.h"
#include "model_types.h"
#include "rumble_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static char *g_pszPortalNonCleansable[] =
{
"func_door",
"func_door_rotating",
"prop_door_rotating",
"func_tracktrain",
"env_ghostanimating",
"physicsshadowclone",
"prop_energy_ball",
NULL,
};
//-----------------------------------------------------------------------------
// Purpose: Removes anything that touches it. If the trigger has a targetname,
// firing it will toggle state.
//-----------------------------------------------------------------------------
class CTriggerPortalCleanser : public CBaseTrigger
{
public:
DECLARE_CLASS( CTriggerPortalCleanser, CBaseTrigger );
void Spawn( void );
void Touch( CBaseEntity *pOther );
DECLARE_DATADESC();
// Outputs
COutputEvent m_OnDissolve;
COutputEvent m_OnFizzle;
COutputEvent m_OnDissolveBox;
};
BEGIN_DATADESC( CTriggerPortalCleanser )
// Outputs
DEFINE_OUTPUT( m_OnDissolve, "OnDissolve" ),
DEFINE_OUTPUT( m_OnFizzle, "OnFizzle" ),
DEFINE_OUTPUT( m_OnDissolveBox, "OnDissolveBox" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( trigger_portal_cleanser, CTriggerPortalCleanser );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerPortalCleanser::Spawn( void )
{
BaseClass::Spawn();
InitTrigger();
}
// Creates a base entity with model/physics matching the parameter ent.
// Used to avoid higher level functions on a disolving entity, which should be inert
// and not react the way it used to (touches, etc).
// Uses simple physics entities declared in physobj.cpp
CBaseEntity* ConvertToSimpleProp ( CBaseEntity* pEnt )
{
CBaseEntity *pRetVal = NULL;
int modelindex = pEnt->GetModelIndex();
const model_t *model = modelinfo->GetModel( modelindex );
if ( model && modelinfo->GetModelType(model) == mod_brush )
{
pRetVal = CreateEntityByName( "simple_physics_brush" );
}
else
{
pRetVal = CreateEntityByName( "simple_physics_prop" );
}
pRetVal->KeyValue( "model", STRING(pEnt->GetModelName()) );
pRetVal->SetAbsOrigin( pEnt->GetAbsOrigin() );
pRetVal->SetAbsAngles( pEnt->GetAbsAngles() );
pRetVal->Spawn();
pRetVal->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false );
return pRetVal;
}
void CTriggerPortalCleanser::Touch( CBaseEntity *pOther )
{
if ( !PassesTriggerFilters( pOther ) )
return;
if ( pOther->IsPlayer() )
{
CPortal_Player *pPlayer = ToPortalPlayer( pOther );
if ( pPlayer )
{
CWeaponPortalgun *pPortalgun = dynamic_cast<CWeaponPortalgun*>( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
if ( pPortalgun )
{
bool bFizzledPortal = false;
if ( pPortalgun->CanFirePortal1() )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( pPortalgun->m_iPortalLinkageGroupID, false );
if ( pPortal && pPortal->m_bActivated )
{
pPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
pPortal->Fizzle();
// HACK HACK! Used to make the gun visually change when going through a cleanser!
pPortalgun->m_fEffectsMaxSize1 = 50.0f;
bFizzledPortal = true;
}
// Cancel portals that are still mid flight
if ( pPortal && pPortal->GetNextThink( s_pDelayedPlacementContext ) > gpGlobals->curtime )
{
pPortal->SetContextThink( NULL, gpGlobals->curtime, s_pDelayedPlacementContext );
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
bFizzledPortal = true;
}
}
if ( pPortalgun->CanFirePortal2() )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( pPortalgun->m_iPortalLinkageGroupID, true );
if ( pPortal && pPortal->m_bActivated )
{
pPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
pPortal->Fizzle();
// HACK HACK! Used to make the gun visually change when going through a cleanser!
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
bFizzledPortal = true;
}
// Cancel portals that are still mid flight
if ( pPortal && pPortal->GetNextThink( s_pDelayedPlacementContext ) > gpGlobals->curtime )
{
pPortal->SetContextThink( NULL, gpGlobals->curtime, s_pDelayedPlacementContext );
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
bFizzledPortal = true;
}
}
if ( bFizzledPortal )
{
pPortalgun->SendWeaponAnim( ACT_VM_FIZZLE );
pPortalgun->SetLastFiredPortal( 0 );
m_OnFizzle.FireOutput( pOther, this );
pPlayer->RumbleEffect( RUMBLE_RPG_MISSILE, 0, RUMBLE_FLAG_RESTART );
}
}
}
return;
}
CBaseAnimating *pBaseAnimating = dynamic_cast<CBaseAnimating*>( pOther );
if ( pBaseAnimating && !pBaseAnimating->IsDissolving() )
{
int i = 0;
while ( g_pszPortalNonCleansable[ i ] )
{
if ( FClassnameIs( pBaseAnimating, g_pszPortalNonCleansable[ i ] ) )
{
// Don't dissolve non cleansable objects
return;
}
++i;
}
// The portal weight box, used for puzzles in the portal mod is differentiated by its name
// always being 'box'. We use special logic when the cleanser dissolves a box so this is a special output for it.
if ( pBaseAnimating->NameMatches( "box" ) )
{
m_OnDissolveBox.FireOutput( pOther, this );
}
if ( FClassnameIs( pBaseAnimating, "updateitem2" ) )
{
pBaseAnimating->EmitSound( "UpdateItem.Fizzle" );
}
Vector vOldVel;
AngularImpulse vOldAng;
pBaseAnimating->GetVelocity( &vOldVel, &vOldAng );
IPhysicsObject* pOldPhys = pBaseAnimating->VPhysicsGetObject();
if ( pOldPhys && ( pOldPhys->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
{
CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pBaseAnimating );
if( pPlayer )
{
// Modify the velocity for held objects so it gets away from the player
pPlayer->ForceDropOfCarriedPhysObjects( pBaseAnimating );
pPlayer->GetAbsVelocity();
vOldVel = pPlayer->GetAbsVelocity() + Vector( pPlayer->EyeDirection2D().x * 4.0f, pPlayer->EyeDirection2D().y * 4.0f, -32.0f );
}
}
// Swap object with an disolving physics model to avoid touch logic
CBaseEntity *pDisolvingObj = ConvertToSimpleProp( pBaseAnimating );
if ( pDisolvingObj )
{
// Remove old prop, transfer name and children to the new simple prop
pDisolvingObj->SetName( pBaseAnimating->GetEntityName() );
UTIL_TransferPoseParameters( pBaseAnimating, pDisolvingObj );
TransferChildren( pBaseAnimating, pDisolvingObj );
pDisolvingObj->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
pBaseAnimating->AddSolidFlags( FSOLID_NOT_SOLID );
pBaseAnimating->AddEffects( EF_NODRAW );
IPhysicsObject* pPhys = pDisolvingObj->VPhysicsGetObject();
if ( pPhys )
{
pPhys->EnableGravity( false );
Vector vVel = vOldVel;
AngularImpulse vAng = vOldAng;
// Disolving hurts, damp and blur the motion a little
vVel *= 0.5f;
vAng.z += 20.0f;
pPhys->SetVelocity( &vVel, &vAng );
}
pBaseAnimating->AddFlag( FL_DISSOLVING );
UTIL_Remove( pBaseAnimating );
}
CBaseAnimating *pDisolvingAnimating = dynamic_cast<CBaseAnimating*>( pDisolvingObj );
if ( pDisolvingAnimating )
{
pDisolvingAnimating->Dissolve( "", gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
}
m_OnDissolve.FireOutput( pOther, this );
}
}
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef WEAPON_PHYSCANNON_H
#define WEAPON_PHYSCANNON_H
#ifdef _WIN32
#pragma once
#endif
class CGrabController;
//-----------------------------------------------------------------------------
// Do we have the super-phys gun?
//-----------------------------------------------------------------------------
bool PlayerHasMegaPhysCannon();
// force the physcannon to drop an object (if carried)
void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis );
void PhysCannonBeginUpgrade( CBaseAnimating *pAnim );
bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupController, CBaseEntity *pHeldEntity );
void ShutdownPickupController( CBaseEntity *pPickupControllerEntity );
float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject );
float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject );
CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon );
CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer );
CBasePlayer *GetPlayerHoldingEntity( CBaseEntity *pEntity );
CGrabController *GetGrabControllerForPlayer( CBasePlayer *pPlayer );
CGrabController *GetGrabControllerForPhysCannon( CBaseCombatWeapon *pActiveWeapon );
void GetSavedParamsForCarriedPhysObject( CGrabController *pGrabController, IPhysicsObject *pObject, float *pSavedMassOut, float *pSavedRotationalDampingOut );
void UpdateGrabControllerTargetPosition( CBasePlayer *pPlayer, Vector *vPosition, QAngle *qAngles );
bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject );
void GrabController_SetPortalPenetratingEntity( CGrabController *pController, CBaseEntity *pPenetrated );
#endif // WEAPON_PHYSCANNON_H
+736
View File
@@ -0,0 +1,736 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "BasePropDoor.h"
#include "portal_player.h"
#include "te_effect_dispatch.h"
#include "gameinterface.h"
#include "prop_combine_ball.h"
#include "portal_shareddefs.h"
#include "triggers.h"
#include "collisionutils.h"
#include "cbaseanimatingprojectile.h"
#include "weapon_physcannon.h"
#include "prop_portal_shared.h"
#include "portal_placement.h"
#include "weapon_portalgun_shared.h"
#include "physicsshadowclone.h"
#include "particle_parse.h"
#define BLAST_SPEED_NON_PLAYER 1000.0f
#define BLAST_SPEED 3000.0f
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalgun, DT_WeaponPortalgun )
BEGIN_NETWORK_TABLE( CWeaponPortalgun, DT_WeaponPortalgun )
SendPropBool( SENDINFO( m_bCanFirePortal1 ) ),
SendPropBool( SENDINFO( m_bCanFirePortal2 ) ),
SendPropInt( SENDINFO( m_iLastFiredPortal ) ),
SendPropBool( SENDINFO( m_bOpenProngs ) ),
SendPropFloat( SENDINFO( m_fCanPlacePortal1OnThisSurface ) ),
SendPropFloat( SENDINFO( m_fCanPlacePortal2OnThisSurface ) ),
SendPropFloat( SENDINFO( m_fEffectsMaxSize1 ) ), // HACK HACK! Used to make the gun visually change when going through a cleanser!
SendPropFloat( SENDINFO( m_fEffectsMaxSize2 ) ),
SendPropInt( SENDINFO( m_EffectState ) ),
END_NETWORK_TABLE()
BEGIN_DATADESC( CWeaponPortalgun )
DEFINE_KEYFIELD( m_bCanFirePortal1, FIELD_BOOLEAN, "CanFirePortal1" ),
DEFINE_KEYFIELD( m_bCanFirePortal2, FIELD_BOOLEAN, "CanFirePortal2" ),
DEFINE_FIELD( m_iLastFiredPortal, FIELD_INTEGER ),
DEFINE_FIELD( m_bOpenProngs, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fCanPlacePortal1OnThisSurface, FIELD_FLOAT ),
DEFINE_FIELD( m_fCanPlacePortal2OnThisSurface, FIELD_FLOAT ),
DEFINE_FIELD( m_fEffectsMaxSize1, FIELD_FLOAT ),
DEFINE_FIELD( m_fEffectsMaxSize2, FIELD_FLOAT ),
DEFINE_FIELD( m_EffectState, FIELD_INTEGER ),
DEFINE_FIELD( m_iPortalLinkageGroupID, FIELD_CHARACTER ),
DEFINE_INPUTFUNC( FIELD_VOID, "ChargePortal1", InputChargePortal1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "ChargePortal2", InputChargePortal2 ),
DEFINE_INPUTFUNC( FIELD_VOID, "FirePortal1", FirePortal1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "FirePortal2", FirePortal2 ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "FirePortalDirection1", FirePortalDirection1 ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "FirePortalDirection2", FirePortalDirection2 ),
DEFINE_SOUNDPATCH( m_pMiniGravHoldSound ),
DEFINE_OUTPUT ( m_OnFiredPortal1, "OnFiredPortal1" ),
DEFINE_OUTPUT ( m_OnFiredPortal2, "OnFiredPortal2" ),
DEFINE_FUNCTION( Think ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( weapon_portalgun, CWeaponPortalgun );
PRECACHE_WEAPON_REGISTER(weapon_portalgun);
extern ConVar sv_portal_placement_debug;
extern ConVar sv_portal_placement_never_fail;
void CWeaponPortalgun::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetThink( &CWeaponPortalgun::Think );
SetNextThink( gpGlobals->curtime + 0.1 );
if( GameRules()->IsMultiplayer() )
{
CBaseEntity *pOwner = GetOwner();
if( pOwner && pOwner->IsPlayer() )
m_iPortalLinkageGroupID = pOwner->entindex();
Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
}
}
void CWeaponPortalgun::Activate()
{
BaseClass::Activate();
CreateSounds();
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer )
{
CBaseEntity *pHeldObject = GetPlayerHeldEntity( pPlayer );
OpenProngs( ( pHeldObject ) ? ( false ) : ( true ) );
OpenProngs( ( pHeldObject ) ? ( true ) : ( false ) );
if( GameRules()->IsMultiplayer() )
m_iPortalLinkageGroupID = pPlayer->entindex();
Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
}
// HACK HACK! Used to make the gun visually change when going through a cleanser!
m_fEffectsMaxSize1 = 4.0f;
m_fEffectsMaxSize2 = 4.0f;
}
void CWeaponPortalgun::OnPickedUp( CBaseCombatCharacter *pNewOwner )
{
if( GameRules()->IsMultiplayer() )
{
if( pNewOwner && pNewOwner->IsPlayer() )
m_iPortalLinkageGroupID = pNewOwner->entindex();
Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
}
BaseClass::OnPickedUp( pNewOwner );
}
void CWeaponPortalgun::CreateSounds()
{
if (!m_pMiniGravHoldSound)
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
CPASAttenuationFilter filter( this );
m_pMiniGravHoldSound = controller.SoundCreate( filter, entindex(), "Weapon_Portalgun.HoldSound" );
controller.Play( m_pMiniGravHoldSound, 0, 100 );
}
}
void CWeaponPortalgun::StopLoopingSounds()
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundDestroy( m_pMiniGravHoldSound );
m_pMiniGravHoldSound = NULL;
BaseClass::StopLoopingSounds();
}
void CWeaponPortalgun::DoEffectBlast( bool bPortal2, int iPlacedBy, const Vector &ptStart, const Vector &ptFinalPos, const QAngle &qStartAngles, float fDelay )
{
CEffectData fxData;
fxData.m_vOrigin = ptStart;
fxData.m_vStart = ptFinalPos;
fxData.m_flScale = gpGlobals->curtime + fDelay;
fxData.m_vAngles = qStartAngles;
fxData.m_nColor = ( ( bPortal2 ) ? ( 2 ) : ( 1 ) );
fxData.m_nDamageType = iPlacedBy;
DispatchEffect( "PortalBlast", fxData );
}
//-----------------------------------------------------------------------------
// Purpose: Allows a generic think function before the others are called
// Input : state - which state the turret is currently in
//-----------------------------------------------------------------------------
bool CWeaponPortalgun::PreThink( void )
{
//Animate
StudioFrameAdvance();
//Do not interrupt current think function
return false;
}
void CWeaponPortalgun::Think( void )
{
//Allow descended classes a chance to do something before the think function
if ( PreThink() )
return;
SetNextThink( gpGlobals->curtime + 0.1f );
CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
if ( !pPlayer || pPlayer->GetActiveWeapon() != this )
{
m_fCanPlacePortal1OnThisSurface = 1.0f;
m_fCanPlacePortal2OnThisSurface = 1.0f;
return;
}
// Test portal placement
m_fCanPlacePortal1OnThisSurface = ( ( m_bCanFirePortal1 ) ? ( FirePortal( false, 0, 1 ) ) : ( 0.0f ) );
m_fCanPlacePortal2OnThisSurface = ( ( m_bCanFirePortal2 ) ? ( FirePortal( true, 0, 2 ) ) : ( 0.0f ) );
// Draw obtained portal color chips
int iSlot1State = ( ( m_bCanFirePortal1 ) ? ( 0 ) : ( 1 ) ); // FIXME: Portal gun might have only red but not blue;
int iSlot2State = ( ( m_bCanFirePortal2 ) ? ( 0 ) : ( 1 ) );
SetBodygroup( 1, iSlot1State );
SetBodygroup( 2, iSlot2State );
if ( pPlayer->GetViewModel() )
{
pPlayer->GetViewModel()->SetBodygroup( 1, iSlot1State );
pPlayer->GetViewModel()->SetBodygroup( 2, iSlot2State );
}
// HACK HACK! Used to make the gun visually change when going through a cleanser!
if ( m_fEffectsMaxSize1 > 4.0f )
{
m_fEffectsMaxSize1 -= gpGlobals->frametime * 400.0f;
if ( m_fEffectsMaxSize1 < 4.0f )
m_fEffectsMaxSize1 = 4.0f;
}
if ( m_fEffectsMaxSize2 > 4.0f )
{
m_fEffectsMaxSize2 -= gpGlobals->frametime * 400.0f;
if ( m_fEffectsMaxSize2 < 4.0f )
m_fEffectsMaxSize2 = 4.0f;
}
}
void CWeaponPortalgun::OpenProngs( bool bOpenProngs )
{
if ( m_bOpenProngs == bOpenProngs )
{
return;
}
m_bOpenProngs = bOpenProngs;
DoEffect( ( m_bOpenProngs ) ? ( EFFECT_HOLDING ) : ( EFFECT_READY ) );
SendWeaponAnim( ( m_bOpenProngs ) ? ( ACT_VM_PICKUP ) : ( ACT_VM_RELEASE ) );
}
void CWeaponPortalgun::InputChargePortal1( inputdata_t &inputdata )
{
DispatchParticleEffect( "portal_1_charge", PATTACH_POINT_FOLLOW, this, "muzzle" );
}
void CWeaponPortalgun::InputChargePortal2( inputdata_t &inputdata )
{
DispatchParticleEffect( "portal_2_charge", PATTACH_POINT_FOLLOW, this, "muzzle" );
}
void CWeaponPortalgun::FirePortal1( inputdata_t &inputdata )
{
FirePortal( false );
m_iLastFiredPortal = 1;
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner && pOwner->IsPlayer() )
{
WeaponSound( SINGLE );
}
else
{
WeaponSound( SINGLE_NPC );
}
}
void CWeaponPortalgun::FirePortal2( inputdata_t &inputdata )
{
FirePortal( true );
m_iLastFiredPortal = 2;
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner && pOwner->IsPlayer() )
{
WeaponSound( WPN_DOUBLE );
}
else
{
WeaponSound( DOUBLE_NPC );
}
}
void CWeaponPortalgun::FirePortalDirection1( inputdata_t &inputdata )
{
Vector vDirection;
inputdata.value.Vector3D( vDirection );
FirePortal( false, &vDirection );
m_iLastFiredPortal = 1;
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner && pOwner->IsPlayer() )
{
WeaponSound( SINGLE );
}
else
{
WeaponSound( SINGLE_NPC );
}
}
void CWeaponPortalgun::FirePortalDirection2( inputdata_t &inputdata )
{
Vector vDirection;
inputdata.value.Vector3D( vDirection );
FirePortal( true, &vDirection );
m_iLastFiredPortal = 2;
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner && pOwner->IsPlayer() )
{
WeaponSound( WPN_DOUBLE );
}
else
{
WeaponSound( DOUBLE_NPC );
}
}
float CWeaponPortalgun::TraceFirePortal( bool bPortal2, const Vector &vTraceStart, const Vector &vDirection, trace_t &tr, Vector &vFinalPosition, QAngle &qFinalAngles, int iPlacedBy, bool bTest /*= false*/ )
{
CTraceFilterSimpleClassnameList baseFilter( this, COLLISION_GROUP_NONE );
UTIL_Portal_Trace_Filter( &baseFilter );
CTraceFilterTranslateClones traceFilterPortalShot( &baseFilter );
Ray_t rayEyeArea;
rayEyeArea.Init( vTraceStart + vDirection * 24.0f, vTraceStart + vDirection * -24.0f );
float fMustBeCloserThan = 2.0f;
CProp_Portal *pNearPortal = UTIL_Portal_FirstAlongRay( rayEyeArea, fMustBeCloserThan );
if ( !pNearPortal )
{
// Check for portal near and infront of you
rayEyeArea.Init( vTraceStart + vDirection * -24.0f, vTraceStart + vDirection * 48.0f );
fMustBeCloserThan = 2.0f;
pNearPortal = UTIL_Portal_FirstAlongRay( rayEyeArea, fMustBeCloserThan );
}
if ( pNearPortal && pNearPortal->IsActivedAndLinked() )
{
iPlacedBy = PORTAL_PLACED_BY_PEDESTAL;
Vector vPortalForward;
pNearPortal->GetVectors( &vPortalForward, 0, 0 );
if ( vDirection.Dot( vPortalForward ) < 0.01f )
{
// If shooting out of the world, fizzle
if ( !bTest )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
pPortal->m_iDelayedFailure = ( ( pNearPortal->m_bIsPortal2 ) ? ( PORTAL_FIZZLE_NEAR_RED ) : ( PORTAL_FIZZLE_NEAR_BLUE ) );
VectorAngles( vPortalForward, pPortal->m_qDelayedAngles );
pPortal->m_vDelayedPosition = pNearPortal->GetAbsOrigin();
vFinalPosition = pPortal->m_vDelayedPosition;
qFinalAngles = pPortal->m_qDelayedAngles;
UTIL_TraceLine( vTraceStart - vDirection * 16.0f, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
return PORTAL_ANALOG_SUCCESS_NEAR;
}
UTIL_TraceLine( vTraceStart - vDirection * 16.0f, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
return PORTAL_ANALOG_SUCCESS_OVERLAP_LINKED;
}
}
// Trace to see where the portal hit
UTIL_TraceLine( vTraceStart, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
if ( !tr.DidHit() || tr.startsolid )
{
// If it didn't hit anything, fizzle
if ( !bTest )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
pPortal->m_iDelayedFailure = PORTAL_FIZZLE_NONE;
VectorAngles( -vDirection, pPortal->m_qDelayedAngles );
pPortal->m_vDelayedPosition = tr.endpos;
vFinalPosition = pPortal->m_vDelayedPosition;
qFinalAngles = pPortal->m_qDelayedAngles;
}
return PORTAL_ANALOG_SUCCESS_PASSTHROUGH_SURFACE;
}
// Trace to the surface to see if there's a rotating door in the way
CBaseEntity *list[1024];
Ray_t ray;
ray.Init( vTraceStart, tr.endpos );
int nCount = UTIL_EntitiesAlongRay( list, 1024, ray, 0 );
// Loop through all entities along the ray between the gun and the surface
for ( int i = 0; i < nCount; i++ )
{
// If the entity is a rotating door
if( FClassnameIs( list[i], "prop_door_rotating" ) )
{
// Check more precise door collision
CBasePropDoor *pRotatingDoor = static_cast<CBasePropDoor *>( list[i] );
Ray_t rayDoor;
rayDoor.Init( vTraceStart, vTraceStart + (vDirection * m_fMaxRange1) );
trace_t trDoor;
pRotatingDoor->TestCollision( rayDoor, 0, trDoor );
if ( trDoor.DidHit() )
{
// There's a door in the way
tr = trDoor;
if ( sv_portal_placement_debug.GetBool() )
{
Vector vMin;
Vector vMax;
Vector vZero = Vector( 0.0f, 0.0f, 0.0f );
list[ i ]->GetCollideable()->WorldSpaceSurroundingBounds( &vMin, &vMax );
NDebugOverlay::Box( vZero, vMin, vMax, 0, 255, 0, 128, 0.5f );
}
if ( !bTest )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
pPortal->m_iDelayedFailure = PORTAL_FIZZLE_CANT_FIT;
VectorAngles( tr.plane.normal, pPortal->m_qDelayedAngles );
pPortal->m_vDelayedPosition = trDoor.endpos;
vFinalPosition = pPortal->m_vDelayedPosition;
qFinalAngles = pPortal->m_qDelayedAngles;
}
return PORTAL_ANALOG_SUCCESS_CANT_FIT;
}
}
else if ( FClassnameIs( list[i], "trigger_portal_cleanser" ) )
{
CBaseTrigger *pTrigger = static_cast<CBaseTrigger*>( list[i] );
if ( pTrigger && !pTrigger->m_bDisabled )
{
Vector vMin;
Vector vMax;
pTrigger->GetCollideable()->WorldSpaceSurroundingBounds( &vMin, &vMax );
IntersectRayWithBox( ray.m_Start, ray.m_Delta, vMin, vMax, 0.0f, &tr );
tr.plane.normal = -vDirection;
if ( !bTest )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
pPortal->m_iDelayedFailure = PORTAL_FIZZLE_CLEANSER;
VectorAngles( tr.plane.normal, pPortal->m_qDelayedAngles );
pPortal->m_vDelayedPosition = tr.endpos;
vFinalPosition = pPortal->m_vDelayedPosition;
qFinalAngles = pPortal->m_qDelayedAngles;
}
return PORTAL_ANALOG_SUCCESS_CLEANSER;
}
}
}
Vector vUp( 0.0f, 0.0f, 1.0f );
if( ( tr.plane.normal.x > -0.001f && tr.plane.normal.x < 0.001f ) && ( tr.plane.normal.y > -0.001f && tr.plane.normal.y < 0.001f ) )
{
//plane is a level floor/ceiling
vUp = vDirection;
}
// Check that the placement succeed
VectorAngles( tr.plane.normal, vUp, qFinalAngles );
vFinalPosition = tr.endpos;
return VerifyPortalPlacement( CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2 ), vFinalPosition, qFinalAngles, iPlacedBy, bTest );
}
float CWeaponPortalgun::FirePortal( bool bPortal2, Vector *pVector /*= 0*/, bool bTest /*= false*/ )
{
bool bPlayer = false;
Vector vEye;
Vector vDirection;
Vector vTracerOrigin;
CBaseEntity *pOwner = GetOwner();
if ( pOwner && pOwner->IsPlayer() )
{
bPlayer = true;
}
if( bPlayer )
{
CPortal_Player *pPlayer = (CPortal_Player *)pOwner;
if ( !bTest && pPlayer )
{
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY, 0 );
}
Vector forward, right, up;
AngleVectors( pPlayer->EyeAngles(), &forward, &right, &up );
pPlayer->EyeVectors( &vDirection, NULL, NULL );
vEye = pPlayer->EyePosition();
// Check if the players eye is behind the portal they're in and translate it
VMatrix matThisToLinked;
CProp_Portal *pPlayerPortal = pPlayer->m_hPortalEnvironment;
if ( pPlayerPortal )
{
Vector ptPortalCenter;
Vector vPortalForward;
ptPortalCenter = pPlayerPortal->GetAbsOrigin();
pPlayerPortal->GetVectors( &vPortalForward, NULL, NULL );
Vector vEyeToPortalCenter = ptPortalCenter - vEye;
float fPortalDist = vPortalForward.Dot( vEyeToPortalCenter );
if( fPortalDist > 0.0f )
{
// Eye is behind the portal
matThisToLinked = pPlayerPortal->MatrixThisToLinked();
}
else
{
pPlayerPortal = NULL;
}
}
if ( pPlayerPortal )
{
UTIL_Portal_VectorTransform( matThisToLinked, forward, forward );
UTIL_Portal_VectorTransform( matThisToLinked, right, right );
UTIL_Portal_VectorTransform( matThisToLinked, up, up );
UTIL_Portal_VectorTransform( matThisToLinked, vDirection, vDirection );
UTIL_Portal_PointTransform( matThisToLinked, vEye, vEye );
if ( pVector )
{
UTIL_Portal_VectorTransform( matThisToLinked, *pVector, *pVector );
}
}
vTracerOrigin = vEye
+ forward * 30.0f
+ right * 4.0f
+ up * (-5.0f);
}
else
{
// This portalgun is not held by the player-- Fire using the muzzle attachment
Vector vecShootOrigin;
QAngle angShootDir;
GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir );
vEye = vecShootOrigin;
vTracerOrigin = vecShootOrigin;
AngleVectors( angShootDir, &vDirection, NULL, NULL );
}
if ( !bTest )
{
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
if ( pVector )
{
vDirection = *pVector;
}
Vector vTraceStart = vEye + (vDirection * m_fMinRange1);
Vector vFinalPosition;
QAngle qFinalAngles;
PortalPlacedByType ePlacedBy = ( bPlayer ) ? ( PORTAL_PLACED_BY_PLAYER ) : ( PORTAL_PLACED_BY_PEDESTAL );
trace_t tr;
float fPlacementSuccess = TraceFirePortal( bPortal2, vTraceStart, vDirection, tr, vFinalPosition, qFinalAngles, ePlacedBy, bTest );
if ( sv_portal_placement_never_fail.GetBool() )
{
fPlacementSuccess = 1.0f;
}
if ( !bTest )
{
CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
// If it was a failure, put the effect at exactly where the player shot instead of where the portal bumped to
if ( fPlacementSuccess < 0.5f )
vFinalPosition = tr.endpos;
pPortal->PlacePortal( vFinalPosition, qFinalAngles, fPlacementSuccess, true );
float fDelay = vTracerOrigin.DistTo( tr.endpos ) / ( ( bPlayer ) ? ( BLAST_SPEED ) : ( BLAST_SPEED_NON_PLAYER ) );
QAngle qFireAngles;
VectorAngles( vDirection, qFireAngles );
DoEffectBlast( pPortal->m_bIsPortal2, ePlacedBy, vTracerOrigin, vFinalPosition, qFireAngles, fDelay );
pPortal->SetContextThink( &CProp_Portal::DelayedPlacementThink, gpGlobals->curtime + fDelay, s_pDelayedPlacementContext );
pPortal->m_vDelayedPosition = vFinalPosition;
pPortal->m_hPlacedBy = this;
}
return fPlacementSuccess;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponPortalgun::StartEffects( void )
{
}
void CWeaponPortalgun::DestroyEffects( void )
{
// Stop everything
StopEffects();
}
//-----------------------------------------------------------------------------
// Purpose: Ready effects
//-----------------------------------------------------------------------------
void CWeaponPortalgun::DoEffectReady( void )
{
if ( m_pMiniGravHoldSound )
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundChangeVolume( m_pMiniGravHoldSound, 0.0, 0.1 );
}
}
//-----------------------------------------------------------------------------
// Holding effects
//-----------------------------------------------------------------------------
void CWeaponPortalgun::DoEffectHolding( void )
{
if ( m_pMiniGravHoldSound )
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundChangeVolume( m_pMiniGravHoldSound, 1.0, 0.1 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Shutdown for the weapon when it's holstered
//-----------------------------------------------------------------------------
void CWeaponPortalgun::DoEffectNone( void )
{
if ( m_pMiniGravHoldSound )
{
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundChangeVolume( m_pMiniGravHoldSound, 0.0, 0.1 );
}
}
void CC_UpgradePortalGun( void )
{
CPortal_Player *pPlayer = ToPortalPlayer( UTIL_GetCommandClient() );
CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
if ( pPortalGun )
{
pPortalGun->SetCanFirePortal1();
pPortalGun->SetCanFirePortal2();
}
}
static ConCommand upgrade_portal("upgrade_portalgun", CC_UpgradePortalGun, "Equips the player with a single portal portalgun. Use twice for a dual portal portalgun.\n\tArguments: none ", FCVAR_CHEAT);
static void change_portalgun_linkage_id_f( const CCommand &args )
{
if( sv_cheats->GetBool() == false ) //heavy handed version since setting the concommand with FCVAR_CHEATS isn't working like I thought
return;
if( args.ArgC() < 2 )
return;
unsigned char iNewID = (unsigned char)atoi( args[1] );
CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
int iWeaponCount = pPlayer->WeaponCount();
for( int i = 0; i != iWeaponCount; ++i )
{
CBaseCombatWeapon *pWeapon = pPlayer->GetWeapon(i);
if( pWeapon == NULL )
continue;
if( dynamic_cast<CWeaponPortalgun *>(pWeapon) != NULL )
{
CWeaponPortalgun *pPortalGun = (CWeaponPortalgun *)pWeapon;
pPortalGun->m_iPortalLinkageGroupID = iNewID;
break;
}
}
}
ConCommand change_portalgun_linkage_id( "change_portalgun_linkage_id", change_portalgun_linkage_id_f, "Changes the portal linkage ID for the portal gun held by the commanding player.", FCVAR_CHEAT );
+141
View File
@@ -0,0 +1,141 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef WEAPON_PORTALGUN_H
#define WEAPON_PORTALGUN_H
#ifdef _WIN32
#pragma once
#endif
#include "weapon_portalbasecombatweapon.h"
#include "prop_portal.h"
class CWeaponPortalgun : public CBasePortalCombatWeapon
{
DECLARE_DATADESC();
public:
DECLARE_CLASS( CWeaponPortalgun, CBasePortalCombatWeapon );
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
private:
CNetworkVar( bool, m_bCanFirePortal1 ); // Is able to use primary fire
CNetworkVar( bool, m_bCanFirePortal2 ); // Is able to use secondary fire
CNetworkVar( int, m_iLastFiredPortal ); // Which portal was placed last
CNetworkVar( bool, m_bOpenProngs ); // Which portal was placed last
CNetworkVar( float, m_fCanPlacePortal1OnThisSurface ); // Tells the gun if it can place on the surface it's pointing at
CNetworkVar( float, m_fCanPlacePortal2OnThisSurface ); // Tells the gun if it can place on the surface it's pointing at
public:
unsigned char m_iPortalLinkageGroupID; //which portal linkage group this gun is tied to, usually set by mapper, or inherited from owning player's index
// HACK HACK! Used to make the gun visually change when going through a cleanser!
CNetworkVar( float, m_fEffectsMaxSize1 );
CNetworkVar( float, m_fEffectsMaxSize2 );
public:
virtual const Vector& GetBulletSpread( void )
{
static Vector cone = VECTOR_CONE_10DEGREES;
return cone;
}
virtual void Precache ( void );
virtual void CreateSounds( void );
virtual void StopLoopingSounds( void );
virtual void OnRestore( void );
virtual void UpdateOnRemove( void );
void Spawn( void );
virtual void Activate();
void DoEffectBlast( bool bPortal2, int iPlacedBy, const Vector &ptStart, const Vector &ptFinalPos, const QAngle &qStartAngles, float fDelay );
virtual void OnPickedUp( CBaseCombatCharacter *pNewOwner );
virtual bool ShouldDrawCrosshair( void );
float GetPortal1Placablity( void ) { return m_fCanPlacePortal1OnThisSurface; }
float GetPortal2Placablity( void ) { return m_fCanPlacePortal2OnThisSurface; }
void SetLastFiredPortal( int iLastFiredPortal ) { m_iLastFiredPortal = iLastFiredPortal; }
int GetLastFiredPortal( void ) { return m_iLastFiredPortal; }
bool Reload( void );
void FillClip( void );
void CheckHolsterReload( void );
void ItemHolsterFrame( void );
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
bool Deploy( void );
void SetCanFirePortal1( bool bCanFire = true );
void SetCanFirePortal2( bool bCanFire = true );
float CanFirePortal1( void ) { return m_bCanFirePortal1; }
float CanFirePortal2( void ) { return m_bCanFirePortal2; }
void PrimaryAttack( void );
void SecondaryAttack( void );
void DelayAttack( float fDelay );
virtual bool PreThink( void );
virtual void Think( void );
void OpenProngs( bool bOpenProngs );
void InputChargePortal1( inputdata_t &inputdata );
void InputChargePortal2( inputdata_t &inputdata );
void FirePortal1( inputdata_t &inputdata );
void FirePortal2( inputdata_t &inputdata );
void FirePortalDirection1( inputdata_t &inputdata );
void FirePortalDirection2( inputdata_t &inputdata );
float TraceFirePortal( bool bPortal2, const Vector &vTraceStart, const Vector &vDirection, trace_t &tr, Vector &vFinalPosition, QAngle &qFinalAngles, int iPlacedBy, bool bTest = false );
float FirePortal( bool bPortal2, Vector *pVector = 0, bool bTest = false );
CSoundPatch *m_pMiniGravHoldSound;
// Outputs for portalgun
COutputEvent m_OnFiredPortal1; // Fires when the gun's first (blue) portal is fired
COutputEvent m_OnFiredPortal2; // Fires when the gun's second (red) portal is fired
void DryFire( void );
virtual float GetFireRate( void ) { return 0.7; };
void WeaponIdle( void );
PortalWeaponID GetWeaponID( void ) const { return WEAPON_PORTALGUN; }
protected:
void StartEffects( void ); // Initialize all sprites and beams
void StopEffects( bool stopSound = true ); // Hide all effects temporarily
void DestroyEffects( void ); // Destroy all sprites and beams
// Portalgun effects
void DoEffect( int effectType, Vector *pos = NULL );
void DoEffectClosed( void );
void DoEffectReady( void );
void DoEffectHolding( void );
void DoEffectNone( void );
CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
public:
DECLARE_ACTTABLE();
CWeaponPortalgun(void);
private:
CWeaponPortalgun( const CWeaponPortalgun & );
};
#endif // WEAPON_PORTALGUN_H