mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-15 21:26:57 +00:00
add hl1,portal,dod source code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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 )
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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 ¶ms );
|
||||
|
||||
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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ¶ms );
|
||||
|
||||
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 ¶ms )
|
||||
{
|
||||
// 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 );
|
||||
@@ -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
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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 );
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user