Merge branch 'develop' into feature/newer-toolsets

This commit is contained in:
Blixibon 2021-11-06 09:42:29 -05:00
commit 2dbd04e3d6
159 changed files with 8870 additions and 994 deletions

View File

@ -784,6 +784,10 @@ C_BaseAnimating::C_BaseAnimating() :
m_nPrevSequence = -1;
m_nRestoreSequence = -1;
m_pRagdoll = NULL;
m_pClientsideRagdoll = NULL;
#ifdef MAPBASE
m_pServerRagdoll = NULL;
#endif
m_builtRagdoll = false;
m_hitboxBoneCacheHandle = 0;
int i;
@ -1986,6 +1990,10 @@ CollideType_t C_BaseAnimating::GetCollideType( void )
return BaseClass::GetCollideType();
}
#ifdef MAPBASE
ConVar ai_death_pose_enabled( "ai_death_pose_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, which cancels sequence transitions while a NPC is ragdolling." );
#endif
//-----------------------------------------------------------------------------
// Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate
//-----------------------------------------------------------------------------
@ -2002,6 +2010,14 @@ void C_BaseAnimating::MaintainSequenceTransitions( IBoneSetup &boneSetup, float
return;
}
#ifdef MAPBASE
if ( IsAboutToRagdoll() && ai_death_pose_enabled.GetBool() )
{
m_nPrevNewSequenceParity = m_nNewSequenceParity;
return;
}
#endif
m_SequenceTransitioner.CheckForSequenceChange(
boneSetup.GetStudioHdr(),
GetSequence(),
@ -2781,14 +2797,29 @@ void C_BaseAnimating::CalculateIKLocks( float currentTime )
// debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 );
float d = (pTarget->est.pos - origin).Length();
Vector vecDelta = (origin - pTarget->est.pos);
float d = vecDelta.Length();
if ( d >= flDist)
continue;
flDist = d;
pTarget->SetPos( origin );
pTarget->SetAngles( angles );
#ifdef MAPBASE
// For blending purposes, IK attachments should obey weight
if ( pTarget->est.flWeight < 1.0f )
{
Quaternion qTarget;
AngleQuaternion( angles, qTarget );
QuaternionSlerp( pTarget->est.q, qTarget, pTarget->est.flWeight, pTarget->est.q );
pTarget->SetPos( pTarget->est.pos + (vecDelta * pTarget->est.flWeight) );
}
else
#endif
{
pTarget->SetPos( origin );
pTarget->SetAngles( angles );
}
// debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
}
@ -4948,26 +4979,26 @@ C_BaseAnimating *C_BaseAnimating::BecomeRagdollOnClient()
{
MoveToLastReceivedPosition( true );
GetAbsOrigin();
C_BaseAnimating *pRagdoll = CreateRagdollCopy();
m_pClientsideRagdoll = CreateRagdollCopy();
matrix3x4_t boneDelta0[MAXSTUDIOBONES];
matrix3x4_t boneDelta1[MAXSTUDIOBONES];
matrix3x4_t currentBones[MAXSTUDIOBONES];
const float boneDt = 0.1f;
GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
m_pClientsideRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
#ifdef MAPBASE_VSCRIPT
// Hook for ragdolling
if (m_ScriptScope.IsInitialized() && g_Hook_OnClientRagdoll.CanRunInScope( m_ScriptScope ))
{
// ragdoll
ScriptVariant_t args[] = { ScriptVariant_t( pRagdoll->GetScriptInstance() ) };
ScriptVariant_t args[] = { ScriptVariant_t( m_pClientsideRagdoll->GetScriptInstance() ) };
g_Hook_OnClientRagdoll.Call( m_ScriptScope, NULL, args );
}
#endif
return pRagdoll;
return m_pClientsideRagdoll;
}
bool C_BaseAnimating::InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints )

View File

@ -531,6 +531,10 @@ private:
public:
CRagdoll *m_pRagdoll;
C_BaseAnimating *m_pClientsideRagdoll; // From Alien Swarm SDK
#ifdef MAPBASE
C_BaseAnimating *m_pServerRagdoll; // Not from Alien Swarm SDK (note that this can exist without the entity having died)
#endif
// Texture group to use
int m_nSkin;

View File

@ -429,7 +429,8 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst )
END_RECV_TABLE()
#ifdef MAPBASE_VSCRIPT
ScriptHook_t CBaseEntity::g_Hook_UpdateOnRemove;
ScriptHook_t C_BaseEntity::g_Hook_UpdateOnRemove;
ScriptHook_t C_BaseEntity::g_Hook_ModifyEmitSoundParams;
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" )
@ -555,7 +556,11 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities
DEFINE_SCRIPTFUNC_NAMED( ScriptSetContextThink, "SetContextThink", "Set a think function on this entity." )
DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." )
DEFINE_SIMPLE_SCRIPTHOOK( C_BaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." )
BEGIN_SCRIPTHOOK( C_BaseEntity::g_Hook_ModifyEmitSoundParams, "ModifyEmitSoundParams", FIELD_VOID, "Called every time a sound is emitted on this entity, allowing for its parameters to be modified." )
DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT )
END_SCRIPTHOOK()
#endif // MAPBASE_VSCRIPT

View File

@ -296,6 +296,7 @@ public:
CScriptScope m_ScriptScope;
static ScriptHook_t g_Hook_UpdateOnRemove;
static ScriptHook_t g_Hook_ModifyEmitSoundParams;
#endif
// IClientUnknown overrides.

View File

@ -281,6 +281,7 @@ END_RECV_TABLE()
RecvPropInt ( RECVINFO( m_spawnflags ), 0, RecvProxy_ShiftPlayerSpawnflags ),
RecvPropBool ( RECVINFO( m_bDrawPlayerModelExternally ) ),
RecvPropBool ( RECVINFO( m_bInTriggerFall ) ),
#endif
END_RECV_TABLE()
@ -331,7 +332,10 @@ END_RECV_TABLE()
RecvPropString( RECVINFO(m_szLastPlaceName) ),
RecvPropEHandle(RECVINFO(m_hPostProcessCtrl)), // Send to everybody - for spectating
#ifdef MAPBASE // From Alien Swarm SDK
RecvPropEHandle( RECVINFO( m_hPostProcessCtrl ) ), // Send to everybody - for spectating
RecvPropEHandle( RECVINFO( m_hColorCorrectionCtrl ) ), // Send to everybody - for spectating
#endif
#if defined USES_ECON_ITEMS
RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ),
@ -2917,6 +2921,7 @@ void C_BasePlayer::UpdateFogBlend( void )
}
}
#ifdef MAPBASE // From Alien Swarm SDK
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
@ -2925,6 +2930,15 @@ C_PostProcessController* C_BasePlayer::GetActivePostProcessController() const
return m_hPostProcessCtrl.Get();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
C_ColorCorrection* C_BasePlayer::GetActiveColorCorrection() const
{
return m_hColorCorrectionCtrl.Get();
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------

View File

@ -23,7 +23,10 @@
#include "hintsystem.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "c_env_fog_controller.h"
#ifdef MAPBASE // From Alien Swarm SDK
#include "c_postprocesscontroller.h"
#include "c_colorcorrection.h"
#endif
#include "igameevents.h"
#include "GameEventListener.h"
@ -381,7 +384,10 @@ public:
void UpdateFogController( void );
void UpdateFogBlend( void );
#ifdef MAPBASE // From Alien Swarm SDK
C_PostProcessController* GetActivePostProcessController() const;
C_ColorCorrection* GetActiveColorCorrection() const;
#endif
float GetFOVTime( void ){ return m_flFOVTime; }
@ -458,6 +464,8 @@ public:
// Allows the player's model to draw on non-main views, like monitors or mirrors.
bool m_bDrawPlayerModelExternally;
bool m_bInTriggerFall;
#endif
protected:
@ -645,7 +653,10 @@ private:
// One for left and one for right side of step
StepSoundCache_t m_StepSoundCache[ 2 ];
CNetworkHandle(C_PostProcessController, m_hPostProcessCtrl); // active postprocessing controller
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkHandle( C_PostProcessController, m_hPostProcessCtrl ); // active postprocessing controller
CNetworkHandle( C_ColorCorrection, m_hColorCorrectionCtrl ); // active FXVolume color correction
#endif
public:

View File

@ -6,6 +6,7 @@
//===========================================================================//
#include "cbase.h"
#include "c_colorcorrection.h"
#include "filesystem.h"
#include "cdll_client_int.h"
#include "colorcorrectionmgr.h"
@ -17,45 +18,27 @@
static ConVar mat_colcorrection_disableentities( "mat_colcorrection_disableentities", "0", FCVAR_NONE, "Disable map color-correction entities" );
//------------------------------------------------------------------------------
// Purpose : Color correction entity with radial falloff
//------------------------------------------------------------------------------
class C_ColorCorrection : public C_BaseEntity
{
public:
DECLARE_CLASS( C_ColorCorrection, C_BaseEntity );
DECLARE_CLIENTCLASS();
C_ColorCorrection();
virtual ~C_ColorCorrection();
void OnDataChanged(DataUpdateType_t updateType);
bool ShouldDraw();
void ClientThink();
private:
Vector m_vecOrigin;
float m_minFalloff;
float m_maxFalloff;
float m_flCurWeight;
char m_netLookupFilename[MAX_PATH];
bool m_bEnabled;
ClientCCHandle_t m_CCHandle;
};
#ifdef MAPBASE // From Alien Swarm SDK
static ConVar mat_colcorrection_forceentitiesclientside( "mat_colcorrection_forceentitiesclientside", "0", FCVAR_CHEAT, "Forces color correction entities to be updated on the client" );
#endif
IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrection, DT_ColorCorrection, CColorCorrection)
RecvPropVector( RECVINFO(m_vecOrigin) ),
RecvPropFloat( RECVINFO(m_minFalloff) ),
RecvPropFloat( RECVINFO(m_maxFalloff) ),
RecvPropFloat( RECVINFO(m_flCurWeight) ),
#ifdef MAPBASE // From Alien Swarm SDK
RecvPropFloat( RECVINFO(m_flMaxWeight) ),
RecvPropFloat( RECVINFO(m_flFadeInDuration) ),
RecvPropFloat( RECVINFO(m_flFadeOutDuration) ),
#endif
RecvPropString( RECVINFO(m_netLookupFilename) ),
RecvPropBool( RECVINFO(m_bEnabled) ),
#ifdef MAPBASE // From Alien Swarm SDK
RecvPropBool( RECVINFO(m_bMaster) ),
RecvPropBool( RECVINFO(m_bClientSide) ),
RecvPropBool( RECVINFO(m_bExclusive) )
#endif
END_RECV_TABLE()
@ -65,14 +48,43 @@ END_RECV_TABLE()
//------------------------------------------------------------------------------
C_ColorCorrection::C_ColorCorrection()
{
#ifdef MAPBASE // From Alien Swarm SDK
m_minFalloff = -1.0f;
m_maxFalloff = -1.0f;
m_flFadeInDuration = 0.0f;
m_flFadeOutDuration = 0.0f;
m_flCurWeight = 0.0f;
m_flMaxWeight = 1.0f;
m_netLookupFilename[0] = '\0';
m_bEnabled = false;
m_bMaster = false;
m_bExclusive = false;
#endif
m_CCHandle = INVALID_CLIENT_CCHANDLE;
#ifdef MAPBASE // From Alien Swarm SDK
m_bFadingIn = false;
m_flFadeStartWeight = 0.0f;
m_flFadeStartTime = 0.0f;
m_flFadeDuration = 0.0f;
#endif
}
C_ColorCorrection::~C_ColorCorrection()
{
#ifdef MAPBASE // From Alien Swarm SDK
g_pColorCorrectionMgr->RemoveColorCorrectionEntity( this, m_CCHandle );
#else
g_pColorCorrectionMgr->RemoveColorCorrection( m_CCHandle );
#endif
}
#ifdef MAPBASE // From Alien Swarm SDK
bool C_ColorCorrection::IsClientSide() const
{
return m_bClientSide || mat_colcorrection_forceentitiesclientside.GetBool();
}
#endif
//------------------------------------------------------------------------------
// Purpose :
@ -87,11 +99,21 @@ void C_ColorCorrection::OnDataChanged(DataUpdateType_t updateType)
{
if ( m_CCHandle == INVALID_CLIENT_CCHANDLE )
{
#ifdef MAPBASE // From Alien Swarm SDK
// forming a unique name without extension
char cleanName[MAX_PATH];
V_StripExtension( m_netLookupFilename, cleanName, sizeof( cleanName ) );
char name[MAX_PATH];
Q_snprintf( name, MAX_PATH, "%s_%d", cleanName, entindex() );
m_CCHandle = g_pColorCorrectionMgr->AddColorCorrectionEntity( this, name, m_netLookupFilename );
#else
char filename[MAX_PATH];
Q_strncpy( filename, m_netLookupFilename, MAX_PATH );
m_CCHandle = g_pColorCorrectionMgr->AddColorCorrection( filename );
SetNextClientThink( ( m_CCHandle != INVALID_CLIENT_CCHANDLE ) ? CLIENT_THINK_ALWAYS : CLIENT_THINK_NEVER );
#endif
}
}
}
@ -104,6 +126,129 @@ bool C_ColorCorrection::ShouldDraw()
return false;
}
#ifdef MAPBASE // From Alien Swarm SDK
void C_ColorCorrection::Update( C_BasePlayer *pPlayer, float ccScale )
{
Assert( m_CCHandle != INVALID_CLIENT_CCHANDLE );
if ( mat_colcorrection_disableentities.GetInt() )
{
// Allow the colorcorrectionui panel (or user) to turn off color-correction entities
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive );
return;
}
// fade weight on client
if ( IsClientSide() )
{
m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f );
}
if( !m_bEnabled && m_flCurWeight == 0.0f )
{
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive );
return;
}
Vector playerOrigin = pPlayer->GetAbsOrigin();
float weight = 0;
if ( ( m_minFalloff != -1 ) && ( m_maxFalloff != -1 ) && m_minFalloff != m_maxFalloff )
{
float dist = (playerOrigin - m_vecOrigin).Length();
weight = (dist-m_minFalloff) / (m_maxFalloff-m_minFalloff);
if ( weight<0.0f ) weight = 0.0f;
if ( weight>1.0f ) weight = 1.0f;
}
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_flCurWeight * ( 1.0 - weight ) * ccScale, m_bExclusive );
}
void C_ColorCorrection::EnableOnClient( bool bEnable, bool bSkipFade )
{
if ( !IsClientSide() )
{
return;
}
m_bFadingIn = bEnable;
// initialize countdown timer
m_flFadeStartWeight = m_flCurWeight;
float flFadeTimeScale = 1.0f;
if ( m_flMaxWeight != 0.0f )
{
flFadeTimeScale = m_flCurWeight / m_flMaxWeight;
}
if ( m_bFadingIn )
{
flFadeTimeScale = 1.0f - flFadeTimeScale;
}
if ( bSkipFade )
{
flFadeTimeScale = 0.0f;
}
StartFade( flFadeTimeScale * ( m_bFadingIn ? m_flFadeInDuration : m_flFadeOutDuration ) );
// update the clientside weight once here, in case the fade duration is 0
m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f );
}
Vector C_ColorCorrection::GetOrigin()
{
return m_vecOrigin;
}
float C_ColorCorrection::GetMinFalloff()
{
return m_minFalloff;
}
float C_ColorCorrection::GetMaxFalloff()
{
return m_maxFalloff;
}
void C_ColorCorrection::SetWeight( float fWeight )
{
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, fWeight, false );
}
void C_ColorCorrection::StartFade( float flDuration )
{
m_flFadeStartTime = gpGlobals->curtime;
m_flFadeDuration = MAX( flDuration, 0.0f );
}
float C_ColorCorrection::GetFadeRatio() const
{
float flRatio = 1.0f;
if ( m_flFadeDuration != 0.0f )
{
flRatio = ( gpGlobals->curtime - m_flFadeStartTime ) / m_flFadeDuration;
flRatio = clamp( flRatio, 0.0f, 1.0f );
}
return flRatio;
}
bool C_ColorCorrection::IsFadeTimeElapsed() const
{
return ( ( gpGlobals->curtime - m_flFadeStartTime ) > m_flFadeDuration ) ||
( ( gpGlobals->curtime - m_flFadeStartTime ) < 0.0f );
}
void UpdateColorCorrectionEntities( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrection **pList, int listCount )
{
for ( int i = 0; i < listCount; i++ )
{
pList[i]->Update(pPlayer, ccScale);
}
}
#else
void C_ColorCorrection::ClientThink()
{
if ( m_CCHandle == INVALID_CLIENT_CCHANDLE )
@ -141,6 +286,7 @@ void C_ColorCorrection::ClientThink()
BaseClass::ClientThink();
}
#endif

View File

@ -0,0 +1,88 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013.
// Although technically a new Mapbase file, it only serves to move otherwise identical code,
// so most code and repo conventions will pretend it was always there.
//
// --------------------------------------------------------------------
//
// Purpose: Color correction entity with simple radial falloff
//
//=============================================================================//
#ifndef C_COLORCORRECTION_H
#define C_COLORCORRECTION_H
#ifdef _WIN32
#pragma once
#endif
#include "colorcorrectionmgr.h"
//------------------------------------------------------------------------------
// Purpose : Color correction entity with radial falloff
//------------------------------------------------------------------------------
class C_ColorCorrection : public C_BaseEntity
{
public:
DECLARE_CLASS( C_ColorCorrection, C_BaseEntity );
DECLARE_CLIENTCLASS();
C_ColorCorrection();
virtual ~C_ColorCorrection();
void OnDataChanged(DataUpdateType_t updateType);
bool ShouldDraw();
#ifdef MAPBASE // From Alien Swarm SDK
virtual void Update(C_BasePlayer *pPlayer, float ccScale);
bool IsMaster() const { return m_bMaster; }
bool IsClientSide() const;
bool IsExclusive() const { return m_bExclusive; }
void EnableOnClient( bool bEnable, bool bSkipFade = false );
Vector GetOrigin();
float GetMinFalloff();
float GetMaxFalloff();
void SetWeight( float fWeight );
protected:
void StartFade( float flDuration );
float GetFadeRatio() const;
bool IsFadeTimeElapsed() const;
#else
void ClientThink();
private:
#endif
Vector m_vecOrigin;
float m_minFalloff;
float m_maxFalloff;
float m_flCurWeight;
char m_netLookupFilename[MAX_PATH];
bool m_bEnabled;
#ifdef MAPBASE // From Alien Swarm SDK
float m_flFadeInDuration;
float m_flFadeOutDuration;
float m_flMaxWeight;
bool m_bMaster;
bool m_bClientSide;
bool m_bExclusive;
bool m_bFadingIn;
float m_flFadeStartWeight;
float m_flFadeStartTime;
float m_flFadeDuration;
#endif
ClientCCHandle_t m_CCHandle;
};
#endif

View File

@ -36,9 +36,26 @@ public:
void OnDataChanged(DataUpdateType_t updateType);
bool ShouldDraw();
#ifdef MAPBASE // From Alien Swarm SDK
void Update( C_BasePlayer *pPlayer, float ccScale );
void StartTouch( C_BaseEntity *pOther );
void EndTouch( C_BaseEntity *pOther );
#else
void ClientThink();
#endif
private:
#ifdef MAPBASE // From Alien Swarm SDK
float m_LastEnterWeight;
float m_LastEnterTime;
float m_LastExitWeight;
float m_LastExitTime;
bool m_bEnabled;
float m_MaxWeight;
float m_FadeDuration;
#endif
float m_Weight;
char m_lookupFilename[MAX_PATH];
@ -46,6 +63,11 @@ private:
};
IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrectionVolume, DT_ColorCorrectionVolume, CColorCorrectionVolume)
#ifdef MAPBASE // From Alien Swarm SDK
RecvPropBool( RECVINFO( m_bEnabled ) ),
RecvPropFloat( RECVINFO( m_MaxWeight ) ),
RecvPropFloat( RECVINFO( m_FadeDuration ) ),
#endif
RecvPropFloat( RECVINFO(m_Weight) ),
RecvPropString( RECVINFO(m_lookupFilename) ),
END_RECV_TABLE()
@ -82,11 +104,21 @@ void C_ColorCorrectionVolume::OnDataChanged(DataUpdateType_t updateType)
{
if ( m_CCHandle == INVALID_CLIENT_CCHANDLE )
{
#ifdef MAPBASE // From Alien Swarm SDK
// forming a unique name without extension
char cleanName[MAX_PATH];
V_StripExtension( m_lookupFilename, cleanName, sizeof( cleanName ) );
char name[MAX_PATH];
Q_snprintf( name, MAX_PATH, "%s_%d", cleanName, entindex() );
m_CCHandle = g_pColorCorrectionMgr->AddColorCorrectionVolume( this, name, m_lookupFilename );
#else
char filename[MAX_PATH];
Q_strncpy( filename, m_lookupFilename, MAX_PATH );
m_CCHandle = g_pColorCorrectionMgr->AddColorCorrection( filename );
SetNextClientThink( ( m_CCHandle != INVALID_CLIENT_CCHANDLE ) ? CLIENT_THINK_ALWAYS : CLIENT_THINK_NEVER );
#endif
}
}
}
@ -99,11 +131,95 @@ bool C_ColorCorrectionVolume::ShouldDraw()
return false;
}
#ifdef MAPBASE // From Alien Swarm SDK
//--------------------------------------------------------------------------------------------------------
void C_ColorCorrectionVolume::StartTouch( CBaseEntity *pEntity )
{
m_LastEnterTime = gpGlobals->curtime;
m_LastEnterWeight = m_Weight;
}
//--------------------------------------------------------------------------------------------------------
void C_ColorCorrectionVolume::EndTouch( CBaseEntity *pEntity )
{
m_LastExitTime = gpGlobals->curtime;
m_LastExitWeight = m_Weight;
}
void C_ColorCorrectionVolume::Update( C_BasePlayer *pPlayer, float ccScale )
{
if ( pPlayer )
{
bool isTouching = CollisionProp()->IsPointInBounds( pPlayer->EyePosition() );
bool wasTouching = m_LastEnterTime > m_LastExitTime;
if ( isTouching && !wasTouching )
{
StartTouch( pPlayer );
}
else if ( !isTouching && wasTouching )
{
EndTouch( pPlayer );
}
}
if( !m_bEnabled )
{
m_Weight = 0.0f;
}
else
{
if( m_LastEnterTime > m_LastExitTime )
{
// we most recently entered the volume
if( m_Weight < 1.0f )
{
float dt = gpGlobals->curtime - m_LastEnterTime;
float weight = m_LastEnterWeight + dt / ((1.0f-m_LastEnterWeight)*m_FadeDuration);
if( weight>1.0f )
weight = 1.0f;
m_Weight = weight;
}
}
else
{
// we most recently exitted the volume
if( m_Weight > 0.0f )
{
float dt = gpGlobals->curtime - m_LastExitTime;
float weight = (1.0f-m_LastExitWeight) + dt / (m_LastExitWeight*m_FadeDuration);
if( weight>1.0f )
weight = 1.0f;
m_Weight = 1.0f - weight;
}
}
}
// Vector entityPosition = GetAbsOrigin();
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_Weight * ccScale );
}
void UpdateColorCorrectionVolumes( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrectionVolume **pList, int listCount )
{
for ( int i = 0; i < listCount; i++ )
{
pList[i]->Update(pPlayer, ccScale);
}
}
#else
void C_ColorCorrectionVolume::ClientThink()
{
Vector entityPosition = GetAbsOrigin();
g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_Weight );
}
#endif

View File

@ -40,7 +40,10 @@ ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); //
ConVar r_RainParticleDensity( "r_RainParticleDensity", "1", FCVAR_NONE, "Density of Particle Rain 0-1" );
#ifdef MAPBASE
ConVar r_RainParticleClampOffset( "r_RainParticleClampOffset", "112", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems" );
ConVar r_RainParticleClampOffset_Rain( "r_RainParticleClampOffset_Rain", "120", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain' type." );
ConVar r_RainParticleClampOffset_Ash( "r_RainParticleClampOffset_Ash", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Ash' type." );
ConVar r_RainParticleClampOffset_RainStorm( "r_RainParticleClampOffset_RainStorm", "112", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain Storm' type." );
ConVar r_RainParticleClampOffset_Snow( "r_RainParticleClampOffset_Snow", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Snow' type." );
ConVar r_RainParticleClampDebug( "r_RainParticleClampDebug", "0", FCVAR_NONE, "Enables debug code for precipitation particle system clamping" );
#endif
@ -951,6 +954,70 @@ void CClient_Precipitation::CreateParticlePrecip( void )
UpdateParticlePrecip( pPlayer );
}
#ifdef MAPBASE
void CClient_Precipitation::ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar )
{
Vector mins, maxs;
modelinfo->GetModelBounds( GetModel(), mins, maxs );
// Account for precipitation height
maxs.z += 180;
Vector vecOrigin; //= WorldSpaceCenter();
VectorLerp( mins, maxs, 0.5f, vecOrigin );
maxs -= vecOrigin;
mins -= vecOrigin;
//float flMax = r_RainParticleClampOffset.GetFloat();
float flMax = 0;
switch (m_nPrecipType)
{
case PRECIPITATION_TYPE_PARTICLERAIN:
flMax = r_RainParticleClampOffset_Rain.GetFloat();
break;
case PRECIPITATION_TYPE_PARTICLEASH:
flMax = r_RainParticleClampOffset_Ash.GetFloat();
break;
case PRECIPITATION_TYPE_PARTICLERAINSTORM:
flMax = r_RainParticleClampOffset_RainStorm.GetFloat();
break;
case PRECIPITATION_TYPE_PARTICLESNOW:
flMax = r_RainParticleClampOffset_Snow.GetFloat();
break;
}
Vector addend( flMax, flMax, 0 );
mins += addend;
maxs -= addend;
if (flMax > 0)
{
// Unless this is extruding outwards, make sure the offset isn't inverting the bounds.
// This means precipitation triggers with bounds less than offset*2 will turn into a thin line
// and the involved precipitation will pretty much be spatial at all times, which is okay.
mins.x = clamp( mins.x, -FLT_MAX, -1 );
mins.y = clamp( mins.y, -FLT_MAX, -1 );
maxs.x = clamp( maxs.x, 1, FLT_MAX );
maxs.y = clamp( maxs.y, 1, FLT_MAX );
}
if (r_RainParticleClampDebug.GetBool())
debugoverlay->AddBoxOverlay( vecOrigin, mins, maxs, vec3_angle, 255, 0, 0, 128, 0.15f );
maxs += vecOrigin;
mins += vecOrigin;
CalcClosestPointOnAABB( mins, maxs, vPlayerPos, vPlayerPos );
CalcClosestPointOnAABB( mins, maxs, vOffsetPos, vOffsetPos );
CalcClosestPointOnAABB( mins, maxs, vOffsetPosNear, vOffsetPosNear );
CalcClosestPointOnAABB( mins, maxs, vOffsetPosFar, vOffsetPosFar );
}
#endif
void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer )
{
if ( !pPlayer )
@ -980,44 +1047,7 @@ void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer )
#ifdef MAPBASE
if (m_spawnflags & SF_PRECIP_PARTICLE_CLAMP)
{
Vector mins, maxs;
modelinfo->GetModelBounds( GetModel(), mins, maxs );
// Account for precipitation height
maxs.z += 180;
Vector vecOrigin; //= WorldSpaceCenter();
VectorLerp( mins, maxs, 0.5f, vecOrigin );
maxs -= vecOrigin;
mins -= vecOrigin;
float flMax = r_RainParticleClampOffset.GetFloat();
Vector addend( flMax, flMax, 0 );
mins += addend;
maxs -= addend;
if (flMax > 0)
{
// Unless this is extruding outwards, make sure the offset isn't inverting the bounds.
// This means precipitation triggers with bounds less than offset*2 will turn into a thin line
// and the involved precipitation will pretty much be spatial at all times, which is okay.
mins.x = clamp( mins.x, -FLT_MAX, -1 );
mins.y = clamp( mins.y, -FLT_MAX, -1 );
maxs.x = clamp( maxs.x, 1, FLT_MAX );
maxs.y = clamp( maxs.y, 1, FLT_MAX );
}
if (r_RainParticleClampDebug.GetBool())
debugoverlay->AddBoxOverlay( vecOrigin, mins, maxs, vec3_angle, 255, 0, 0, 128, 0.15f );
maxs += vecOrigin;
mins += vecOrigin;
CalcClosestPointOnAABB( mins, maxs, vPlayerPos, vPlayerPos );
CalcClosestPointOnAABB( mins, maxs, vOffsetPos, vOffsetPos );
CalcClosestPointOnAABB( mins, maxs, vOffsetPosNear, vOffsetPosNear );
CalcClosestPointOnAABB( mins, maxs, vOffsetPosFar, vOffsetPosFar );
ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar );
}
#endif
@ -1236,6 +1266,13 @@ void CClient_Precipitation::DispatchInnerParticlePrecip( C_BasePlayer *pPlayer,
Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * m_flParticleInnerDist ); // 100.0
Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity;
#ifdef MAPBASE
if (m_spawnflags & SF_PRECIP_PARTICLE_CLAMP)
{
ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar );
}
#endif
#ifdef MAPBASE
if (!(m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER))
#endif

View File

@ -130,6 +130,10 @@ private:
void CreateAshParticle( void );
void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity );
#ifdef MAPBASE
void ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar );
#endif
// Information helpful in creating and rendering particles
IMaterial *m_MatHandle; // material used

View File

@ -48,6 +48,9 @@ protected:
int m_iCachedDesiredOverlay;
int m_iCurrentOverlay;
float m_flCurrentOverlayTime;
#ifdef MAPBASE
int m_iOverlayIndex;
#endif
};
IMPLEMENT_CLIENTCLASS_DT( C_EnvScreenOverlay, DT_EnvScreenOverlay, CEnvScreenOverlay )
@ -56,6 +59,9 @@ IMPLEMENT_CLIENTCLASS_DT( C_EnvScreenOverlay, DT_EnvScreenOverlay, CEnvScreenOve
RecvPropFloat( RECVINFO( m_flStartTime ) ),
RecvPropInt( RECVINFO( m_iDesiredOverlay ) ),
RecvPropBool( RECVINFO( m_bIsActive ) ),
#ifdef MAPBASE
RecvPropInt( RECVINFO( m_iOverlayIndex ) ),
#endif
END_RECV_TABLE()
//-----------------------------------------------------------------------------
@ -77,7 +83,11 @@ void C_EnvScreenOverlay::PostDataUpdate( DataUpdateType_t updateType )
BaseClass::PostDataUpdate( updateType );
// If we have a start time now, start the overlays going
#ifdef MAPBASE
if ( m_bIsActive && m_flStartTime > 0 && (view->GetScreenOverlayMaterial() == NULL || (m_iOverlayIndex != -1 && view->GetIndexedScreenOverlayMaterial(m_iOverlayIndex) == NULL)) )
#else
if ( m_bIsActive && m_flStartTime > 0 && view->GetScreenOverlayMaterial() == NULL )
#endif
{
StartOverlays();
}
@ -111,7 +121,16 @@ void C_EnvScreenOverlay::StopOverlays( void )
if ( m_bWasActive && !m_bIsActive )
{
view->SetScreenOverlayMaterial( NULL );
#ifdef MAPBASE
if (m_iOverlayIndex != -1)
{
view->SetIndexedScreenOverlayMaterial( m_iOverlayIndex, NULL );
}
else
#endif
{
view->SetScreenOverlayMaterial( NULL );
}
}
}
@ -163,7 +182,16 @@ void C_EnvScreenOverlay::StartCurrentOverlay( void )
IMaterial *pMaterial = materials->FindMaterial( m_iszOverlayNames[m_iCurrentOverlay], TEXTURE_GROUP_CLIENT_EFFECTS, false );
if ( !IsErrorMaterial( pMaterial ) )
{
view->SetScreenOverlayMaterial( pMaterial );
#ifdef MAPBASE
if (m_iOverlayIndex != -1)
{
view->SetIndexedScreenOverlayMaterial( m_iOverlayIndex, pMaterial );
}
else
#endif
{
view->SetScreenOverlayMaterial( pMaterial );
}
}
else
{

File diff suppressed because it is too large Load Diff

View File

@ -112,6 +112,15 @@ public:
C_BasePlayer *GetPlayerOwner( void );
bool IsInputOnlyToOwner( void );
#ifdef MAPBASE
void GetSize( float &width, float &height ) const { width = m_flWidth; height = m_flHeight; }
void GetPixelSize( int &width, int &height ) const { width = m_nPixelWidth; height = m_nPixelHeight; }
void SetWidth( float flWidth ) { m_flWidth = flWidth; }
void SetHeight( float flHeight ) { m_flHeight = flHeight; }
void SetPixelWidth( int nWidth ) { m_nPixelWidth = nWidth; }
void SetPixelHeight( int nHeight ) { m_nPixelHeight = nHeight; }
#endif
private:
// Vgui screen management
void CreateVguiScreen( const char *pTypeName );

View File

@ -292,8 +292,11 @@ ClientModeShared::ClientModeShared()
m_pWeaponSelection = NULL;
m_nRootSize[ 0 ] = m_nRootSize[ 1 ] = -1;
#ifdef MAPBASE // From Alien Swarm SDK
m_pCurrentPostProcessController = NULL;
m_PostProcessLerpTimer.Invalidate();
m_pCurrentColorCorrection = NULL;
#endif
#if defined( REPLAY_ENABLED )
m_pReplayReminderPanel = NULL;
@ -635,6 +638,43 @@ void ClientModeShared::Update()
}
}
#ifdef MAPBASE // From Alien Swarm SDK
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ClientModeShared::OnColorCorrectionWeightsReset( void )
{
C_ColorCorrection *pNewColorCorrection = NULL;
C_ColorCorrection *pOldColorCorrection = m_pCurrentColorCorrection;
C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
if ( pPlayer )
{
pNewColorCorrection = pPlayer->GetActiveColorCorrection();
}
if ( pNewColorCorrection != pOldColorCorrection )
{
if ( pOldColorCorrection )
{
pOldColorCorrection->EnableOnClient( false );
}
if ( pNewColorCorrection )
{
pNewColorCorrection->EnableOnClient( true, pOldColorCorrection == NULL );
}
m_pCurrentColorCorrection = pNewColorCorrection;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float ClientModeShared::GetColorCorrectionScale( void ) const
{
return 1.0f;
}
#endif
//-----------------------------------------------------------------------------
// This processes all input before SV Move messages are sent
//-----------------------------------------------------------------------------

View File

@ -91,6 +91,11 @@ public:
virtual void ProcessInput(bool bActive);
virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd );
virtual void Update();
#ifdef MAPBASE // From Alien Swarm SDK
virtual void OnColorCorrectionWeightsReset( void );
virtual float GetColorCorrectionScale( void ) const;
virtual void ClearCurrentColorCorrection() { m_pCurrentColorCorrection = NULL; }
#endif
// Input
virtual int KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
@ -165,12 +170,16 @@ private:
CBaseHudWeaponSelection *m_pWeaponSelection;
int m_nRootSize[2];
#ifdef MAPBASE // From Alien Swarm SDK
void UpdatePostProcessingEffects();
const C_PostProcessController* m_pCurrentPostProcessController;
PostProcessParameters_t m_CurrentPostProcessParameters;
PostProcessParameters_t m_LerpStartPostProcessParameters, m_LerpEndPostProcessParameters;
CountdownTimer m_PostProcessLerpTimer;
CHandle<C_ColorCorrection> m_pCurrentColorCorrection;
#endif
};
#endif // CLIENTMODE_NORMAL_H

View File

@ -8,6 +8,12 @@
#include "cbase.h"
#include "tier0/vprof.h"
#include "colorcorrectionmgr.h"
#ifdef MAPBASE // From Alien Swarm SDK
#include "clientmode_shared.h" //"clientmode.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
#endif
//------------------------------------------------------------------------------
@ -16,6 +22,13 @@
static CColorCorrectionMgr s_ColorCorrectionMgr;
CColorCorrectionMgr *g_pColorCorrectionMgr = &s_ColorCorrectionMgr;
#ifdef MAPBASE // From Alien Swarm SDK
static ConVar mat_colcorrection_editor( "mat_colcorrection_editor", "0" );
static CUtlVector<C_ColorCorrection *> g_ColorCorrectionList;
static CUtlVector<C_ColorCorrectionVolume *> g_ColorCorrectionVolumeList;
#endif
//------------------------------------------------------------------------------
// Constructor
@ -62,10 +75,89 @@ void CColorCorrectionMgr::RemoveColorCorrection( ClientCCHandle_t h )
}
}
#ifdef MAPBASE // From Alien Swarm SDK
ClientCCHandle_t CColorCorrectionMgr::AddColorCorrectionEntity( C_ColorCorrection *pEntity, const char *pName, const char *pFileName )
{
ClientCCHandle_t h = AddColorCorrection(pName, pFileName);
if ( h != INVALID_CLIENT_CCHANDLE )
{
Assert(g_ColorCorrectionList.Find(pEntity) == -1);
g_ColorCorrectionList.AddToTail(pEntity);
}
return h;
}
void CColorCorrectionMgr::RemoveColorCorrectionEntity( C_ColorCorrection *pEntity, ClientCCHandle_t h)
{
RemoveColorCorrection(h);
g_ColorCorrectionList.FindAndFastRemove(pEntity);
}
ClientCCHandle_t CColorCorrectionMgr::AddColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, const char *pName, const char *pFileName )
{
ClientCCHandle_t h = AddColorCorrection(pName, pFileName);
if ( h != INVALID_CLIENT_CCHANDLE )
{
Assert(g_ColorCorrectionVolumeList.Find(pVolume) == -1);
g_ColorCorrectionVolumeList.AddToTail(pVolume);
}
return h;
}
void CColorCorrectionMgr::RemoveColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, ClientCCHandle_t h)
{
RemoveColorCorrection(h);
g_ColorCorrectionVolumeList.FindAndFastRemove(pVolume);
}
#endif
//------------------------------------------------------------------------------
// Modify color correction weights
//------------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight, bool bExclusive )
{
if ( h != INVALID_CLIENT_CCHANDLE )
{
SetWeightParams_t params = { h, flWeight, bExclusive };
m_colorCorrectionWeights.AddToTail( params );
if( bExclusive && m_bHaveExclusiveWeight && ( flWeight != 0.0f ) )
{
DevWarning( "Found multiple active color_correction entities with exclusive setting enabled. This is invalid.\n" );
}
if ( bExclusive )
{
m_bHaveExclusiveWeight = true;
m_flExclusiveWeight = flWeight;
}
}
}
void CColorCorrectionMgr::CommitColorCorrectionWeights()
{
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
for ( int i = 0; i < m_colorCorrectionWeights.Count(); i++ )
{
ColorCorrectionHandle_t ccHandle = reinterpret_cast<ColorCorrectionHandle_t>( m_colorCorrectionWeights[i].handle );
float flWeight = m_colorCorrectionWeights[i].flWeight;
if ( !m_colorCorrectionWeights[i].bExclusive )
{
flWeight = (1.0f - m_flExclusiveWeight ) * m_colorCorrectionWeights[i].flWeight;
}
pRenderContext->SetLookupWeight( ccHandle, flWeight );
// FIXME: NOTE! This doesn't work if the same handle has
// its weight set twice with no intervening calls to ResetColorCorrectionWeights
// which, at the moment, is true
if ( flWeight != 0.0f )
{
++m_nActiveWeightCount;
}
}
m_colorCorrectionWeights.RemoveAll();
}
#else
void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight )
{
if ( h != INVALID_CLIENT_CCHANDLE )
@ -83,6 +175,7 @@ void CColorCorrectionMgr::SetColorCorrectionWeight( ClientCCHandle_t h, float fl
}
}
}
#endif
void CColorCorrectionMgr::ResetColorCorrectionWeights()
{
@ -93,6 +186,11 @@ void CColorCorrectionMgr::ResetColorCorrectionWeights()
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->ResetLookupWeights();
m_nActiveWeightCount = 0;
#ifdef MAPBASE // From Alien Swarm SDK
m_bHaveExclusiveWeight = false;
m_flExclusiveWeight = 0.0f;
m_colorCorrectionWeights.RemoveAll();
#endif
}
void CColorCorrectionMgr::SetResetable( ClientCCHandle_t h, bool bResetable )
@ -113,7 +211,34 @@ void CColorCorrectionMgr::SetResetable( ClientCCHandle_t h, bool bResetable )
//------------------------------------------------------------------------------
// Is color correction active?
//------------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
bool CColorCorrectionMgr::HasNonZeroColorCorrectionWeights() const
{
return ( m_nActiveWeightCount != 0 ) || mat_colcorrection_editor.GetBool();
}
void CColorCorrectionMgr::UpdateColorCorrection()
{
ResetColorCorrectionWeights();
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
IClientMode *pClientMode = GetClientModeNormal(); //GetClientMode();
Assert( pClientMode );
if ( !pPlayer || !pClientMode )
{
return;
}
pClientMode->OnColorCorrectionWeightsReset();
float ccScale = pClientMode->GetColorCorrectionScale();
UpdateColorCorrectionEntities( pPlayer, ccScale, g_ColorCorrectionList.Base(), g_ColorCorrectionList.Count() );
UpdateColorCorrectionVolumes( pPlayer, ccScale, g_ColorCorrectionVolumeList.Base(), g_ColorCorrectionVolumeList.Count() );
CommitColorCorrectionWeights();
}
#else
bool CColorCorrectionMgr::HasNonZeroColorCorrectionWeights() const
{
return ( m_nActiveWeightCount != 0 );
}
#endif

View File

@ -14,6 +14,10 @@
#include "igamesystem.h"
#ifdef MAPBASE // From Alien Swarm SDK
class C_ColorCorrection;
class C_ColorCorrectionVolume;
#endif
//------------------------------------------------------------------------------
// Purpose : Singleton manager for color correction on the client
@ -35,8 +39,21 @@ public:
ClientCCHandle_t AddColorCorrection( const char *pName, const char *pFileName = NULL );
void RemoveColorCorrection( ClientCCHandle_t );
#ifdef MAPBASE // From Alien Swarm SDK
ClientCCHandle_t AddColorCorrectionEntity( C_ColorCorrection *pEntity, const char *pName, const char *pFileName = NULL );
void RemoveColorCorrectionEntity( C_ColorCorrection *pEntity, ClientCCHandle_t );
ClientCCHandle_t AddColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, const char *pName, const char *pFileName = NULL );
void RemoveColorCorrectionVolume( C_ColorCorrectionVolume *pVolume, ClientCCHandle_t );
#endif
// Modify color correction weights
#ifdef MAPBASE // From Alien Swarm SDK
void SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight, bool bExclusive = false );
void UpdateColorCorrection();
#else
void SetColorCorrectionWeight( ClientCCHandle_t h, float flWeight );
#endif
void ResetColorCorrectionWeights();
void SetResetable( ClientCCHandle_t h, bool bResetable );
@ -45,8 +62,27 @@ public:
private:
int m_nActiveWeightCount;
#ifdef MAPBASE // From Alien Swarm SDK
bool m_bHaveExclusiveWeight;
float m_flExclusiveWeight;
struct SetWeightParams_t
{
ClientCCHandle_t handle;
float flWeight;
bool bExclusive;
};
CUtlVector< SetWeightParams_t > m_colorCorrectionWeights;
void CommitColorCorrectionWeights();
#endif
};
#ifdef MAPBASE // From Alien Swarm SDK
void UpdateColorCorrectionEntities( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrection **pList, int listCount );
void UpdateColorCorrectionVolumes( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrectionVolume **pList, int listCount );
#endif
//------------------------------------------------------------------------------
// Singleton access

View File

@ -25,6 +25,13 @@ extern ConVar r_drawmodeldecals;
ImpactSoundRouteFn g_pImpactSoundRouteFn = NULL;
#ifdef MAPBASE
ConVar g_ragdoll_steal_impacts_client( "g_ragdoll_steal_impacts_client", "1", FCVAR_NONE, "Allows clientside death ragdolls to \"steal\" impacts from their source entities. This fixes issues with NPCs dying before decals are applied." );
ConVar g_ragdoll_steal_impacts_server( "g_ragdoll_steal_impacts_server", "1", FCVAR_NONE, "Allows serverside death ragdolls to \"steal\" impacts from their source entities. This fixes issues with NPCs dying before decals are applied." );
ConVar g_ragdoll_client_impact_decals( "g_ragdoll_client_impact_decals", "1", FCVAR_NONE, "Applies decals to clientside ragdolls when they are hit." );
#endif
//==========================================================================================================================
// RAGDOLL ENUMERATOR
//==========================================================================================================================
@ -32,7 +39,11 @@ CRagdollEnumerator::CRagdollEnumerator( Ray_t& shot, int iDamageType )
{
m_rayShot = shot;
m_iDamageType = iDamageType;
#ifdef MAPBASE
m_pHitEnt = NULL;
#else
m_bHit = false;
#endif
}
IterationRetval_t CRagdollEnumerator::EnumElement( IHandleEntity *pHandleEntity )
@ -57,7 +68,11 @@ IterationRetval_t CRagdollEnumerator::EnumElement( IHandleEntity *pHandleEntity
if ( tr.fraction < 1.0 )
{
pModel->ImpactTrace( &tr, m_iDamageType, NULL );
#ifdef MAPBASE
m_pHitEnt = pModel;
#else
m_bHit = true;
#endif
//FIXME: Yes? No?
return ITERATION_STOP;
@ -84,6 +99,22 @@ bool FX_AffectRagdolls( Vector vecOrigin, Vector vecStart, int iDamageType )
return ragdollEnum.Hit();
}
#ifdef MAPBASE
C_BaseAnimating *FX_AffectRagdolls_GetHit( Vector vecOrigin, Vector vecStart, int iDamageType )
{
// don't do this when lots of ragdolls are simulating
if ( s_RagdollLRU.CountRagdolls(true) > 1 )
return false;
Ray_t shotRay;
shotRay.Init( vecStart, vecOrigin );
CRagdollEnumerator ragdollEnum( shotRay, iDamageType );
partition->EnumerateElementsAlongRay( PARTITION_CLIENT_RESPONSIVE_EDICTS, shotRay, false, &ragdollEnum );
return ragdollEnum.GetHit();
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
// Input : &data -
@ -104,6 +135,22 @@ bool Impact( Vector &vecOrigin, Vector &vecStart, int iMaterial, int iDamageType
Assert ( pEntity );
#ifdef MAPBASE
// If the entity already has a ragdoll that was created on the current tick, use that ragdoll instead.
// This allows the killing damage's decals to show up on the ragdoll.
if (C_BaseAnimating *pAnimating = pEntity->GetBaseAnimating())
{
if (pAnimating->m_pClientsideRagdoll && WasRagdollCreatedOnCurrentTick( pAnimating->m_pClientsideRagdoll ) && g_ragdoll_steal_impacts_client.GetBool())
{
pEntity = pAnimating->m_pClientsideRagdoll;
}
else if (pAnimating->m_pServerRagdoll && WasRagdollCreatedOnCurrentTick( pAnimating->m_pServerRagdoll ) && g_ragdoll_steal_impacts_server.GetBool())
{
pEntity = pAnimating->m_pServerRagdoll;
}
}
#endif
// Clear out the trace
memset( &tr, 0, sizeof(trace_t));
tr.fraction = 1.0f;
@ -115,13 +162,52 @@ bool Impact( Vector &vecOrigin, Vector &vecStart, int iMaterial, int iDamageType
VectorMA( vecStart, flLength + 8.0f, shotDir, traceExt );
// Attempt to hit ragdolls
bool bHitRagdoll = false;
#ifdef MAPBASE
if ( !pEntity->IsClientCreated() )
{
C_BaseAnimating *pRagdoll = FX_AffectRagdolls_GetHit( vecOrigin, vecStart, iDamageType );
if (pRagdoll)
{
bHitRagdoll = true;
if (g_ragdoll_client_impact_decals.GetBool())
{
pEntity = pRagdoll;
// HACKHACK: Get the ragdoll's nearest bone for its material
int iNearestMaterial = 0;
float flNearestDistSqr = FLT_MAX;
IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
for ( int i = 0; i < count; i++ )
{
Vector vecPosition;
QAngle angAngles;
pList[i]->GetPosition( &vecPosition, &angAngles );
float flDistSqr = (vecStart - vecPosition).LengthSqr();
if (flDistSqr < flNearestDistSqr)
{
iNearestMaterial = pList[i]->GetMaterialIndex();
flNearestDistSqr = flDistSqr;
}
}
// Get the material from the surfaceprop
surfacedata_t *psurfaceData = physprops->GetSurfaceData( iNearestMaterial );
iMaterial = psurfaceData->game.material;
}
}
}
#else
if ( !pEntity->IsClientCreated() )
{
bHitRagdoll = FX_AffectRagdolls( vecOrigin, vecStart, iDamageType );
}
#endif
if ( (nFlags & IMPACT_NODECAL) == 0 )
{

View File

@ -58,12 +58,21 @@ public:
// Actual work code
virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
#ifdef MAPBASE
bool Hit( void ) const { return m_pHitEnt != NULL; }
C_BaseAnimating *GetHit( void ) { return m_pHitEnt; }
#else
bool Hit( void ) const { return m_bHit; }
#endif
private:
Ray_t m_rayShot;
int m_iDamageType;
#ifdef MAPBASE
C_BaseAnimating *m_pHitEnt;
#else
bool m_bHit;
#endif
};
#endif // FX_IMPACT_H

View File

@ -146,6 +146,32 @@ bool CBaseViewport::LoadHudAnimations( void )
return true;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Reloads HUD animations after loading a map-specific HUD animations file.
//-----------------------------------------------------------------------------
void CBaseViewport::ReloadHudAnimations( void )
{
// Force a reload
if ( LoadHudAnimations() == false )
{
// Fall back to just the main
if ( m_pAnimController->SetScriptFile( GetVPanel(), "scripts/HudAnimations.txt", true ) == false )
{
Assert(0);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Loads a map-specific HUD animations file.
//-----------------------------------------------------------------------------
bool CBaseViewport::LoadCustomHudAnimations( const char *pszFile )
{
return m_pAnimController->SetScriptFile( GetVPanel(), pszFile, true );
}
#endif
//================================================================
CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport")
{

View File

@ -73,6 +73,10 @@ public:
public: // IGameEventListener:
virtual void FireGameEvent( IGameEvent * event);
#ifdef MAPBASE
bool LoadCustomHudAnimations( const char *pszFile );
void ReloadHudAnimations( void );
#endif
protected:

View File

@ -32,6 +32,16 @@ struct creditname_t
float flTimeAdd;
float flTimeStart;
int iSlot;
#ifdef MAPBASE
// New credits stuff
Color cColorOverride;
// Images
int iImageID = -1;
float flImageScale = 1.0f;
#endif
};
#define CREDITS_FILE "scripts/credits.txt"
@ -93,6 +103,10 @@ private:
void DrawOutroCreditsName( void );
void DrawIntroCreditsName( void );
void DrawLogo( void );
#ifdef MAPBASE
void DrawOutroCreditFont( const char *pCreditName, float flYPos, vgui::HFont hTFont, const Color &cColor, int iScreenWidth, int iDivisor = 2 );
void DrawOutroCreditTexture( int iImageID, float flYPos, float flImageScale, const Color &cColor, int iScreenWidth, int iDivisor = 2 );
#endif
void PrepareLogo( float flTime );
void PrepareOutroCredits( void );
@ -102,6 +116,10 @@ private:
void PrepareLine( vgui::HFont hFont, char const *pchLine );
#ifdef MAPBASE
int GetOrAllocateImageID( const char *szFileName );
#endif
CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "Default" );
CPanelAnimationVar( Color, m_TextColor, "TextColor", "FgColor" );
@ -110,7 +128,10 @@ private:
float m_flScrollTime;
float m_flSeparation;
#ifdef MAPBASE
int m_iEndLines;
int m_iEndLines;
float m_flEndLinesFadeHoldTime;
bool m_bAllowColumns;
CUtlDict<int, int> m_ImageDict;
#endif
float m_flFadeTime;
bool m_bLastOneInPlace;
@ -202,6 +223,20 @@ void CHudCredits::Clear( void )
m_bLastOneInPlace = false;
m_Alpha = m_TextColor[3];
m_iLogoState = LOGO_FADEOFF;
#ifdef MAPBASE
if ( surface() )
{
for (int i = m_ImageDict.Count()-1; i >= 0; i--)
{
if (m_ImageDict[i] != -1)
{
surface()->DestroyTextureID( m_ImageDict[i] );
m_ImageDict.RemoveAt( i );
}
}
}
#endif
}
//-----------------------------------------------------------------------------
@ -229,7 +264,11 @@ void CHudCredits::ReadNames( KeyValues *pKeyValue )
{
creditname_t Credits;
V_strcpy_safe( Credits.szCreditName, pKVNames->GetName() );
#ifdef MAPBASE
V_strcpy_safe( Credits.szFontName, pKVNames->GetString( (const char *)NULL, "Default" ) );
#else
V_strcpy_safe( Credits.szFontName, pKeyValue->GetString( Credits.szCreditName, "Default" ) );
#endif
m_CreditsList.AddToTail( Credits );
pKVNames = pKVNames->GetNextKey();
@ -248,6 +287,8 @@ void CHudCredits::ReadParams( KeyValues *pKeyValue )
m_flSeparation = pKeyValue->GetFloat( "separation", 5 );
#ifdef MAPBASE
m_iEndLines = pKeyValue->GetInt( "endlines", 1 );
m_flEndLinesFadeHoldTime = pKeyValue->GetFloat( "endlines_fadeholdtime", ( IsConsole() ? 2.0f : 10.0f ) ); // "360 certification requires that we not hold a static image too long."
m_bAllowColumns = pKeyValue->GetBool( "allow_columns", false );
#endif
m_flFadeInTime = pKeyValue->GetFloat( "fadeintime", 1 );
@ -300,10 +341,46 @@ void CHudCredits::DrawOutroCreditsName( void )
if ( pCredit == NULL )
continue;
#ifdef MAPBASE
vgui::HScheme scheme = GetScheme();
#else
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
#endif
vgui::HFont m_hTFont = INVALID_FONT;
int iFontTall = surface()->GetFontTall ( m_hTFont );
int iFontTall = 1;
#ifdef MAPBASE
if (pCredit->iImageID != -1)
{
// Get the size of the tallest image if there's multiple
int iFontWide;
if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" ))
{
CUtlStringList outStrings;
V_SplitString( pCredit->szCreditName, "\t", outStrings );
FOR_EACH_VEC( outStrings, i )
{
int iTempTall;
surface()->DrawGetTextureSize( GetOrAllocateImageID( outStrings[i] ), iFontWide, iTempTall );
if (iTempTall > iFontTall)
iFontTall = iTempTall;
}
outStrings.PurgeAndDeleteElements();
}
else
{
surface()->DrawGetTextureSize( GetOrAllocateImageID( pCredit->szCreditName ), iFontWide, iFontTall );
}
iFontTall = ((float)iFontTall * pCredit->flImageScale);
}
else
#endif
{
m_hTFont = vgui::scheme()->GetIScheme( scheme )->GetFont( pCredit->szFontName, true );
iFontTall = surface()->GetFontTall( m_hTFont );
}
if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall )
{
@ -317,6 +394,9 @@ void CHudCredits::DrawOutroCreditsName( void )
Color cColor = m_TextColor;
#ifdef MAPBASE
if (pCredit->cColorOverride.a() > 0)
cColor = pCredit->cColorOverride;
// Some lines should stick around and fade out
if ( i >= m_CreditsList.Count()-m_iEndLines )
#else
@ -333,8 +413,12 @@ void CHudCredits::DrawOutroCreditsName( void )
{
m_bLastOneInPlace = true;
#ifdef MAPBASE
m_flFadeTime = gpGlobals->curtime + m_flEndLinesFadeHoldTime;
#else
// 360 certification requires that we not hold a static image too long.
m_flFadeTime = gpGlobals->curtime + ( IsConsole() ? 2.0f : 10.0f );
#endif
}
}
else
@ -364,6 +448,50 @@ void CHudCredits::DrawOutroCreditsName( void )
if ( pCredit->bActive == false )
continue;
#ifdef MAPBASE
// Credits separated by tabs should appear divided
if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" ))
{
CUtlStringList outStrings;
V_SplitString( pCredit->szCreditName, "\t", outStrings );
int iDivisor = 1 + outStrings.Count();
if (pCredit->iImageID != -1)
{
FOR_EACH_VEC( outStrings, i )
{
int iImageID = GetOrAllocateImageID( outStrings[i] );
// Center the image if needed
int iImageWide, iImageTall = 1;
surface()->DrawGetTextureSize( iImageID, iImageWide, iImageTall );
if (iImageTall < iFontTall)
{
DrawOutroCreditTexture( iImageID, pCredit->flYPos + ((iFontTall * 0.5f) - (iImageTall * 0.5f)), pCredit->flImageScale, cColor, iWidth*(i + 1), iDivisor );
}
else
{
DrawOutroCreditTexture( iImageID, pCredit->flYPos, pCredit->flImageScale, cColor, iWidth*(i + 1), iDivisor );
}
}
}
else
{
FOR_EACH_VEC( outStrings, i )
{
DrawOutroCreditFont( outStrings[i], pCredit->flYPos, m_hTFont, cColor, iWidth*(i + 1), iDivisor );
}
}
outStrings.PurgeAndDeleteElements();
}
else if (pCredit->iImageID != -1)
{
DrawOutroCreditTexture( pCredit->iImageID, pCredit->flYPos, pCredit->flImageScale, cColor, iWidth, 2 );
}
else
{
DrawOutroCreditFont( pCredit->szCreditName, pCredit->flYPos, m_hTFont, cColor, iWidth, 2 );
}
#else
surface()->DrawSetTextFont( m_hTFont );
surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
@ -382,9 +510,56 @@ void CHudCredits::DrawOutroCreditsName( void )
surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), pCredit->flYPos );
surface()->DrawUnicodeString( unicode );
#endif
}
}
#ifdef MAPBASE
void CHudCredits::DrawOutroCreditFont( const char *pCreditName, float flYPos, vgui::HFont hTFont, const Color &cColor, int iScreenWidth, int iDivisor )
{
surface()->DrawSetTextFont( hTFont );
surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
wchar_t unicode[256];
if ( pCreditName[0] == '#' )
{
g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(pCreditName), 0 );
}
else
{
g_pVGuiLocalize->ConvertANSIToUnicode( pCreditName, unicode, sizeof( unicode ) );
}
int iStringWidth = GetStringPixelWidth( unicode, hTFont );
// ((iScreenWidth*iMultiplier) / iDivisor)
// When needed, just multiply iScreenWidth before sending to the function
surface()->DrawSetTextPos( (iScreenWidth / iDivisor) - (iStringWidth / 2), flYPos );
surface()->DrawUnicodeString( unicode );
}
void CHudCredits::DrawOutroCreditTexture( int iImageID, float flYPos, float flImageScale, const Color &cColor, int iScreenWidth, int iDivisor )
{
int iImageWide, iImageTall;
surface()->DrawGetTextureSize( iImageID, iImageWide, iImageTall );
// Scale for resolution
flImageScale *= ((float)GetTall() / 900.0f);
iImageWide = ((float)(iImageWide) * flImageScale);
iImageTall = ((float)(iImageTall) * flImageScale);
iImageWide /= 2;
//iImageTall /= 2;
iScreenWidth /= iDivisor;
surface()->DrawSetColor( cColor );
surface()->DrawSetTexture( iImageID );
surface()->DrawTexturedRect( iScreenWidth - iImageWide, flYPos, iScreenWidth + iImageWide, flYPos + iImageTall );
}
#endif
void CHudCredits::DrawLogo( void )
{
if( m_iLogoState == LOGO_FADEOFF )
@ -464,7 +639,11 @@ void CHudCredits::DrawLogo( void )
Q_snprintf( szLogoFont, sizeof( szLogoFont ), "WeaponIcons" );
}
#ifdef MAPBASE
vgui::HScheme scheme = GetScheme();
#else
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
#endif
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( szLogoFont );
int iFontTall = surface()->GetFontTall ( m_hTFont );
@ -544,14 +723,26 @@ void CHudCredits::DrawIntroCreditsName( void )
if ( pCredit->bActive == false )
continue;
#ifdef MAPBASE
vgui::HScheme scheme = GetScheme();
#else
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
#endif
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName );
float localTime = gpGlobals->curtime - pCredit->flTimeStart;
surface()->DrawSetTextFont( m_hTFont );
#ifdef MAPBASE
Color cColor = m_cColor;
if (pCredit->cColorOverride.a() > 0)
cColor = pCredit->cColorOverride;
surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * cColor[3] );
#else
surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * m_cColor[3] );
#endif
wchar_t unicode[256];
g_pVGuiLocalize->ConvertANSIToUnicode( pCredit->szCreditName, unicode, sizeof( unicode ) );
@ -692,17 +883,119 @@ void CHudCredits::PrepareOutroCredits( void )
if ( pCredit == NULL )
continue;
#ifdef MAPBASE
vgui::HScheme scheme = GetScheme();
#else
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
#endif
pCredit->flYPos = iHeight;
pCredit->bActive = false;
#ifdef MAPBASE
if (pCredit->szFontName[0] == '$')
{
if (V_strncmp( pCredit->szFontName + 1, "Image", 5 ) == 0)
{
if (pCredit->szFontName[6] == ';')
{
CUtlStringList outStrings;
V_SplitString( pCredit->szFontName, ";", outStrings );
FOR_EACH_VEC( outStrings, i )
{
switch (i)
{
// Get scale
case 1:
pCredit->flImageScale = atof( outStrings[i] );
break;
iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation;
// Get color
case 2:
int tmp[4];
UTIL_StringToIntArray( tmp, 4, outStrings[i] );
pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] );
break;
}
}
outStrings.PurgeAndDeleteElements();
}
PrepareLine( m_hTFont, pCredit->szCreditName );
// Get the size of the tallest image if there's multiple
int iFontWide, iFontTall = 1;
if (m_bAllowColumns && V_strstr( pCredit->szCreditName, "\t" ))
{
CUtlStringList outStrings;
V_SplitString( pCredit->szCreditName, "\t", outStrings );
FOR_EACH_VEC( outStrings, i )
{
pCredit->iImageID = GetOrAllocateImageID( outStrings[i] );
int iTempTall;
surface()->DrawGetTextureSize( pCredit->iImageID, iFontWide, iTempTall );
if (iTempTall > iFontTall)
iFontTall = iTempTall;
}
outStrings.PurgeAndDeleteElements();
}
else
{
pCredit->iImageID = GetOrAllocateImageID( pCredit->szCreditName );
surface()->DrawGetTextureSize( pCredit->iImageID, iFontWide, iFontTall );
}
pCredit->flYPos = iHeight;
pCredit->bActive = false;
iHeight += ((float)iFontTall * pCredit->flImageScale * ((float)GetTall() / 900.0f)) + m_flSeparation;
Msg( "'%s' is image type (image scale is %f)\n", pCredit->szCreditName, pCredit->flImageScale );
}
else
{
//Msg( "'%s' is not an image type\n", pCredit->szFontName + 1 );
}
}
else
#endif
{
#ifdef MAPBASE
if (V_strstr( pCredit->szFontName, ";" ))
{
CUtlStringList outStrings;
V_SplitString( pCredit->szFontName, ";", outStrings );
FOR_EACH_VEC( outStrings, i )
{
switch (i)
{
// Get color
case 1:
int tmp[4];
UTIL_StringToIntArray( tmp, 4, outStrings[i] );
pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] );
break;
}
}
Q_strncpy( pCredit->szFontName, outStrings[0], sizeof( pCredit->szFontName ) );
outStrings.PurgeAndDeleteElements();
}
#endif
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme( scheme )->GetFont( pCredit->szFontName, true );
pCredit->flYPos = iHeight;
pCredit->bActive = false;
iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation;
PrepareLine( m_hTFont, pCredit->szCreditName );
}
}
#ifdef MAPBASE
// Check if the last line has a color override. If it does, use that as the alpha for the fadeout
if (m_CreditsList.Tail().cColorOverride.a() != 0)
m_Alpha = m_CreditsList.Tail().cColorOverride.a();
#endif
SetActive( true );
g_iCreditsPixelHeight = iHeight;
@ -721,7 +1014,34 @@ void CHudCredits::PrepareIntroCredits( void )
if ( pCredit == NULL )
continue;
#ifdef MAPBASE
if (V_strstr( pCredit->szFontName, ";" ))
{
CUtlStringList outStrings;
V_SplitString( pCredit->szFontName, ";", outStrings );
FOR_EACH_VEC( outStrings, i )
{
switch (i)
{
// Get color
case 1:
int tmp[4];
UTIL_StringToIntArray( tmp, 4, outStrings[i] );
pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] );
break;
}
}
Q_strncpy( pCredit->szFontName, outStrings[0], sizeof( pCredit->szFontName ) );
outStrings.PurgeAndDeleteElements();
}
#endif
#ifdef MAPBASE
vgui::HScheme scheme = GetScheme();
#else
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
#endif
vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName );
pCredit->flYPos = m_flY + ( iSlot * surface()->GetFontTall ( m_hTFont ) );
@ -749,6 +1069,21 @@ void CHudCredits::PrepareIntroCredits( void )
SetActive( true );
}
#ifdef MAPBASE
int CHudCredits::GetOrAllocateImageID( const char *szFileName )
{
int iIndex = m_ImageDict.Find( szFileName );
if (iIndex == m_ImageDict.InvalidIndex())
{
iIndex = surface()->CreateNewTextureID();
m_ImageDict.Insert( szFileName, iIndex );
surface()->DrawSetTextureFile( iIndex, szFileName, true, false );
return iIndex;
}
return m_ImageDict[iIndex];
}
#endif
void CHudCredits::MsgFunc_CreditsMsg( bf_read &msg )
{
m_iCreditsType = msg.ReadByte();

View File

@ -1612,6 +1612,9 @@ struct WorkUnitParams
clr = Color( 255, 255, 255, 255 );
newline = false;
font = 0;
#ifdef MAPBASE
customFont = false;
#endif
}
~WorkUnitParams()
@ -1657,6 +1660,9 @@ struct WorkUnitParams
Color clr;
bool newline;
vgui::HFont font;
#ifdef MAPBASE
bool customFont;
#endif
};
void CHudCloseCaption::AddWorkUnit( CCloseCaptionItem *item,
@ -1771,27 +1777,58 @@ void CHudCloseCaption::ComputeStreamWork( int available_width, CCloseCaptionItem
{
AddWorkUnit( item, params );
params.italic = !params.italic;
#ifdef MAPBASE
params.customFont = false;
#endif
}
else if ( !wcscmp( cmd, L"B" ) )
{
AddWorkUnit( item, params );
params.bold = !params.bold;
#ifdef MAPBASE
params.customFont = false;
#endif
}
#ifdef MAPBASE
else if ( !wcscmp( cmd, L"font" ) )
{
AddWorkUnit( item, params );
vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
if ( args[0] != 0 )
{
char font[64];
g_pVGuiLocalize->ConvertUnicodeToANSI( args, font, sizeof( font ) );
params.font = pScheme->GetFont( font );
params.customFont = true;
}
else
{
params.customFont = false;
}
}
#endif
continue;
}
int font;
if ( IsPC() )
vgui::HFont useF = params.font;
#ifdef MAPBASE
if (params.customFont == false)
#endif
{
font = params.GetFontNumber();
int font;
if ( IsPC() )
{
font = params.GetFontNumber();
}
else
{
font = streamlen >= cc_smallfontlength.GetInt() ? CCFONT_SMALL : CCFONT_NORMAL;
}
useF = m_hFonts[font];
params.font = useF;
}
else
{
font = streamlen >= cc_smallfontlength.GetInt() ? CCFONT_SMALL : CCFONT_NORMAL;
}
vgui::HFont useF = m_hFonts[font];
params.font = useF;
int w, h;
@ -2630,6 +2667,124 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile )
g_AsyncCaptionResourceManager.SetDbInfo( m_AsyncCaptions );
}
#ifdef MAPBASE
void CHudCloseCaption::AddAdditionalCaptionDictionary( const char *dbfile, CUtlVector<CUtlSymbol> &outPathSymbols )
{
CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Adding additional caption dictionary \"%s\"\n", dbfile );
g_AsyncCaptionResourceManager.Clear();
char searchPaths[4096];
filesystem->GetSearchPath( "MOD", true, searchPaths, sizeof( searchPaths ) );
for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) )
{
if ( IsX360() && ( filesystem->GetDVDMode() == DVDMODE_STRICT ) && !V_stristr( path, ".zip" ) )
{
// only want zip paths
continue;
}
char fullpath[MAX_PATH];
Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, dbfile );
Q_FixSlashes( fullpath );
if ( IsX360() )
{
char fullpath360[MAX_PATH];
UpdateOrCreateCaptionFile( fullpath, fullpath360, sizeof( fullpath360 ) );
Q_strncpy( fullpath, fullpath360, sizeof( fullpath ) );
}
// Seach for this dictionary. If it already exists, remove it.
for (int i = 0; i < m_AsyncCaptions.Count(); ++i)
{
if (FStrEq( m_AsyncCaptions[i].m_DataBaseFile.String(), fullpath ))
{
m_AsyncCaptions.Remove( i );
break;
}
}
FileHandle_t fh = filesystem->Open( fullpath, "rb" );
if ( FILESYSTEM_INVALID_HANDLE != fh )
{
MEM_ALLOC_CREDIT();
CUtlBuffer dirbuffer;
AsyncCaption_t& entry = m_AsyncCaptions[ m_AsyncCaptions.AddToTail() ];
// Read the header
filesystem->Read( &entry.m_Header, sizeof( entry.m_Header ), fh );
if ( entry.m_Header.magic != COMPILED_CAPTION_FILEID )
Error( "Invalid file id for %s\n", fullpath );
if ( entry.m_Header.version != COMPILED_CAPTION_VERSION )
Error( "Invalid file version for %s\n", fullpath );
if ( entry.m_Header.directorysize < 0 || entry.m_Header.directorysize > 64 * 1024 )
Error( "Invalid directory size %d for %s\n", entry.m_Header.directorysize, fullpath );
//if ( entry.m_Header.blocksize != MAX_BLOCK_SIZE )
// Error( "Invalid block size %d, expecting %d for %s\n", entry.m_Header.blocksize, MAX_BLOCK_SIZE, fullpath );
int directoryBytes = entry.m_Header.directorysize * sizeof( CaptionLookup_t );
entry.m_CaptionDirectory.EnsureCapacity( entry.m_Header.directorysize );
dirbuffer.EnsureCapacity( directoryBytes );
filesystem->Read( dirbuffer.Base(), directoryBytes, fh );
filesystem->Close( fh );
entry.m_CaptionDirectory.CopyArray( (const CaptionLookup_t *)dirbuffer.PeekGet(), entry.m_Header.directorysize );
entry.m_CaptionDirectory.RedoSort( true );
entry.m_DataBaseFile = fullpath;
outPathSymbols.AddToTail( entry.m_DataBaseFile );
}
}
g_AsyncCaptionResourceManager.SetDbInfo( m_AsyncCaptions );
}
void CHudCloseCaption::AddCustomCaptionFile( char const *file, CUtlVector<CUtlSymbol> &outPathSymbols )
{
//
// 'file' should be something like "maps/mapbase_demo01_closecaption_%language%"
//
CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Adding custom caption file \"%s\"\n", file );
if (!IsX360())
{
g_pVGuiLocalize->AddFile( file, "MOD", true );
}
char uilanguage[64];
engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
char dbfile[512];
V_StrSubst( file, "%language%", uilanguage, dbfile, sizeof( dbfile ) );
V_SetExtension( dbfile, ".dat", sizeof( dbfile ) );
AddAdditionalCaptionDictionary( dbfile, outPathSymbols );
}
void CHudCloseCaption::RemoveCaptionDictionary( const CUtlSymbol &dbFileSymbol )
{
//
// 'file' should be something like "maps/mapbase_demo01_closecaption_%language%"
//
CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Removing custom caption file \"%s\"\n", dbFileSymbol.String() );
for (int i = 0; i < m_AsyncCaptions.Count(); ++i)
{
if ( m_AsyncCaptions[i].m_DataBaseFile == dbFileSymbol )
{
m_AsyncCaptions.Remove( i );
break;
}
}
}
#endif
void CHudCloseCaption::OnFinishAsyncLoad( int nFileIndex, int nBlockNum, AsyncCaptionData_t *pData )
{
// Fill in data for all users of pData->m_nBlockNum

View File

@ -111,6 +111,11 @@ public:
void PlayRandomCaption();
void InitCaptionDictionary( char const *dbfile );
#ifdef MAPBASE
void AddAdditionalCaptionDictionary( char const *dbfile, CUtlVector<CUtlSymbol> &outPathSymbols );
void AddCustomCaptionFile( char const *file, CUtlVector<CUtlSymbol> &outPathSymbols );
void RemoveCaptionDictionary( const CUtlSymbol &dbFileSymbol );
#endif
void OnFinishAsyncLoad( int nFileIndex, int nBlockNum, AsyncCaptionData_t *pData );
void Flush();
@ -133,6 +138,11 @@ public:
void FindSound( char const *pchANSI );
#ifdef MAPBASE
inline bool IsUsingCommentaryDimensions() const { return m_bUsingCommentaryDimensions; }
inline void SetUsingCommentaryDimensions( bool bToggle ) { m_bUsingCommentaryDimensions = bToggle; }
#endif
public:
struct CaptionRepeat
@ -212,6 +222,10 @@ private:
bool m_bVisibleDueToDirect;
bool m_bPaintDebugInfo;
CUtlSymbol m_CurrentLanguage;
#ifdef MAPBASE
bool m_bUsingCommentaryDimensions;
#endif
};
#endif // HUD_CLOSECAPTION_H

View File

@ -112,6 +112,11 @@ public:
virtual bool CanRecordDemo( char *errorMsg, int length ) const = 0;
#ifdef MAPBASE // From Alien Swarm SDK
virtual void OnColorCorrectionWeightsReset( void ) = 0;
virtual float GetColorCorrectionScale( void ) const = 0;
#endif
virtual void ComputeVguiResConditions( KeyValues *pkvConditions ) = 0;
//=============================================================================

View File

@ -115,6 +115,13 @@ public:
virtual void SetScreenOverlayMaterial( IMaterial *pMaterial ) = 0;
virtual IMaterial *GetScreenOverlayMaterial( ) = 0;
#ifdef MAPBASE
virtual void SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial ) = 0;
virtual IMaterial *GetIndexedScreenOverlayMaterial( int i ) = 0;
virtual void ResetIndexedScreenOverlays() = 0;
virtual int GetMaxIndexedScreenOverlays() const = 0;
#endif
virtual void WriteSaveGameScreenshot( const char *pFilename ) = 0;
virtual void WriteSaveGameScreenshotOfSize( const char *pFilename, int width, int height, bool bCreatePowerOf2Padded = false, bool bWriteVTF = false ) = 0;

View File

@ -1,6 +1,6 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Recreates Portal 2 linked_portal_door functionality using SDK code only.
// Purpose: Recreates Portal 2 linked_portal_door visual functionality using SDK code only.
// (basically a combination of point_camera and func_reflective_glass)
//
// $NoKeywords: $
@ -120,8 +120,8 @@ C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t
//-----------------------------------------------------------------------------
// Iterates through fake world portals instead of just picking one
//-----------------------------------------------------------------------------
C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane,
const Frustum_t &frustum )
C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view,
cplane_t &plane, Vector &vecPlaneOrigin, const Frustum_t &frustum )
{
// Early out if no cameras
C_FuncFakeWorldPortal *pReflectiveGlass = NULL;
@ -167,6 +167,7 @@ C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const
if ( !pReflectiveGlass->m_hTargetPlane )
continue;
vecPlaneOrigin = vecOrigin;
return pReflectiveGlass;
}
}

View File

@ -55,8 +55,8 @@ public:
//-----------------------------------------------------------------------------
C_FuncFakeWorldPortal *IsFakeWorldPortalInView( const CViewSetup& view, cplane_t &plane );
C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view, cplane_t &plane,
const Frustum_t &frustum );
C_FuncFakeWorldPortal *NextFakeWorldPortal( C_FuncFakeWorldPortal *pStart, const CViewSetup& view,
cplane_t &plane, Vector &vecPlaneOrigin, const Frustum_t &frustum );
#endif // C_FUNC_FAKE_WORLDPORTAL

View File

@ -157,11 +157,34 @@ void C_TextDisplayPanel::UpdateText()
m_pDisplayTextLabel->SetText( m_hScreenEntity->GetDisplayText() );
//SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() );
SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() );
m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() );
//m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() );
Label::Alignment iAlignment = m_hScreenEntity->GetContentAlignment();
switch (iAlignment)
{
// Use a special scaling method when using a south alignment
case Label::Alignment::a_southwest:
case Label::Alignment::a_south:
case Label::Alignment::a_southeast:
int lW, lT;
m_pDisplayTextLabel->GetContentSize( lW, lT );
SetSize( m_hScreenEntity->GetResolution(), lT );
m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), lT );
float sW, sT;
m_hVGUIScreen->GetSize( sW, sT );
//Msg( "Screen width: %f, new height: %f\n", sW, sW * (lT / m_hScreenEntity->GetResolution()) );
m_hVGUIScreen->SetHeight( sW * ((float)lT / (float)m_hScreenEntity->GetResolution()) );
m_hVGUIScreen->SetPixelHeight( lT );
break;
default:
SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() );
m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() );
break;
}
m_pDisplayTextLabel->SetContentAlignment( iAlignment );
bool bWrap = true;

View File

@ -487,6 +487,9 @@ int C_ServerRagdoll::InternalDrawModel( int flags )
return ret;
}
#ifdef MAPBASE
static ConVar g_ragdoll_server_snatch_instance( "g_ragdoll_server_snatch_instance", "1", FCVAR_NONE, "Allows serverside ragdolls to snatch their source entities' model instances in the same way clientside ragdolls do, thereby retaining decals." );
#endif
CStudioHdr *C_ServerRagdoll::OnNewModel( void )
{
@ -509,6 +512,26 @@ CStudioHdr *C_ServerRagdoll::OnNewModel( void )
m_iv_ragAngles.SetMaxCount( m_elementCount );
}
#ifdef MAPBASE
if ( GetOwnerEntity() )
{
if (GetOwnerEntity()->GetModelName() == GetModelName())
{
// TODO: Is there a better place for this?
if (GetOwnerEntity()->GetBaseAnimating())
GetOwnerEntity()->GetBaseAnimating()->m_pServerRagdoll = this;
if (g_ragdoll_server_snatch_instance.GetBool())
{
GetOwnerEntity()->SnatchModelInstance( this );
}
}
}
// Add server ragdolls to the creation tick list
NoteRagdollCreationTick( this );
#endif
return hdr;
}

View File

@ -107,7 +107,7 @@ extern ConVar cl_forwardspeed;
static ConVar v_centermove( "v_centermove", "0.15");
static ConVar v_centerspeed( "v_centerspeed","500" );
#ifdef TF_CLIENT_DLL
#if defined(TF_CLIENT_DLL) || defined(MAPBASE)
// 54 degrees approximates a 35mm camera - we determined that this makes the viewmodels
// and motions look the most natural.
ConVar v_viewmodel_fov( "viewmodel_fov", "54", FCVAR_ARCHIVE );

View File

@ -29,6 +29,9 @@ static ConVar mat_wateroverlaysize( "mat_wateroverlaysize", "256" );
static ConVar mat_showframebuffertexture( "mat_showframebuffertexture", "0", FCVAR_CHEAT );
static ConVar mat_framebuffercopyoverlaysize( "mat_framebuffercopyoverlaysize", "256" );
static ConVar mat_showcamerarendertarget( "mat_showcamerarendertarget", "0", FCVAR_CHEAT );
#ifdef MAPBASE
static ConVar mat_showcamerarendertarget_all( "mat_showcamerarendertarget_all", "0", FCVAR_CHEAT );
#endif
static ConVar mat_camerarendertargetoverlaysize( "mat_camerarendertargetoverlaysize", "256", FCVAR_CHEAT );
static ConVar mat_hsv( "mat_hsv", "0", FCVAR_CHEAT );
static ConVar mat_yuv( "mat_yuv", "0", FCVAR_CHEAT );
@ -178,6 +181,11 @@ void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float fl
pMaterial = materials->FindMaterial( pszMaterialName, TEXTURE_GROUP_OTHER, true );
if( !IsErrorMaterial( pMaterial ) )
{
#ifdef MAPBASE
// HACKHACK
pMaterial->IncrementReferenceCount();
#endif
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( pMaterial );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
@ -203,6 +211,11 @@ void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float fl
meshBuilder.End();
pMesh->Draw();
#ifdef MAPBASE
// HACKHACK
pMaterial->DecrementReferenceCount();
#endif
}
}
@ -214,7 +227,11 @@ static void OverlayFrameBufferTexture( int nFrameBufferIndex )
IMaterial *pMaterial;
char buf[MAX_PATH];
Q_snprintf( buf, MAX_PATH, "debug/debugfbtexture%d", nFrameBufferIndex );
#ifdef MAPBASE
pMaterial = materials->FindMaterial( buf, NULL, true );
#else
pMaterial = materials->FindMaterial( buf, TEXTURE_GROUP_OTHER, true );
#endif
if( !IsErrorMaterial( pMaterial ) )
{
CMatRenderContextPtr pRenderContext( materials );
@ -586,12 +603,52 @@ void CDebugViewRender::Draw2DDebuggingInfo( const CViewSetup &view )
if ( mat_showcamerarendertarget.GetBool() )
{
#ifdef MAPBASE
float w = mat_camerarendertargetoverlaysize.GetFloat();
float h = mat_camerarendertargetoverlaysize.GetFloat();
#else
float w = mat_wateroverlaysize.GetFloat();
float h = mat_wateroverlaysize.GetFloat();
#endif
#ifdef PORTAL
g_pPortalRender->OverlayPortalRenderTargets( w, h );
#else
#ifdef MAPBASE
int iCameraNum = mat_showcamerarendertarget.GetInt();
if (iCameraNum == 1) // Display the default camera
{
OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h );
}
else if (mat_showcamerarendertarget_all.GetBool()) // Display all cameras
{
OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h );
// Already showed one camera
iCameraNum--;
// Show Mapbase's cameras
char szTextureName[48];
for (int i = 0; i < iCameraNum; i++)
{
V_snprintf( szTextureName, sizeof( szTextureName ), "debug/debugcamerarendertarget_camera%i", i );
// Show them vertically if the cvar is set to 2
if (mat_showcamerarendertarget_all.GetInt() == 2)
OverlayCameraRenderTarget( szTextureName, 0, h * (i + 1), w, h );
else
OverlayCameraRenderTarget( szTextureName, w * (i + 1), 0, w, h );
}
}
else // Display one of the new cameras
{
OverlayCameraRenderTarget( VarArgs( "debug/debugcamerarendertarget_camera%i", iCameraNum-2 ), 0, 0, w, h );
}
#else
OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h );
#endif
#endif
}
@ -655,6 +712,57 @@ CON_COMMAND_F( r_screenoverlay, "Draw specified material as an overlay", FCVAR_C
}
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// The same as above, but using the new indexed overlays
//-----------------------------------------------------------------------------
CON_COMMAND_F( r_screenoverlay_indexed, "Draw specified material as an overlay in the specified index", FCVAR_CHEAT|FCVAR_SERVER_CAN_EXECUTE )
{
if( args.ArgC() == 3 )
{
int index = atoi( args[1] );
if (index < 0 || index >= MAX_SCREEN_OVERLAYS)
{
Warning( "r_screenoverlay_indexed: '%i' is out of range (should be 0-9)\n", index );
return;
}
if ( !Q_stricmp( "off", args[2] ) )
{
view->SetIndexedScreenOverlayMaterial( index, NULL );
}
else
{
IMaterial *pMaterial = materials->FindMaterial( args[2], TEXTURE_GROUP_OTHER, false );
if ( !IsErrorMaterial( pMaterial ) )
{
view->SetIndexedScreenOverlayMaterial( index, pMaterial );
}
else
{
view->SetIndexedScreenOverlayMaterial( index, NULL );
}
}
}
else if ( args.ArgC() == 2 )
{
int index = atoi( args[1] );
if (index < 0 || index >= MAX_SCREEN_OVERLAYS)
{
Warning( "r_screenoverlay_indexed: '%i' is out of range (should be 0-9)\n", index );
return;
}
IMaterial *pMaterial = view->GetIndexedScreenOverlayMaterial( index );
Warning( "r_screenoverlay_indexed %i: %s\n", index, pMaterial ? pMaterial->GetName() : "off" );
}
else
{
Warning( "Format: r_screenoverlay_indexed <index> <material> %s\n" );
}
}
#endif
// Used to verify frame syncing.
void CDebugViewRender::GenerateOverdrawForTesting()
{

View File

@ -76,6 +76,7 @@
#ifdef MAPBASE
#include "mapbase/c_func_fake_worldportal.h"
#include "colorcorrectionmgr.h"
#endif
// Projective textures
@ -1218,6 +1219,73 @@ IMaterial *CViewRender::GetScreenOverlayMaterial( )
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Sets the screen space effect material (can't be done during rendering)
//-----------------------------------------------------------------------------
void CViewRender::SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial )
{
if (i < 0 || i >= MAX_SCREEN_OVERLAYS)
return;
m_IndexedScreenOverlayMaterials[i].Init( pMaterial );
if (pMaterial == NULL)
{
// Check if we should set to false
int i;
for (i = 0; i < MAX_SCREEN_OVERLAYS; i++)
{
if (m_IndexedScreenOverlayMaterials[i] != NULL)
break;
}
if (i == MAX_SCREEN_OVERLAYS)
m_bUsingIndexedScreenOverlays = false;
}
else
{
m_bUsingIndexedScreenOverlays = true;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
IMaterial *CViewRender::GetIndexedScreenOverlayMaterial( int i )
{
if (i < 0 || i >= MAX_SCREEN_OVERLAYS)
return NULL;
return m_IndexedScreenOverlayMaterials[i];
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CViewRender::ResetIndexedScreenOverlays()
{
for (int i = 0; i < MAX_SCREEN_OVERLAYS; i++)
{
m_IndexedScreenOverlayMaterials[i].Init( NULL );
}
m_bUsingIndexedScreenOverlays = false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CViewRender::GetMaxIndexedScreenOverlays( ) const
{
return MAX_SCREEN_OVERLAYS;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Performs screen space effects, if any
//-----------------------------------------------------------------------------
@ -1254,6 +1322,44 @@ void CViewRender::PerformScreenOverlay( int x, int y, int w, int h )
render->ViewDrawFade( color, m_ScreenOverlayMaterial );
}
}
#ifdef MAPBASE
if (m_bUsingIndexedScreenOverlays)
{
for (int i = 0; i < MAX_SCREEN_OVERLAYS; i++)
{
if (!m_IndexedScreenOverlayMaterials[i])
continue;
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
if ( m_IndexedScreenOverlayMaterials[i]->NeedsFullFrameBufferTexture() )
{
// FIXME: check with multi/sub-rect renders. Should this be 0,0,w,h instead?
DrawScreenEffectMaterial( m_IndexedScreenOverlayMaterials[i], x, y, w, h );
}
else if ( m_IndexedScreenOverlayMaterials[i]->NeedsPowerOfTwoFrameBufferTexture() )
{
// First copy the FB off to the offscreen texture
UpdateRefractTexture( x, y, w, h, true );
// Now draw the entire screen using the material...
CMatRenderContextPtr pRenderContext( materials );
ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( );
int sw = pTexture->GetActualWidth();
int sh = pTexture->GetActualHeight();
// Note - don't offset by x,y - already done by the viewport.
pRenderContext->DrawScreenSpaceRectangle( m_IndexedScreenOverlayMaterials[i], 0, 0, w, h,
0, 0, sw-1, sh-1, sw, sh );
}
else
{
byte color[4] = { 255, 255, 255, 255 };
render->ViewDrawFade( color, m_IndexedScreenOverlayMaterials[i] );
}
}
}
#endif
}
void CViewRender::DrawUnderwaterOverlay( void )
@ -2005,18 +2111,19 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT
GeneratePerspectiveFrustum( view.origin, view.angles, view.zNear, view.zFar, view.fov, view.m_flAspectRatio, frustum );
cplane_t portalPlane;
Vector vecPlaneOrigin;
//C_FuncFakeWorldPortal *pPortalEnt = IsFakeWorldPortalInView( view, portalPlane );
//if ( pPortalEnt )
C_FuncFakeWorldPortal *pPortalEnt = NextFakeWorldPortal( NULL, view, portalPlane, frustum );
C_FuncFakeWorldPortal *pPortalEnt = NextFakeWorldPortal( NULL, view, portalPlane, vecPlaneOrigin, frustum );
while ( pPortalEnt != NULL )
{
ITexture *pCameraTarget = pPortalEnt->RenderTarget();
int width = pCameraTarget->GetActualWidth();
int height = pCameraTarget->GetActualHeight();
DrawFakeWorldPortal( pCameraTarget, pPortalEnt, viewMiddle, C_BasePlayer::GetLocalPlayer(), 0, 0, width, height, view, portalPlane );
DrawFakeWorldPortal( pCameraTarget, pPortalEnt, viewMiddle, C_BasePlayer::GetLocalPlayer(), 0, 0, width, height, view, portalPlane, vecPlaneOrigin );
pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, portalPlane, frustum );
pPortalEnt = NextFakeWorldPortal( pPortalEnt, view, portalPlane, vecPlaneOrigin, frustum );
}
#endif
}
@ -2027,6 +2134,10 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT
// Must be first
render->SceneBegin();
#ifdef MAPBASE // From Alien Swarm SDK
g_pColorCorrectionMgr->UpdateColorCorrection();
#endif
pRenderContext.GetFrom( materials );
pRenderContext->TurnOnToneMapping();
pRenderContext.SafeRelease();
@ -3434,8 +3545,7 @@ ConVar r_fakeworldportal_debug("r_fakeworldportal_debug", "0");
//-----------------------------------------------------------------------------
// Purpose: Sets up scene and renders WIP fake world portal view.
// Based on code from monitors, mirrors, and 3D skyboxes.
// It's also terrible right now.
// Based on code from monitors, mirrors, and logic_measure_movement.
//
// Input : cameraNum -
// &cameraView
@ -3449,7 +3559,7 @@ ConVar r_fakeworldportal_debug("r_fakeworldportal_debug", "0");
//-----------------------------------------------------------------------------
bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer,
int x, int y, int width, int height,
const CViewSetup &mainView, cplane_t &ourPlane )
const CViewSetup &mainView, cplane_t &ourPlane, const Vector &vecPlaneOrigin )
{
#ifdef USE_MONITORS
VPROF_INCREMENT_COUNTER( "cameras rendered", 1 );
@ -3488,97 +3598,89 @@ bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldP
monitorView.origin = mainView.origin;
monitorView.angles = mainView.angles;
// Temporary debug stuff
// Debug stuff
static float flLastDebugTime = 0.0f;
bool bDebug = r_fakeworldportal_debug.GetBool() && gpGlobals->curtime > flLastDebugTime;
QAngle angTargetAngles = pCameraEnt->m_hTargetPlane->GetAbsAngles() + pCameraEnt->m_PlaneAngles;
// RED - First origin
if (bDebug)
debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 0, 0, 128, 10.0f );
// Make sure the origin and angles are relative to the target plane
monitorView.origin -= pCameraEnt->GetAbsOrigin();
// scale origin by sky scale
if ( pCameraEnt->m_flScale > 0 )
{
float scale = 1.0f / pCameraEnt->m_flScale;
VectorScale( monitorView.origin, scale, monitorView.origin );
}
// YELLOW - Main origin
if (bDebug)
debugoverlay->AddBoxOverlay( pCameraEnt->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 224, 0, 128, 10.0f );
// Make sure our angles are relative to the main plane, just like the origin
QAngle angOurAngles;
VectorAngles( ourPlane.normal * -1, angOurAngles );
//angles -= angOurAngles;
// First, create a matrix for the sky's angles.
matrix3x4_t matSkyAngles;
AngleMatrix( angTargetAngles - angOurAngles, matSkyAngles );
Vector vecSkyForward, vecSkyRight, vecSkyUp;
//
// Calculate the angles for the fake portal plane
//
QAngle angTargetAngles = pCameraEnt->m_hTargetPlane->GetAbsAngles() - pCameraEnt->m_PlaneAngles;
QAngle angFakePortalAngles;
// Get vectors from our original angles.
Vector vPlayerForward, vPlayerRight, vPlayerUp;
AngleVectors( monitorView.angles, &vPlayerForward, &vPlayerRight, &vPlayerUp );
VectorTransform( vPlayerForward, matSkyAngles, vecSkyForward );
VectorTransform( vPlayerRight, matSkyAngles, vecSkyRight );
VectorTransform( vPlayerUp, matSkyAngles, vecSkyUp );
// Normalize them.
VectorNormalize( vecSkyForward );
VectorNormalize( vecSkyRight );
VectorNormalize( vecSkyUp );
Vector vOurForward, vOurRight, vOurUp;
AngleVectors( pCameraEnt->GetAbsAngles(), &vOurForward, &vOurRight, &vOurUp );
Quaternion quat;
BasisToQuaternion( vecSkyForward, vecSkyRight, vecSkyUp, quat );
QuaternionAngles( quat, monitorView.angles );
BasisToQuaternion( ourPlane.normal, vOurRight, vOurUp, quat );
QuaternionAngles( quat, angFakePortalAngles );
// End of code mostly lifted from projected texture screenspace stuff
// ----------------------------------------------------------------------
// Now just rotate our origin with that matrix.
// We create a copy of the origin since VectorRotate doesn't want in1 to be the same variable as the destination.
VectorRotate(Vector(monitorView.origin), matSkyAngles, monitorView.origin);
// BLUE - Target origin
if (bDebug)
debugoverlay->AddBoxOverlay( pCameraEnt->m_hTargetPlane->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 0, 255, 128, 10.0f );
monitorView.origin += pCameraEnt->m_hTargetPlane->GetAbsOrigin();
// GREEN - Final origin
if (bDebug)
{
// RED - Initial player origin
debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 255, 0, 0, 128, 10.0f );
// YELLOW - Portal origin
debugoverlay->AddBoxOverlay( pCameraEnt->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), angFakePortalAngles, 255, 224, 0, 128, 10.0f );
}
//
// Translate the actual portal view position to be relative to the target
//
matrix3x4_t matPlayer, matPortal, matPlayerToPortal;
AngleIMatrix( monitorView.angles, monitorView.origin, matPlayer );
AngleMatrix( angFakePortalAngles, pCameraEnt->GetAbsOrigin(), matPortal );
ConcatTransforms( matPlayer, matPortal, matPlayerToPortal );
// Apply the scale factor
if ( pCameraEnt->m_flScale > 0 )
{
Vector vecTranslation;
MatrixGetColumn( matPlayerToPortal, 3, vecTranslation );
vecTranslation /= pCameraEnt->m_flScale;
MatrixSetColumn( vecTranslation, 3, matPlayerToPortal );
}
matrix3x4_t matTarget;
AngleMatrix( angTargetAngles, pCameraEnt->m_hTargetPlane->GetAbsOrigin(), matTarget );
// Now apply the new matrix to the new reference point
matrix3x4_t matPortalToPlayer, matNewPlayerPosition;
MatrixInvert( matPlayerToPortal, matPortalToPlayer );
ConcatTransforms( matTarget, matPortalToPlayer, matNewPlayerPosition );
MatrixAngles( matNewPlayerPosition, monitorView.angles, monitorView.origin );
if (bDebug)
{
// BLUE - Target origin
debugoverlay->AddBoxOverlay( pCameraEnt->m_hTargetPlane->GetAbsOrigin(), Vector(-32,-32,-32), Vector(32,32,32), angTargetAngles, 0, 0, 255, 128, 10.0f );
// GREEN - Final origin
debugoverlay->AddBoxOverlay( monitorView.origin, Vector(-32,-32,-32), Vector(32,32,32), monitorView.angles, 0, 255, 0, 128, 10.0f );
flLastDebugTime = gpGlobals->curtime + 5.0f;
}
monitorView.fov = mainView.fov;
monitorView.m_bOrtho = false;
monitorView.m_flAspectRatio = 0.0f;
monitorView.m_bOrtho = mainView.m_bOrtho;
monitorView.m_flAspectRatio = mainView.m_flAspectRatio;
monitorView.m_bViewToProjectionOverride = false;
// @MULTICORE (toml 8/11/2006): this should be a renderer....
int nClearFlags = (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL);
bool bDrew3dSkybox = false;
SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode();
Frustum frustum;
render->Push3DView( monitorView, nClearFlags, pRenderTarget, (VPlane *)frustum );
//
// Monitor sky handling
// Sky handling
//
if ( pCameraEnt->SkyMode() == SKYBOX_3DSKYBOX_VISIBLE )
SkyboxVisibility_t nSkyMode = pCameraEnt->SkyMode();
if ( nSkyMode == SKYBOX_3DSKYBOX_VISIBLE )
{
// if the 3d skybox world is drawn, then don't draw the normal skybox
CSkyboxView *pSkyView = new CSkyboxView( this );
@ -3589,16 +3691,21 @@ bool CViewRender::DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldP
SafeRelease( pSkyView );
}
//
// Make a clipping plane for the target view
//
Vector4D plane;
// Combine the target angles and the plane angles
Vector vecAnglesNormal( angTargetAngles.x, angTargetAngles.y, angTargetAngles.z );
Vector vecAnglesNormal;
AngleVectors( angTargetAngles, &vecAnglesNormal );
VectorNormalize( vecAnglesNormal );
VectorCopy( vecAnglesNormal, plane.AsVector3D() );
VectorCopy( -vecAnglesNormal, plane.AsVector3D() );
// TODO: How do we get a good value for this!?!?
//plane.w = m_OurPlane.dist + 0.1f;
plane.w = -32.0f + 0.1f;
// The portal plane's distance from the actual brush's origin
float flPlaneDist = vecPlaneOrigin.Length();
// The target's distance from world origin
plane.w = -((pCameraEnt->m_hTargetPlane->GetAbsOrigin() * vecAnglesNormal).Length() + flPlaneDist) + 0.1f;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushCustomClipPlane( plane.Base() );

View File

@ -454,7 +454,7 @@ private:
#ifdef MAPBASE
bool DrawFakeWorldPortal( ITexture *pRenderTarget, C_FuncFakeWorldPortal *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer,
int x, int y, int width, int height,
const CViewSetup &mainView, cplane_t &ourPlane );
const CViewSetup &mainView, cplane_t &ourPlane, const Vector &vecPlaneOrigin );
#endif
// Drawing primitives
@ -468,6 +468,13 @@ private:
IMaterial *GetScreenOverlayMaterial( );
void PerformScreenOverlay( int x, int y, int w, int h );
#ifdef MAPBASE
void SetIndexedScreenOverlayMaterial( int i, IMaterial *pMaterial );
IMaterial *GetIndexedScreenOverlayMaterial( int i );
void ResetIndexedScreenOverlays();
int GetMaxIndexedScreenOverlays() const;
#endif
void DrawUnderwaterOverlay( void );
// Water-related methods
@ -511,6 +518,10 @@ private:
CMaterialReference m_TranslucentSingleColor;
CMaterialReference m_ModulateSingleColor;
CMaterialReference m_ScreenOverlayMaterial;
#ifdef MAPBASE
CMaterialReference m_IndexedScreenOverlayMaterials[MAX_SCREEN_OVERLAYS];
bool m_bUsingIndexedScreenOverlays;
#endif
CMaterialReference m_UnderWaterOverlayMaterial;
Vector m_vecLastFacing;

View File

@ -41,10 +41,13 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#endif // VMPROFILE
#ifdef MAPBASE_VSCRIPT
static ScriptHook_t g_Hook_OnEntityCreated;
static ScriptHook_t g_Hook_OnEntityDeleted;
//-----------------------------------------------------------------------------
// Purpose: A clientside variant of CScriptEntityIterator.
//-----------------------------------------------------------------------------
class CScriptClientEntityIterator
class CScriptClientEntityIterator : public IClientEntityListener
{
public:
HSCRIPT GetLocalPlayer()
@ -96,6 +99,38 @@ public:
return NULL;
}
void EnableEntityListening()
{
// Start getting entity updates!
ClientEntityList().AddListenerEntity( this );
}
void DisableEntityListening()
{
// Stop getting entity updates!
ClientEntityList().RemoveListenerEntity( this );
}
void OnEntityCreated( CBaseEntity *pEntity )
{
if ( g_pScriptVM )
{
// entity
ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) };
g_Hook_OnEntityCreated.Call( NULL, NULL, args );
}
};
void OnEntityDeleted( CBaseEntity *pEntity )
{
if ( g_pScriptVM )
{
// entity
ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) };
g_Hook_OnEntityDeleted.Call( NULL, NULL, args );
}
};
private:
} g_ScriptEntityIterator;
@ -106,6 +141,17 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptClientEntityIterator, "CEntities", SCRIPT_SI
DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" )
DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" )
DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" )
DEFINE_SCRIPTFUNC( EnableEntityListening, "Enables the 'OnEntity' hooks. This function must be called before using them." )
DEFINE_SCRIPTFUNC( DisableEntityListening, "Disables the 'OnEntity' hooks." )
BEGIN_SCRIPTHOOK( g_Hook_OnEntityCreated, "OnEntityCreated", FIELD_VOID, "Called when an entity is created. Requires EnableEntityListening() to be fired beforehand." )
DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( g_Hook_OnEntityDeleted, "OnEntityDeleted", FIELD_VOID, "Called when an entity is deleted. Requires EnableEntityListening() to be fired beforehand." )
DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT )
END_SCRIPTHOOK()
END_SCRIPTDESC();
//-----------------------------------------------------------------------------

View File

@ -57,6 +57,45 @@ BEGIN_DATADESC( CBaseAnimatingOverlay )
END_DATADESC()
#ifdef MAPBASE_VSCRIPT
BEGIN_ENT_SCRIPTDESC( CBaseAnimatingOverlay, CBaseAnimating, "Animating models which support dynamic animation layers/overlays." )
DEFINE_SCRIPTFUNC( GetNumAnimOverlays, "Gets the current number of animation layers." )
DEFINE_SCRIPTFUNC( RemoveAllGestures, "Removes all animation layers." )
DEFINE_SCRIPTFUNC( IsValidLayer, "Returns true if the specified layer index is valid." )
DEFINE_SCRIPTFUNC( HasActiveLayer, "Returns true if there is currently an active layer." )
DEFINE_SCRIPTFUNC( RemoveLayer, "Removes the specified layer index with the specified kill rate and delay." )
DEFINE_SCRIPTFUNC( FastRemoveLayer, "Removes the specified layer index immediately." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddGesture, "AddGesture", "Adds a new animation layer using the specified activity name." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureID, "AddGestureID", "Adds a new animation layer using the specified activity index." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureSequence, "AddGestureSequence", "Adds a new animation layer using the specified activity name." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddGestureSequenceID, "AddGestureSequenceID", "Adds a new animation layer using the specified sequence index." )
DEFINE_SCRIPTFUNC_NAMED( ScriptFindGestureLayer, "FindGestureLayer", "Finds and returns the first active animation layer which uses the specified activity name." )
DEFINE_SCRIPTFUNC_NAMED( ScriptFindGestureLayerByID, "FindGestureLayerByID", "Finds and returns the first active animation layer which uses the specified activity index." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetLayerActivity, "GetLayerActivity", "Gets the activity name of the specified layer index." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetLayerActivityID, "GetLayerActivityID", "Gets the activity index of the specified layer index." )
DEFINE_SCRIPTFUNC( GetLayerSequence, "Gets the sequence index of the specified layer index." )
DEFINE_SCRIPTFUNC( SetLayerDuration, "Sets the duration of the specified layer index." )
DEFINE_SCRIPTFUNC( GetLayerDuration, "Gets the duration of the specified layer index." )
DEFINE_SCRIPTFUNC_NAMED( ScriptSetLayerCycle, "SetLayerCycle", "Sets the cycle of the specified layer index." )
DEFINE_SCRIPTFUNC( GetLayerCycle, "Gets the cycle of the specified layer index." )
DEFINE_SCRIPTFUNC( SetLayerPlaybackRate, "Sets the playback rate of the specified layer index." )
DEFINE_SCRIPTFUNC( SetLayerWeight, "Sets the weight of the specified layer index." )
DEFINE_SCRIPTFUNC( GetLayerWeight, "Gets the weight of the specified layer index." )
DEFINE_SCRIPTFUNC( SetLayerBlendIn, "Sets the fade-in of the specified layer index, with the fade being a 0-1 fraction of the cycle." )
DEFINE_SCRIPTFUNC( SetLayerBlendOut, "Sets the fade-out of the specified layer index, with the fade being a 0-1 fraction of the cycle." )
DEFINE_SCRIPTFUNC( SetLayerAutokill, "Sets whether or not the specified layer index should remove itself when it's finished playing." )
DEFINE_SCRIPTFUNC( SetLayerLooping, "Sets whether or not the specified layer index should loop." )
DEFINE_SCRIPTFUNC( SetLayerNoRestore, "Sets whether or not the specified layer index should restore after a save is loaded." )
DEFINE_SCRIPTFUNC( SetLayerNoEvents, "Sets whether or not the specified layer index should fire animation events." )
END_SCRIPTDESC();
#endif
#define ORDER_BITS 4
#define WEIGHT_BITS 8
@ -354,7 +393,11 @@ void CBaseAnimatingOverlay::DispatchAnimEvents ( CBaseAnimating *eventHandler )
for ( int i = 0; i < m_AnimOverlay.Count(); i++ )
{
#ifdef MAPBASE // From Alien Swarm SDK
if (m_AnimOverlay[ i ].IsActive() && !m_AnimOverlay[ i ].NoEvents())
#else
if (m_AnimOverlay[ i ].IsActive())
#endif
{
m_AnimOverlay[ i ].DispatchAnimEvents( eventHandler, this );
}
@ -1052,6 +1095,38 @@ void CBaseAnimatingOverlay::SetLayerNoRestore( int iLayer, bool bNoRestore )
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// From Alien Swarm SDK
//-----------------------------------------------------------------------------
void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents )
{
if (!IsValidLayer( iLayer ))
return;
if (bNoEvents)
{
m_AnimOverlay[iLayer].m_fFlags |= ANIM_LAYER_NOEVENTS;
}
else
{
m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_NOEVENTS;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseAnimatingOverlay::IsLayerFinished( int iLayer )
{
if (!IsValidLayer( iLayer ))
return true;
return m_AnimOverlay[iLayer].m_bSequenceFinished;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -1149,4 +1224,36 @@ bool CBaseAnimatingOverlay::HasActiveLayer( void )
return false;
}
#ifdef MAPBASE_VSCRIPT
int CBaseAnimatingOverlay::ScriptAddGesture( const char *pszActivity, bool autokill )
{
return AddGesture( (Activity)CAI_BaseNPC::GetActivityID( pszActivity ), autokill );
}
int CBaseAnimatingOverlay::ScriptAddGestureID( int iActivity, bool autokill )
{
return AddGesture( (Activity)iActivity, autokill );
}
int CBaseAnimatingOverlay::ScriptFindGestureLayer( const char *pszActivity )
{
return FindGestureLayer( (Activity)CAI_BaseNPC::GetActivityID( pszActivity ) );
}
int CBaseAnimatingOverlay::ScriptFindGestureLayerByID( int iActivity )
{
return FindGestureLayer( (Activity)iActivity );
}
const char *CBaseAnimatingOverlay::ScriptGetLayerActivity( int iLayer )
{
return CAI_BaseNPC::GetActivityName( GetLayerActivity( iLayer ) );
}
int CBaseAnimatingOverlay::ScriptGetLayerActivityID( int iLayer )
{
return GetLayerActivity( iLayer );
}
#endif
//-----------------------------------------------------------------------------

View File

@ -43,6 +43,9 @@ public:
#define ANIM_LAYER_DONTRESTORE 0x0008
#define ANIM_LAYER_CHECKACCESS 0x0010
#define ANIM_LAYER_DYING 0x0020
#ifdef MAPBASE // From Alien Swarm SDK
#define ANIM_LAYER_NOEVENTS 0x0040
#endif
int m_fFlags;
@ -80,6 +83,9 @@ public:
void Dying( void ) { m_fFlags |= ANIM_LAYER_DYING; }
bool IsDying( void ) { return ((m_fFlags & ANIM_LAYER_DYING) != 0); }
void Dead( void ) { m_fFlags &= ~ANIM_LAYER_DYING; }
#ifdef MAPBASE // From Alien Swarm SDK
bool NoEvents( void ) { return ((m_fFlags & ANIM_LAYER_NOEVENTS) != 0); }
#endif
bool IsAbandoned( void );
void MarkActive( void );
@ -175,6 +181,11 @@ public:
void SetLayerAutokill( int iLayer, bool bAutokill );
void SetLayerLooping( int iLayer, bool bLooping );
void SetLayerNoRestore( int iLayer, bool bNoRestore );
#ifdef MAPBASE
void SetLayerNoEvents( int iLayer, bool bNoEvents ); // From Alien Swarm SDK
bool IsLayerFinished( int iLayer );
#endif
Activity GetLayerActivity( int iLayer );
int GetLayerSequence( int iLayer );
@ -195,9 +206,26 @@ public:
private:
int AllocateLayer( int iPriority = 0 ); // lower priorities are processed first
#ifdef MAPBASE_VSCRIPT
int ScriptAddGesture( const char *pszActivity, bool autokill );
int ScriptAddGestureID( int iActivity, bool autokill );
int ScriptAddGestureSequence( const char *pszSequence, bool autokill ) { return AddGestureSequence( LookupSequence( pszSequence ), autokill ); }
int ScriptAddGestureSequenceID( int iSequence, bool autokill ) { return AddGestureSequence( iSequence, autokill ); }
int ScriptFindGestureLayer( const char *pszActivity );
int ScriptFindGestureLayerByID( int iActivity );
const char *ScriptGetLayerActivity( int iLayer );
int ScriptGetLayerActivityID( int iLayer );
void ScriptSetLayerCycle( int iLayer, float flCycle ) { SetLayerCycle( iLayer, flCycle ); }
#endif
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
DECLARE_PREDICTABLE();
#ifdef MAPBASE_VSCRIPT
DECLARE_ENT_SCRIPTDESC();
#endif
};
EXTERN_SEND_TABLE(DT_BaseAnimatingOverlay);

View File

@ -103,6 +103,12 @@ protected:
inline CBaseEntity *GetActivator();
#ifdef MAPBASE
inline float GetNPCOpenDistance() { return m_flNPCOpenDistance; }
inline Activity GetNPCOpenFrontActivity() { return m_eNPCOpenFrontActivity; }
inline Activity GetNPCOpenBackActivity() { return m_eNPCOpenBackActivity; }
#endif
private:
// Implement these in your leaf class.
@ -196,6 +202,12 @@ private:
string_t m_SoundOpen;
string_t m_SoundClose;
#ifdef MAPBASE
float m_flNPCOpenDistance;
Activity m_eNPCOpenFrontActivity;
Activity m_eNPCOpenBackActivity;
#endif
// dvs: FIXME: can we remove m_flSpeed from CBaseEntity?
//float m_flSpeed; // Rotation speed when opening or closing in degrees per second.

View File

@ -72,8 +72,23 @@ class CPointCommentaryNode : public CBaseAnimating
DECLARE_CLASS( CPointCommentaryNode, CBaseAnimating );
public:
DECLARE_DATADESC();
#ifdef MAPBASE_VSCRIPT
DECLARE_ENT_SCRIPTDESC();
#endif
DECLARE_SERVERCLASS();
CPointCommentaryNode()
{
#ifdef MAPBASE
m_flViewTargetSpeedScale = 1.0f;
m_flViewPositionSpeedScale = 1.0f;
m_flReturnSpeedScale = 0.0f;
m_flPanelScale = 1.0f;
m_flPanelX = -1.0f;
m_flPanelY = -1.0f;
#endif
}
void Spawn( void );
void Precache( void );
void Activate( void );
@ -102,11 +117,37 @@ public:
void TeleportTo( CBasePlayer *pPlayer );
bool CanTeleportTo( void );
#ifdef MAPBASE
bool IsActive() { return m_bActive; }
bool IsDisabled() { return m_bDisabled; }
int GetCommentaryType() { return m_iCommentaryType; }
void SetCommentaryType( int iType ) { m_iCommentaryType = iType; }
const char *GetCommentaryFile() { return STRING( m_iszCommentaryFile.Get() ); }
void SetCommentaryFile( const char *pszNewFile ) { m_iszCommentaryFile.Set( AllocPooledString( pszNewFile ) ); }
const char *GetSpeakers() { return STRING( m_iszSpeakers.Get() ); }
void SetSpeakers( const char *pszSpeakers ) { m_iszSpeakers.Set( AllocPooledString( pszSpeakers ) ); }
const char *GetPrintName() { return STRING( m_iszPrintName.Get() ); }
void SetPrintName( const char *pszPrintName ) { m_iszPrintName.Set( AllocPooledString( pszPrintName ) ); }
#endif
// Inputs
void InputStartCommentary( inputdata_t &inputdata );
void InputStartUnstoppableCommentary( inputdata_t &inputdata );
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
#ifdef MAPBASE
void InputSetViewTarget( inputdata_t &inputdata );
void InputSetViewPosition( inputdata_t &inputdata );
void InputSetViewTargetSpeed( inputdata_t &inputdata );
void InputSetViewPositionSpeed( inputdata_t &inputdata );
void InputSetReturnSpeed( inputdata_t &inputdata );
#endif
#ifdef MAPBASE_VSCRIPT
static ScriptHook_t g_Hook_PreStartCommentary;
#endif
private:
string_t m_iszPreCommands;
@ -119,6 +160,13 @@ private:
string_t m_iszViewPosition;
CNetworkVar( EHANDLE, m_hViewPosition );
EHANDLE m_hViewPositionMover; // Entity used to blend the view to the viewposition entity
#ifdef MAPBASE
float m_flViewTargetSpeedScale;
float m_flViewPositionSpeedScale;
float m_flReturnSpeedScale;
CNetworkVar( string_t, m_iszPrintName );
float m_flViewPositionChangedTime; // View position now blends relative to this value. Mainly needed for when SetViewPosition is used
#endif
bool m_bPreventMovement;
bool m_bUnderCrosshair;
bool m_bUnstoppable;
@ -138,6 +186,13 @@ private:
CNetworkVar( string_t, m_iszSpeakers );
CNetworkVar( int, m_iNodeNumber );
CNetworkVar( int, m_iNodeNumberMax );
#ifdef MAPBASE
CNetworkVar( int, m_iCommentaryType );
CNetworkVar( float, m_flPanelScale );
CNetworkVar( float, m_flPanelX );
CNetworkVar( float, m_flPanelY );
#endif
};
BEGIN_DATADESC( CPointCommentaryNode )
@ -166,6 +221,17 @@ BEGIN_DATADESC( CPointCommentaryNode )
DEFINE_FIELD( m_bPreventChangesWhileMoving, FIELD_BOOLEAN ),
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "start_disabled" ),
DEFINE_KEYFIELD( m_vecTeleportOrigin, FIELD_VECTOR, "teleport_origin" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_flViewTargetSpeedScale, FIELD_FLOAT, "viewtarget_speed" ),
DEFINE_KEYFIELD( m_flViewPositionSpeedScale, FIELD_FLOAT, "viewposition_speed" ),
DEFINE_KEYFIELD( m_flReturnSpeedScale, FIELD_FLOAT, "return_speed" ),
DEFINE_KEYFIELD( m_iszPrintName, FIELD_STRING, "printname" ),
DEFINE_FIELD( m_flViewPositionChangedTime, FIELD_TIME ),
DEFINE_KEYFIELD( m_iCommentaryType, FIELD_INTEGER, "type" ),
DEFINE_KEYFIELD( m_flPanelScale, FIELD_FLOAT, "panelscale" ),
DEFINE_KEYFIELD( m_flPanelX, FIELD_FLOAT, "x" ),
DEFINE_KEYFIELD( m_flPanelY, FIELD_FLOAT, "y" ),
#endif
// Outputs
DEFINE_OUTPUT( m_pOnCommentaryStarted, "OnCommentaryStarted" ),
@ -176,6 +242,13 @@ BEGIN_DATADESC( CPointCommentaryNode )
DEFINE_INPUTFUNC( FIELD_VOID, "StartUnstoppableCommentary", InputStartUnstoppableCommentary ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetViewTarget", InputSetViewTarget ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetViewPosition", InputSetViewPosition ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetViewTargetSpeed", InputSetViewTargetSpeed ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetViewPositionSpeed", InputSetViewPositionSpeed ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetReturnSpeed", InputSetReturnSpeed ),
#endif
// Functions
DEFINE_THINKFUNC( SpinThink ),
@ -183,6 +256,35 @@ BEGIN_DATADESC( CPointCommentaryNode )
DEFINE_THINKFUNC( UpdateViewPostThink ),
END_DATADESC()
#ifdef MAPBASE_VSCRIPT
ScriptHook_t CPointCommentaryNode::g_Hook_PreStartCommentary;
BEGIN_ENT_SCRIPTDESC( CPointCommentaryNode, CBaseAnimating, "Commentary nodes which play commentary in commentary mode." )
DEFINE_SCRIPTFUNC( IsDisabled, "" )
DEFINE_SCRIPTFUNC( SetDisabled, "" )
DEFINE_SCRIPTFUNC( IsActive, "" )
DEFINE_SCRIPTFUNC( GetCommentaryFile, "" )
DEFINE_SCRIPTFUNC( SetCommentaryFile, "" )
DEFINE_SCRIPTFUNC( GetSpeakers, "" )
DEFINE_SCRIPTFUNC( SetSpeakers, "" )
DEFINE_SCRIPTFUNC( GetPrintName, "" )
DEFINE_SCRIPTFUNC( SetPrintName, "" )
DEFINE_SCRIPTFUNC( GetCommentaryType, "" )
DEFINE_SCRIPTFUNC( SetCommentaryType, "" )
DEFINE_SCRIPTFUNC( HasViewTarget, "" )
DEFINE_SCRIPTFUNC( PreventsMovement, "" )
DEFINE_SCRIPTFUNC( CannotBeStopped, "" )
DEFINE_SCRIPTFUNC( AbortPlaying, "Stops playing the node and snaps out of its camera control immediately. The game uses this function to shut down commentary while in the middle of playing a node, as it can't smoothly blend out (since the commentary entities need to be removed)." )
DEFINE_SIMPLE_SCRIPTHOOK( CPointCommentaryNode::g_Hook_PreStartCommentary, "PreStartCommentary", FIELD_BOOLEAN, "Called just before commentary begins. Use this to modify variables or commentary behavior before it begins. Returning false will prevent the commentary from starting." )
END_SCRIPTDESC();
#endif // MAPBASE_VSCRIPT
IMPLEMENT_SERVERCLASS_ST( CPointCommentaryNode, DT_PointCommentaryNode )
SendPropBool( SENDINFO(m_bActive) ),
SendPropStringT( SENDINFO(m_iszCommentaryFile) ),
@ -192,6 +294,13 @@ IMPLEMENT_SERVERCLASS_ST( CPointCommentaryNode, DT_PointCommentaryNode )
SendPropInt( SENDINFO(m_iNodeNumber), 8, SPROP_UNSIGNED ),
SendPropInt( SENDINFO(m_iNodeNumberMax), 8, SPROP_UNSIGNED ),
SendPropEHandle( SENDINFO(m_hViewPosition) ),
#ifdef MAPBASE
SendPropStringT( SENDINFO( m_iszPrintName ) ),
SendPropInt( SENDINFO( m_iCommentaryType ), 2, SPROP_UNSIGNED ),
SendPropFloat( SENDINFO( m_flPanelScale ) ),
SendPropFloat( SENDINFO( m_flPanelX ) ),
SendPropFloat( SENDINFO( m_flPanelY ) ),
#endif
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( point_commentary_node, CPointCommentaryNode );
@ -895,7 +1004,29 @@ void CPointCommentaryNode::Spawn( void )
const char *szModel = STRING( GetModelName() );
if (!szModel || !*szModel)
{
#ifdef MAPBASE
switch (m_iCommentaryType)
{
case COMMENTARY_TYPE_TEXT:
szModel = "models/extras/info_text.mdl";
break;
case COMMENTARY_TYPE_IMAGE:
szModel = "models/extras/info_image.mdl";
break;
case COMMENTARY_TYPE_SCENE:
szModel = "models/extras/info_scene.mdl";
break;
default:
case COMMENTARY_TYPE_AUDIO:
szModel = "models/extras/info_speech.mdl";
break;
}
#else
szModel = "models/extras/info_speech.mdl";
#endif
SetModelName( AllocPooledString(szModel) );
}
@ -906,6 +1037,12 @@ void CPointCommentaryNode::Spawn( void )
AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
AddEffects( EF_NOSHADOW );
#ifdef MAPBASE
// Default to view position speed scale (which in turn defaults to 1.0)
if (m_flReturnSpeedScale == 0.0f)
m_flReturnSpeedScale = m_flViewPositionSpeedScale;
#endif
// Setup for animation
ResetSequence( LookupSequence("idle") );
SetThink( &CPointCommentaryNode::SpinThink );
@ -1119,6 +1256,19 @@ void CPointCommentaryNode::StartCommentary( void )
if ( !pPlayer )
return;
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_PreStartCommentary.CanRunInScope( m_ScriptScope ))
{
ScriptVariant_t functionReturn;
if ( g_Hook_PreStartCommentary.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_BOOLEAN )
{
// Don't play the commentary if it returned false
if (functionReturn.m_bool == false)
return;
}
}
#endif
m_bActive = true;
m_flAnimTime = gpGlobals->curtime;
@ -1140,6 +1290,21 @@ void CPointCommentaryNode::StartCommentary( void )
// Start the commentary
m_flStartTime = gpGlobals->curtime;
#ifdef MAPBASE
if (m_hViewPosition.Get())
{
m_flViewPositionChangedTime = gpGlobals->curtime;
}
else
{
m_flViewPositionChangedTime = -1.0f;
}
// This is now used in certain places to denote the "last blend to" origin
m_vecFinishOrigin = pPlayer->EyePosition();
m_vecFinishAngles = pPlayer->EyeAngles();
#endif
// If we have a view target, start blending towards it
if ( m_hViewTarget || m_hViewPosition.Get() )
{
@ -1214,6 +1379,10 @@ void CPointCommentaryNode::UpdateViewThink( void )
float dx = AngleDiff( angGoal.x, angCurrent.x );
float dy = AngleDiff( angGoal.y, angCurrent.y );
float mod = 1.0 - ExponentialDecay( 0.5, 0.3, gpGlobals->frametime );
#ifdef MAPBASE
if (m_flViewTargetSpeedScale != 1.0f)
mod *= m_flViewTargetSpeedScale;
#endif
float dxmod = dx * mod;
float dymod = dy * mod;
@ -1254,16 +1423,85 @@ void CPointCommentaryNode::UpdateViewThink( void )
}
// Blend to the target position over time.
float flCurTime = (gpGlobals->curtime - m_flStartTime);
#ifdef MAPBASE
float flCurTime = (gpGlobals->curtime - m_flViewPositionChangedTime);
if (m_flViewPositionSpeedScale != 1.0f)
flCurTime *= m_flViewPositionSpeedScale;
#else
float flCurTime = (gpGlobals->curtime - m_flStartTime);
#endif
float flBlendPerc = clamp( flCurTime * 0.5f, 0.f, 1.f );
// Figure out the current view position
Vector vecCurEye;
#ifdef MAPBASE
VectorLerp( m_vecFinishOrigin, m_hViewPosition.Get()->GetAbsOrigin(), flBlendPerc, vecCurEye );
#else
VectorLerp( pPlayer->EyePosition(), m_hViewPosition.Get()->GetAbsOrigin(), flBlendPerc, vecCurEye );
#endif
m_hViewPositionMover->SetAbsOrigin( vecCurEye );
SetNextThink( gpGlobals->curtime, s_pCommentaryUpdateViewThink );
}
#ifdef MAPBASE
else if ( m_flViewPositionChangedTime != -1.0f && m_hViewPositionMover )
{
// Blend back to the player's position over time.
float flCurTime = (gpGlobals->curtime - m_flViewPositionChangedTime);
if (m_flViewPositionSpeedScale != 1.0f)
flCurTime *= m_flViewPositionSpeedScale;
//float flTimeToBlend = MIN( 2.0, m_flViewPositionChangedTime - m_flStartTime );
//float flBlendPerc = 1.0f - clamp( flCurTime / flTimeToBlend, 0.f, 1.f );
float flBlendPerc = 1.0f - clamp( flCurTime * 0.5f, 0.f, 1.f );
//Msg("OUT: CurTime %.2f, BlendTime: %.2f, Blend: %.3f\n", flCurTime, flTimeToBlend, flBlendPerc );
// Only do this while we're still moving
if ( flBlendPerc > 0 )
{
// Figure out the current view position
Vector vecPlayerPos = pPlayer->EyePosition();
Vector vecToPosition = (m_vecFinishOrigin - vecPlayerPos);
Vector vecCurEye = pPlayer->EyePosition() + (vecToPosition * flBlendPerc);
m_hViewPositionMover->SetAbsOrigin( vecCurEye );
if ( m_hViewTarget )
{
Quaternion quatFinish;
Quaternion quatOriginal;
Quaternion quatCurrent;
AngleQuaternion( m_vecOriginalAngles, quatOriginal );
AngleQuaternion( m_vecFinishAngles, quatFinish );
QuaternionSlerp( quatFinish, quatOriginal, 1.0 - flBlendPerc, quatCurrent );
QAngle angCurrent;
QuaternionAngles( quatCurrent, angCurrent );
m_hViewPositionMover->SetAbsAngles( angCurrent );
}
SetNextThink( gpGlobals->curtime, s_pCommentaryUpdateViewThink );
return;
}
else
{
pPlayer->SnapEyeAngles( m_hViewPositionMover->GetAbsAngles() );
// Try to clean up the view position stuff without ending the commentary
if ( !m_hViewTargetAngles && pPlayer->GetActiveWeapon() )
{
pPlayer->GetActiveWeapon()->Deploy();
}
if (pPlayer->GetViewEntity() == m_hViewPositionMover)
{
pPlayer->SetViewEntity( NULL );
}
UTIL_Remove( m_hViewPositionMover );
m_flViewPositionChangedTime = -1.0f;
}
}
#endif
}
//-----------------------------------------------------------------------------
@ -1279,6 +1517,10 @@ void CPointCommentaryNode::UpdateViewPostThink( void )
{
// Blend back to the player's position over time.
float flCurTime = (gpGlobals->curtime - m_flFinishedTime);
#ifdef MAPBASE
if (m_flReturnSpeedScale != 1.0f)
flCurTime *= m_flReturnSpeedScale;
#endif
float flTimeToBlend = MIN( 2.0, m_flFinishedTime - m_flStartTime );
float flBlendPerc = 1.0f - clamp( flCurTime / flTimeToBlend, 0.f, 1.f );
@ -1440,6 +1682,79 @@ void CPointCommentaryNode::InputDisable( inputdata_t &inputdata )
SetDisabled( true );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointCommentaryNode::InputSetViewTarget( inputdata_t &inputdata )
{
m_hViewTarget = inputdata.value.Entity();
// Do not let Activate() reassign this
m_iszViewTarget = NULL_STRING;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointCommentaryNode::InputSetViewPosition( inputdata_t &inputdata )
{
if (m_hViewPosition.Get() && m_hViewPositionMover)
{
// In case the view position is being cleared, assign the "finish" vectors
m_vecFinishOrigin = m_hViewPositionMover->GetAbsOrigin();
m_vecFinishAngles = m_hViewPositionMover->GetAbsAngles();
}
else
{
CBasePlayer *pPlayer = GetCommentaryPlayer();
if (pPlayer)
{
// And in case it's a new view position coming from the player, assign the "finish" vectors to the player
m_vecFinishOrigin = pPlayer->EyePosition();
m_vecFinishAngles = m_vecOriginalAngles = pPlayer->EyeAngles();
}
}
m_hViewPosition = inputdata.value.Entity();
// Do not let Activate() reassign this
m_iszViewPosition = NULL_STRING;
m_flViewPositionChangedTime = gpGlobals->curtime;
// If we have a view target, start blending towards it
if ( m_hViewPosition.Get() )
{
SetContextThink( &CPointCommentaryNode::UpdateViewThink, gpGlobals->curtime, s_pCommentaryUpdateViewThink );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointCommentaryNode::InputSetViewTargetSpeed( inputdata_t &inputdata )
{
m_flViewTargetSpeedScale = inputdata.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointCommentaryNode::InputSetViewPositionSpeed( inputdata_t &inputdata )
{
m_flViewPositionSpeedScale = inputdata.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointCommentaryNode::InputSetReturnSpeed( inputdata_t &inputdata )
{
m_flReturnSpeedScale = inputdata.value.Float();
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -1501,7 +1816,11 @@ void CPointCommentaryNode::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways
bool CPointCommentaryNode::PreventsMovement( void )
{
// If we're moving the player's view at all, prevent movement
#ifdef MAPBASE
if ( m_hViewPosition.Get() || m_flViewPositionChangedTime != -1.0f )
#else
if ( m_hViewPosition.Get() )
#endif
return true;
return m_bPreventMovement;

View File

@ -2180,15 +2180,22 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2 );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 );
//ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 );
ADD_ACTIVITY_TO_SR( ACT_COVER_AR2_LOW );
#endif
#ifdef SHARED_COMBINE_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_COMBINE_THROW_GRENADE );
ADD_ACTIVITY_TO_SR( ACT_COMBINE_AR2_ALTFIRE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_THROW_GRENADE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_AR2_ALTFIRE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK1 );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK2 );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_ADVANCE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_FORWARD );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_GROUP );
@ -2198,8 +2205,249 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_TAKECOVER );
#endif
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_IDLE_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_WALK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RUN_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_COVER_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_COVER_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_AIM_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_AIM_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_IDLE_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_COVER_SHOTGUN_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_LOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_RPG );
ADD_ACTIVITY_TO_SR( ACT_WALK_MELEE );
ADD_ACTIVITY_TO_SR( ACT_RUN_MELEE );
ADD_ACTIVITY_TO_SR( ACT_RUN_PACKAGE );
ADD_ACTIVITY_TO_SR( ACT_RUN_SUITCASE );
ADD_ACTIVITY_TO_SR( ACT_ARM_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_ARM_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_ARM_RPG );
ADD_ACTIVITY_TO_SR( ACT_ARM_MELEE );
ADD_ACTIVITY_TO_SR( ACT_DISARM_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_DISARM_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_DISARM_RPG );
ADD_ACTIVITY_TO_SR( ACT_DISARM_MELEE );
#endif
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_CLIMB_ALL );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_IDLE );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_TOP );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_BOTTOM );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_DISMOUNT_BOTTOM );
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SHOTGUN_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_PISTOL_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_PISTOL_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_MED );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_PISTOL );
#endif
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: This is a multi-purpose table which links NPC activities to their gesture variants.
//-----------------------------------------------------------------------------
CAI_BaseNPC::actlink_t CAI_BaseNPC::gm_ActivityGestureLinks[] =
{
{ ACT_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK1 },
{ ACT_RANGE_ATTACK2, ACT_GESTURE_RANGE_ATTACK2 },
{ ACT_MELEE_ATTACK1, ACT_GESTURE_MELEE_ATTACK1 },
{ ACT_MELEE_ATTACK2, ACT_GESTURE_MELEE_ATTACK2 },
{ ACT_RELOAD, ACT_GESTURE_RELOAD },
{ ACT_RANGE_ATTACK1_LOW, ACT_GESTURE_RANGE_ATTACK1 }, // NOTE: ACT_GESTURE_RANGE_ATTACK1_LOW exists, but isn't used
{ ACT_RANGE_ATTACK2_LOW, ACT_GESTURE_RANGE_ATTACK2 }, // NOTE: ACT_GESTURE_RANGE_ATTACK2_LOW exists, but isn't used
{ ACT_RELOAD_LOW, ACT_GESTURE_RELOAD },
{ ACT_MELEE_ATTACK_SWING, ACT_GESTURE_MELEE_ATTACK_SWING },
// -----------------------------------------------------------
{ ACT_RANGE_ATTACK_AR2, ACT_GESTURE_RANGE_ATTACK_AR2 },
{ ACT_RANGE_ATTACK_AR2_LOW, ACT_GESTURE_RANGE_ATTACK_AR2 },
{ ACT_RANGE_ATTACK_SMG1, ACT_GESTURE_RANGE_ATTACK_SMG1 },
{ ACT_RANGE_ATTACK_SMG1_LOW, ACT_GESTURE_RANGE_ATTACK_SMG1 },
{ ACT_RANGE_ATTACK_SHOTGUN, ACT_GESTURE_RANGE_ATTACK_SHOTGUN },
{ ACT_RANGE_ATTACK_SHOTGUN_LOW, ACT_GESTURE_RANGE_ATTACK_SHOTGUN },
{ ACT_RANGE_ATTACK_PISTOL, ACT_GESTURE_RANGE_ATTACK_PISTOL },
{ ACT_RANGE_ATTACK_PISTOL_LOW, ACT_GESTURE_RANGE_ATTACK_PISTOL },
// -----------------------------------------------------------
{ ACT_SMALL_FLINCH, ACT_GESTURE_SMALL_FLINCH },
{ ACT_BIG_FLINCH, ACT_GESTURE_BIG_FLINCH },
{ ACT_FLINCH_HEAD, ACT_GESTURE_FLINCH_HEAD },
{ ACT_FLINCH_CHEST, ACT_GESTURE_FLINCH_CHEST },
{ ACT_FLINCH_STOMACH, ACT_GESTURE_FLINCH_STOMACH },
{ ACT_FLINCH_LEFTARM, ACT_GESTURE_FLINCH_LEFTARM },
{ ACT_FLINCH_RIGHTARM, ACT_GESTURE_FLINCH_RIGHTARM },
{ ACT_FLINCH_LEFTLEG, ACT_GESTURE_FLINCH_LEFTLEG },
{ ACT_FLINCH_RIGHTLEG, ACT_GESTURE_FLINCH_RIGHTLEG },
// -----------------------------------------------------------
#if AR2_ACTIVITY_FIX == 1
{ ACT_RELOAD_AR2, ACT_GESTURE_RELOAD_AR2 },
{ ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2 },
#endif
{ ACT_RELOAD_SMG1, ACT_GESTURE_RELOAD_SMG1 },
{ ACT_RELOAD_SMG1_LOW, ACT_GESTURE_RELOAD_SMG1 },
{ ACT_RELOAD_SHOTGUN, ACT_GESTURE_RELOAD_SHOTGUN },
{ ACT_RELOAD_SHOTGUN_LOW, ACT_GESTURE_RELOAD_SHOTGUN },
{ ACT_RELOAD_PISTOL, ACT_GESTURE_RELOAD_PISTOL },
{ ACT_RELOAD_PISTOL_LOW, ACT_GESTURE_RELOAD_PISTOL },
#ifdef SHARED_COMBINE_ACTIVITIES
{ ACT_SPECIAL_ATTACK1, ACT_GESTURE_SPECIAL_ATTACK1 },
{ ACT_SPECIAL_ATTACK2, ACT_GESTURE_SPECIAL_ATTACK2 },
{ ACT_COMBINE_THROW_GRENADE, ACT_GESTURE_COMBINE_THROW_GRENADE },
{ ACT_COMBINE_AR2_ALTFIRE, ACT_GESTURE_COMBINE_AR2_ALTFIRE },
{ ACT_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_ADVANCE },
{ ACT_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_FORWARD },
{ ACT_SIGNAL_GROUP, ACT_GESTURE_SIGNAL_GROUP },
{ ACT_SIGNAL_HALT, ACT_GESTURE_SIGNAL_HALT },
{ ACT_SIGNAL_LEFT, ACT_GESTURE_SIGNAL_LEFT },
{ ACT_SIGNAL_RIGHT, ACT_GESTURE_SIGNAL_RIGHT },
{ ACT_SIGNAL_TAKECOVER, ACT_GESTURE_SIGNAL_TAKECOVER },
#endif
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RANGE_ATTACK_REVOLVER, ACT_GESTURE_RANGE_ATTACK_REVOLVER },
{ ACT_RANGE_ATTACK_REVOLVER_LOW, ACT_GESTURE_RANGE_ATTACK_REVOLVER },
{ ACT_RANGE_ATTACK_CROSSBOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW },
{ ACT_RANGE_ATTACK_CROSSBOW_LOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW },
{ ACT_RANGE_ATTACK_RPG, ACT_GESTURE_RANGE_ATTACK_RPG },
{ ACT_RANGE_ATTACK_RPG_LOW, ACT_GESTURE_RANGE_ATTACK_RPG },
{ ACT_RELOAD_REVOLVER, ACT_GESTURE_RELOAD_REVOLVER },
{ ACT_RELOAD_REVOLVER_LOW, ACT_GESTURE_RELOAD_REVOLVER },
{ ACT_RELOAD_CROSSBOW, ACT_GESTURE_RELOAD_CROSSBOW },
{ ACT_RELOAD_CROSSBOW_LOW, ACT_GESTURE_RELOAD_CROSSBOW },
#endif
};
Activity CAI_BaseNPC::GetGestureVersionOfActivity( Activity inActivity )
{
actlink_t *pTable = gm_ActivityGestureLinks;
int actCount = ARRAYSIZE( gm_ActivityGestureLinks );
for ( int i = 0; i < actCount; i++, pTable++ )
{
if ( inActivity == pTable->sequence )
{
return pTable->gesture;
}
}
return ACT_INVALID;
}
Activity CAI_BaseNPC::GetSequenceVersionOfGesture( Activity inActivity )
{
actlink_t *pTable = gm_ActivityGestureLinks;
int actCount = ARRAYSIZE( gm_ActivityGestureLinks );
for (int i = 0; i < actCount; i++, pTable++)
{
if (inActivity == pTable->gesture)
{
return pTable->sequence;
}
}
return ACT_INVALID;
}
#endif

View File

@ -101,8 +101,8 @@ END_DATADESC()
#ifdef MAPBASE_VSCRIPT
BEGIN_ENT_SCRIPTDESC( CAI_BaseActor, CAI_BaseNPC, "The base class for NPCs which act in complex choreo scenes." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTarget, "AddLookTarget", "Add a potential look target for this actor." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTargetPos, "AddLookTargetPos", "Add a potential look target position for this actor." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTarget, "AddLookTarget", "Add a potential look target for this actor with the specified importance, duration, and ramp." )
DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTargetPos, "AddLookTargetPos", "Add a potential look target position for this actor with the specified importance, duration, and ramp." )
END_SCRIPTDESC();
#endif
@ -835,7 +835,11 @@ void CAI_BaseActor::UpdateLatchedValues( )
// set head latch
m_fLatchedPositions |= HUMANOID_LATCHED_HEAD;
#ifdef MAPBASE // From Alien Swarm SDK
if ( CanSkipAnimation() || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection ))
#else
if (!HasCondition( COND_IN_PVS ) || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection ))
#endif
{
m_latchedEyeOrigin = BaseClass::EyePosition( );
AngleVectors( GetLocalAngles(), &m_latchedHeadDirection );
@ -1626,7 +1630,11 @@ void CAI_BaseActor::MaintainLookTargets( float flInterval )
}
// don't bother with any of the rest if the player can't see you
#ifdef MAPBASE // From Alien Swarm SDK
if ( CanSkipAnimation() )
#else
if (!HasCondition( COND_IN_PVS ))
#endif
{
return;
}

View File

@ -100,6 +100,10 @@
#include "items.h"
#endif
#ifdef MAPBASE_VSCRIPT
#include "mapbase/vscript_funcs_shared.h"
#endif
#include "env_debughistory.h"
#include "collisionutils.h"
@ -165,6 +169,8 @@ extern ConVar ai_vehicle_avoidance;
extern ISoundEmitterSystemBase *soundemitterbase;
ConVar ai_dynint_always_enabled( "ai_dynint_always_enabled", "0", FCVAR_NONE, "Makes the \"Don't Care\" setting equivalent to \"Yes\"." );
ConVar ai_debug_fake_sequence_gestures_always_play( "ai_debug_fake_sequence_gestures_always_play", "0", FCVAR_NONE, "Always plays fake sequence gestures." );
#endif
#ifndef _RETAIL
@ -309,6 +315,8 @@ ScriptHook_t CAI_BaseNPC::g_Hook_QuerySeeEntity;
ScriptHook_t CAI_BaseNPC::g_Hook_TranslateActivity;
ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule;
ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition;
ScriptHook_t CAI_BaseNPC::g_Hook_OverrideMove;
ScriptHook_t CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture;
#endif
//
@ -664,7 +672,7 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo
#ifdef HL2_EPISODIC
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if ( pPlayer->IRelationType( this ) != D_LI )
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
{
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
@ -729,7 +737,7 @@ HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy )
AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(pEnemy) );
if (info)
{
hScript = g_pScriptVM->RegisterInstance( info );
hScript = g_pScriptVM->RegisterInstance( reinterpret_cast<Script_AI_EnemyInfo_t*>(info) );
}
return hScript;
@ -989,29 +997,27 @@ int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info )
// only fire once per frame
m_OnDamaged.FireOutput( info.GetAttacker(), this);
if( info.GetAttacker()->IsPlayer() )
if ( info.GetAttacker() )
{
m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this );
// This also counts as being harmed by player's squad.
m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this );
}
else
{
// See if the person that injured me is an NPC.
#ifdef MAPBASE
// Sometimes I find these things and I can't just sit idly by.
CAI_BaseNPC *pAttacker = info.GetAttacker()->MyNPCPointer();
#else
CAI_BaseNPC *pAttacker = dynamic_cast<CAI_BaseNPC *>( info.GetAttacker() );
#endif
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if( pAttacker && pAttacker->IsAlive() && pPlayer )
if( info.GetAttacker()->IsPlayer() )
{
if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() )
m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this );
// This also counts as being harmed by player's squad.
m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this );
}
else
{
// See if the person that injured me is an NPC.
CAI_BaseNPC *pAttacker = info.GetAttacker()->MyNPCPointer();
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if( pAttacker && pAttacker->IsAlive() && pPlayer )
{
m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this );
if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() )
{
m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this );
}
}
}
}
@ -2592,7 +2598,11 @@ bool CAI_BaseNPC::FValidateHintType ( CAI_Hint *pHint )
Activity CAI_BaseNPC::GetHintActivity( short sHintType, Activity HintsActivity )
{
if ( HintsActivity != ACT_INVALID )
#ifdef MAPBASE
return TranslateActivity( HintsActivity ); // Always translate the activity
#else
return HintsActivity;
#endif
return ACT_IDLE;
}
@ -6507,7 +6517,7 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur
//-----------------------------------------------------------------------------
Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity )
{
if (CapabilitiesGet() & bits_CAP_DUCK)
if (CapabilitiesGet() & bits_CAP_DUCK && CanTranslateCrouchActivity())
{
// ========================================================================
// The main issue with cover hint nodes is that crouch activities are not translated at the right time
@ -6539,10 +6549,18 @@ Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity )
CAI_Hint *pHint = GetHintNode();
if (pHint)
{
if (pHint->HintType() == HINT_TACTICAL_COVER_LOW || pHint->HintType() == HINT_TACTICAL_COVER_MED)
if (pHint->HintType() == HINT_TACTICAL_COVER_LOW)
{
nCoverActivity = ACT_RANGE_ATTACK1_LOW;
}
else if (pHint->HintType() == HINT_TACTICAL_COVER_MED)
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
nCoverActivity = ACT_RANGE_ATTACK1_MED;
#else
nCoverActivity = ACT_RANGE_ATTACK1_LOW;
#endif
}
}
}
}
@ -6584,15 +6602,30 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity )
//if (eNewActivity == ACT_DROP_WEAPON)
// return TranslateActivity(ACT_IDLE);
// ---------------------------------------------
// Accounts for certain act busy activities that aren't on all NPCs.
if (eNewActivity == ACT_BUSY_QUEUE || eNewActivity == ACT_BUSY_STAND)
return TranslateActivity(ACT_IDLE);
// ---------------------------------------------
if (eNewActivity == ACT_WALK_ANGRY)
return TranslateActivity(ACT_WALK);
// GetCoverActivity() should have this covered.
// ---------------------------------------------
// If one climbing animation isn't available, use the other
if (eNewActivity == ACT_CLIMB_DOWN)
return ACT_CLIMB_UP;
else if (eNewActivity == ACT_CLIMB_UP)
return ACT_CLIMB_DOWN;
// ---------------------------------------------
if (eNewActivity == ACT_COVER_MED)
eNewActivity = ACT_COVER_LOW;
//if (eNewActivity == ACT_COVER)
// return TranslateActivity(ACT_IDLE);
@ -6607,6 +6640,14 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity )
//-----------------------------------------------------------------------------
Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( GetNavType() == NAV_CLIMB && eNewActivity == ACT_IDLE )
{
// Schedules which break into idle activities should try to maintain the climbing animation.
return ACT_CLIMB_IDLE;
}
#endif
#ifdef MAPBASE
Assert( eNewActivity != ACT_INVALID );
@ -6616,15 +6657,7 @@ Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity )
{
case ACT_RANGE_ATTACK1: eNewActivity = ACT_RANGE_ATTACK1_LOW; break;
case ACT_RELOAD: eNewActivity = ACT_RELOAD_LOW; break;
case ACT_IDLE: eNewActivity = ACT_CROUCHIDLE; break;
// ====
// HACK : LEIPZIG 06 - The underlying problem is that the AR2 and SMG1 cannot map IDLE_ANGRY to a crouched equivalent automatically
// which causes the character to pop up and down in their idle state of firing while crouched. -- jdw
case ACT_IDLE_ANGRY_SMG1:
case ACT_IDLE_ANGRY_AR2:
eNewActivity = ACT_RANGE_AIM_LOW;
break;
case ACT_IDLE: eNewActivity = ACT_RANGE_AIM_LOW; break; // ACT_CROUCHIDLE is more-or-less deprecated and not friendly to weapon translation
}
}
@ -6837,7 +6870,12 @@ void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence
translatedActivity = TranslateActivity( NewActivity, &weaponActivity );
#ifdef MAPBASE
// Cover cases where TranslateActivity() returns a sequence by using ACT_SCRIPT_CUSTOM_MOVE
if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE || translatedActivity == ACT_SCRIPT_CUSTOM_MOVE )
#else
if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE )
#endif
{
iSequence = GetScriptCustomMoveSequence();
}
@ -7023,6 +7061,23 @@ void CAI_BaseNPC::SetIdealActivity( Activity NewActivity )
// Perform translation in case we need to change sequences within a single activity,
// such as between a standing idle and a crouching idle.
ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity);
#ifdef MAPBASE
// Check if we need a gesture to imitate this sequence
if ( ShouldPlayFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ) )
{
Activity nGesture = SelectFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity );
if (nGesture != -1)
{
PlayFakeSequenceGesture( nGesture, m_IdealActivity, m_IdealTranslatedActivity );
}
}
else if (GetFakeSequenceGesture() != -1)
{
// Reset the current gesture sequence if there is one
ResetFakeSequenceGesture();
}
#endif
}
@ -7071,6 +7126,14 @@ void CAI_BaseNPC::AdvanceToIdealActivity(void)
//DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence));
SetActivity(m_IdealActivity);
}
#ifdef MAPBASE
// If there was a gesture imitating a sequence, get rid of it
if ( GetFakeSequenceGesture() != -1 )
{
ResetFakeSequenceGesture();
}
#endif
}
@ -7128,6 +7191,12 @@ void CAI_BaseNPC::MaintainActivity(void)
}
// Else a transition sequence is in progress, do nothing.
}
#ifdef MAPBASE
else if (GetFakeSequenceGesture() != -1)
{
// Don't advance even if the sequence gesture is finished, as advancing would just play the original activity afterwards
}
#endif
// Else get a specific sequence for the activity and try to transition to that.
else
{
@ -7146,11 +7215,103 @@ void CAI_BaseNPC::MaintainActivity(void)
}
#ifdef MAPBASE
bool CAI_BaseNPC::ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity )
{
// Don't do anything if we're resetting our activity
if (GetActivity() == ACT_RESET)
return false;
// No need to do this while we're moving
if (IsCurTaskContinuousMove())
return false;
if (ai_debug_fake_sequence_gestures_always_play.GetBool())
return true;
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_ShouldPlayFakeSequenceGesture.CanRunInScope(m_ScriptScope))
{
// activity, translatedActivity
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { GetActivityName( nActivity ), GetActivityName( nTranslatedActivity ) };
if (g_Hook_ShouldPlayFakeSequenceGesture.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_type == FIELD_BOOLEAN)
return functionReturn.m_bool;
}
}
#endif
if (GetHintNode() && GetHintNode()->HintActivityName() != NULL_STRING)
{
switch (GetHintNode()->HintType())
{
// Cover nodes with custom activities should allow NPCs to do things like reload while in cover.
case HINT_TACTICAL_COVER_LOW:
case HINT_TACTICAL_COVER_MED:
case HINT_TACTICAL_COVER_CUSTOM:
if (HasMemory( bits_MEMORY_INCOVER ))
{
// Don't attack while using a custom animation in cover
if (nActivity != ACT_RANGE_ATTACK1 && nActivity != ACT_RANGE_ATTACK1_LOW)
return true;
}
break;
}
}
return false;
}
Activity CAI_BaseNPC::SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity )
{
return GetGestureVersionOfActivity( nTranslatedActivity );
}
inline void CAI_BaseNPC::PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence )
{
RestartGesture( nActivity, true, true );
m_FakeSequenceGestureLayer = FindGestureLayer( nActivity );
switch ( nSequence )
{
case ACT_RANGE_ATTACK1:
//case ACT_RANGE_ATTACK2:
{
OnRangeAttack1();
// FIXME: this seems a bit wacked
Weapon_SetActivity( Weapon_TranslateActivity( nSequence ), 0 );
} break;
}
}
inline int CAI_BaseNPC::GetFakeSequenceGesture()
{
return m_FakeSequenceGestureLayer;
}
void CAI_BaseNPC::ResetFakeSequenceGesture()
{
SetLayerCycle( m_FakeSequenceGestureLayer, 1.0f );
m_FakeSequenceGestureLayer = -1;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Returns true if our ideal activity has finished playing.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsActivityFinished( void )
{
#ifdef MAPBASE
if (GetFakeSequenceGesture() != -1)
{
return IsLayerFinished( GetFakeSequenceGesture() );
}
#endif
return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence));
}
@ -7186,6 +7347,15 @@ void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity )
eNewActivity == ACT_WALK )
{
Stand();
#ifdef MAPBASE
// Unlock custom cover nodes
if (GetHintNode() && GetHintNode()->HintType() == HINT_TACTICAL_COVER_CUSTOM && HasMemory(bits_MEMORY_INCOVER))
{
GetHintNode()->Unlock( GetHintDelay( GetHintNode()->HintType() ) );
SetHintNode( NULL );
}
#endif
}
}
@ -7462,7 +7632,11 @@ void CAI_BaseNPC::SetHullSizeNormal( bool force )
if ( m_fIsUsingSmallHull || force )
{
// Find out what the height difference will be between the versions and adjust our bbox accordingly to keep us level
#ifdef MAPBASE // From Alien Swarm SDK
const float flScale = MIN( 1.0f, GetModelScale() ); // NOTE: Cannot scale NPC bounding box up, as pathfinding will fail (hull needs to match the traces used for the node network)
#else
const float flScale = GetModelScale();
#endif
Vector vecMins = ( GetHullMins() * flScale );
Vector vecMaxs = ( GetHullMaxs() * flScale );
@ -7770,7 +7944,7 @@ int CAI_BaseNPC::HolsterWeapon( void )
if ( IsWeaponHolstered() )
return -1;
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef MAPBASE
Activity activity = TranslateActivity( ACT_DISARM );
int iHolsterGesture = FindGestureLayer( activity );
if ( iHolsterGesture != -1 )
@ -7826,7 +8000,7 @@ int CAI_BaseNPC::UnholsterWeapon( void )
if ( !IsWeaponHolstered() )
return -1;
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef MAPBASE
Activity activity = TranslateActivity( ACT_ARM );
int iHolsterGesture = FindGestureLayer( activity );
#else
@ -7855,13 +8029,12 @@ int CAI_BaseNPC::UnholsterWeapon( void )
{
SetActiveWeapon( GetWeapon(i) );
#ifdef COMPANION_HOLSTER_WORKAROUND
int iLayer = AddGesture( activity, true );
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
#ifdef MAPBASE
int iLayer = AddGesture( TranslateActivity( ACT_ARM ), true );
#else
int iLayer = AddGesture( ACT_ARM, true );
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
#endif
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
if (iLayer != -1)
{
@ -8040,6 +8213,26 @@ bool CAI_BaseNPC::DoUnholster( void )
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the NPC should be unholstering their weapon
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::ShouldUnholsterWeapon( void )
{
return GetState() == NPC_STATE_COMBAT;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the NPC can unholster their weapon
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::CanUnholsterWeapon( void )
{
// Don't unholster during special navigation
if ( GetNavType() == NAV_JUMP || GetNavType() == NAV_CLIMB )
return false;
return IsWeaponHolstered();
}
//------------------------------------------------------------------------------
// Purpose: Give the NPC in question the weapon specified
//------------------------------------------------------------------------------
@ -9106,27 +9299,45 @@ Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint )
switch (pHint->HintType())
{
case HINT_TACTICAL_COVER_MED:
#ifndef MAPBASE // I know what you're thinking, but ACT_COVER_MED is pretty much deprecated at this point anyway.
{
nCoverActivity = ACT_COVER_MED;
#ifdef MAPBASE
// Some NPCs lack ACT_COVER_MED, but could easily use ACT_COVER_LOW.
if (SelectWeightedSequence(nCoverActivity) == ACTIVITY_NOT_AVAILABLE)
nCoverActivity = ACT_COVER_LOW;
#endif
// NPCs which lack ACT_COVER_MED should fall through to HINT_TACTICAL_COVER_LOW
if (SelectWeightedSequence( ACT_COVER_MED ) != ACTIVITY_NOT_AVAILABLE)
{
nCoverActivity = ACT_COVER_MED;
}
#else
nCoverActivity = ACT_COVER_MED;
break;
}
#endif
}
case HINT_TACTICAL_COVER_LOW:
{
#ifdef MAPBASE
if (pHint->HintActivityName() != NULL_STRING)
nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) );
else
#endif
// Make sure nCoverActivity wasn't already assigned above, then fall through to HINT_TACTICAL_COVER_CUSTOM
if (nCoverActivity == ACT_INVALID)
nCoverActivity = ACT_COVER_LOW;
#else
nCoverActivity = ACT_COVER_LOW;
break;
#endif
}
#ifdef MAPBASE
case HINT_TACTICAL_COVER_CUSTOM:
{
if (pHint->HintActivityName() != NULL_STRING)
{
nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) );
if (nCoverActivity == ACT_INVALID)
{
m_iszSceneCustomMoveSeq = pHint->HintActivityName();
nCoverActivity = ACT_SCRIPT_CUSTOM_MOVE;
}
}
break;
}
#endif
}
}
@ -9164,6 +9375,13 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget )
return UTIL_VecToYaw( vecProjection - GetLocalOrigin() );
}
#ifdef MAPBASE
// Allow hint nodes to override the yaw without needing to control AI
else if (GetHintNode() && GetHintNode()->OverridesNPCYaw( this ))
{
return GetHintNode()->Yaw();
}
#endif
else
{
return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() );
@ -9504,7 +9722,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
if ( GetActiveWeapon() )
{
#ifdef MAPBASE
GetActiveWeapon()->Reload_NPC();
GetActiveWeapon()->Reload_NPC( true );
#else
GetActiveWeapon()->WeaponSound( RELOAD_NPC );
GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
@ -9528,8 +9746,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
case EVENT_WEAPON_RELOAD_FILL_CLIP:
{
if ( GetActiveWeapon() )
{
{
#ifdef MAPBASE
GetActiveWeapon()->Reload_NPC( false );
#else
GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
#endif
ClearCondition(COND_LOW_PRIMARY_AMMO);
ClearCondition(COND_NO_PRIMARY_AMMO);
ClearCondition(COND_NO_SECONDARY_AMMO);
@ -9544,7 +9766,11 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
case NPC_EVENT_OPEN_DOOR:
{
#ifdef MAPBASE
CBasePropDoor *pDoor = m_hOpeningDoor;
#else
CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData();
#endif
if (pDoor != NULL)
{
OpenPropDoorNow( pDoor );
@ -10692,6 +10918,7 @@ Vector CAI_BaseNPC::GetActualShootPosition( const Vector &shootOrigin )
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope))
{
// shootOrigin, target
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { shootOrigin, ToHScript( GetEnemy() ) };
if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ))
@ -11491,6 +11718,14 @@ bool CAI_BaseNPC::ChooseEnemy( void )
//=========================================================
void CAI_BaseNPC::PickupWeapon( CBaseCombatWeapon *pWeapon )
{
#ifdef MAPBASE
if ( pWeapon->VPhysicsGetObject() && pWeapon->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
pPlayer->ForceDropOfCarriedPhysObjects( pWeapon );
}
#endif
pWeapon->OnPickedUp( this );
Weapon_Equip( pWeapon );
m_iszPendingWeapon = NULL_STRING;
@ -11572,6 +11807,10 @@ CBaseEntity *CAI_BaseNPC::DropItem ( const char *pszItemName, Vector vecPos, QAn
pItem->ApplyLocalAngularVelocityImpulse( AngularImpulse( 0, random->RandomFloat( 0, 100 ), 0 ) );
}
#ifdef MAPBASE
m_OnItemDrop.Set( pItem, pItem, this );
#endif
return pItem;
}
else
@ -11894,6 +12133,7 @@ BEGIN_DATADESC( CAI_BaseNPC )
DEFINE_KEYFIELD( m_FriendlyFireOverride, FIELD_INTEGER, "FriendlyFireOverride" ),
DEFINE_KEYFIELD( m_flSpeedModifier, FIELD_FLOAT, "BaseSpeedModifier" ),
DEFINE_FIELD( m_FakeSequenceGestureLayer, FIELD_INTEGER ),
#endif
// Satisfy classcheck
@ -11939,6 +12179,7 @@ BEGIN_DATADESC( CAI_BaseNPC )
DEFINE_OUTPUT( m_OnForcedInteractionFinished, "OnForcedInteractionFinished" ),
#ifdef MAPBASE
DEFINE_OUTPUT( m_OnItemPickup, "OnItemPickup" ),
DEFINE_OUTPUT( m_OnItemDrop, "OnItemDrop" ),
#endif
// Inputs
@ -12050,6 +12291,11 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." )
DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivity, "GetGestureVersionOfActivity", "Get the gesture activity counterpart of the specified sequence activity, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivityID, "GetGestureVersionOfActivityID", "Get the gesture activity ID counterpart of the specified sequence activity ID, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGesture, "GetSequenceVersionOfGesture", "Get the sequence activity counterpart of the specified gesture activity, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGestureID, "GetSequenceVersionOfGestureID", "Get the sequence activity ID counterpart of the specified gesture activity ID, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSchedule, "GetSchedule", "Get the NPC's current schedule." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetScheduleID, "GetScheduleID", "Get the NPC's current schedule ID." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetSchedule, "SetSchedule", "Set the NPC's current schedule." )
@ -12096,10 +12342,17 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTHOOK_PARAM( "schedule", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "schedule_id", FIELD_INTEGER )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VOID, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" )
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VECTOR, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" )
DEFINE_SCRIPTHOOK_PARAM( "shootOrigin", FIELD_VECTOR )
DEFINE_SCRIPTHOOK_PARAM( "target", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OverrideMove, "OverrideMove", FIELD_VOID, "Called when the NPC runs movement code, allowing the NPC's movement to be overridden by some other method. (NOTE: NPCs which override this themselves might not always use this hook!)" )
DEFINE_SCRIPTHOOK_PARAM( "interval", FIELD_FLOAT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture, "ShouldPlayFakeSequenceGesture", FIELD_BOOLEAN, "Called when an activity is set on a NPC. Returning true will make the NPC convert the activity into a gesture (if a gesture is available) and continue their current activity instead." )
DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "translatedActivity", FIELD_CSTRING )
END_SCRIPTHOOK()
END_SCRIPTDESC();
#endif
@ -12750,6 +13003,8 @@ CAI_BaseNPC::CAI_BaseNPC(void)
#ifdef MAPBASE
m_iDynamicInteractionsAllowed = TRS_NONE;
m_flSpeedModifier = 1.0f;
m_FakeSequenceGestureLayer = -1;
#endif
}
@ -13406,6 +13661,10 @@ void CAI_BaseNPC::Teleport( const Vector *newPosition, const QAngle *newAngles,
CleanupScriptsOnTeleport( false );
BaseClass::Teleport( newPosition, newAngles, newVelocity );
#ifdef MAPBASE // From Alien Swarm SDK
CheckPVSCondition();
#endif
}
//-----------------------------------------------------------------------------
@ -13529,6 +13788,20 @@ bool CAI_BaseNPC::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInt
bool CAI_BaseNPC::OverrideMove( float flInterval )
{
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_OverrideMove.CanRunInScope(m_ScriptScope))
{
// interval
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { flInterval };
if (g_Hook_OverrideMove.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_type == FIELD_BOOLEAN)
return functionReturn.m_bool;
}
}
#endif
return false;
}
@ -13700,6 +13973,10 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal,
GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute );
#ifdef MAPBASE
GetNavigator()->SetArrivalDirection( opendata.vecFaceDir );
#endif
m_hOpeningDoor = pDoor;
pMoveGoal->maxDist = distClear;
*pResult = AIMR_CHANGE_TYPE;
@ -13720,6 +13997,21 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal,
//-----------------------------------------------------------------------------
void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor )
{
#ifdef MAPBASE
opendata_t opendata;
pDoor->GetNPCOpenData(this, opendata);
if (HaveSequenceForActivity( opendata.eActivity ))
{
int iLayer = AddGesture( opendata.eActivity );
float flDuration = GetLayerDuration( iLayer );
// Face the door and wait for the activity to finish before trying to move through the doorway.
m_flMoveWaitFinished = gpGlobals->curtime + flDuration + pDoor->GetOpenInterval();
AddFacingTarget( opendata.vecFaceDir, 1.0, flDuration );
}
else
#else
// dvs: not quite working, disabled for now.
//opendata_t opendata;
//pDoor->GetNPCOpenData(this, opendata);
@ -13729,6 +14021,7 @@ void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor )
// SetIdealActivity(opendata.eActivity);
//}
//else
#endif
{
// We don't have an appropriate sequence, just open the door magically.
OpenPropDoorNow( pDoor );
@ -13748,6 +14041,15 @@ void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor )
// Wait for the door to finish opening before trying to move through the doorway.
m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval();
#ifdef MAPBASE
// Remove the door from our waypoint
if (GetNavigator()->GetPath() && GetNavigator()->GetCurWaypointFlags() & bits_WP_TO_DOOR)
{
GetNavigator()->GetPath()->GetCurWaypoint()->ModifyFlags( bits_WP_TO_DOOR, false );
GetNavigator()->GetPath()->GetCurWaypoint()->m_hData = NULL;
}
#endif
}
@ -15937,6 +16239,26 @@ bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget )
return bResult;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Check if this position will block our line of sight if aiming low.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist )
{
Vector vGunPos = vecPosition;
vGunPos += (GetCrouchGunOffset() + vecRight * 8);
trace_t tr;
AI_TraceLOS( vGunPos, vGunPos + (vecForward * flDist), this, &tr );
if (tr.fraction != 1.0)
{
return false;
}
return true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -15952,9 +16274,16 @@ bool CAI_BaseNPC::IsCrouchedActivity( Activity activity )
case ACT_COVER_SMG1_LOW:
case ACT_RELOAD_SMG1_LOW:
#ifdef MAPBASE
//case ACT_RELOAD_AR2_LOW:
#if AR2_ACTIVITY_FIX == 1
case ACT_COVER_AR2_LOW:
case ACT_RELOAD_AR2_LOW:
#endif
case ACT_RELOAD_PISTOL_LOW:
case ACT_RELOAD_SHOTGUN_LOW:
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
case ACT_RELOAD_REVOLVER_LOW:
case ACT_RELOAD_CROSSBOW_LOW:
#endif
#endif
return true;
}

View File

@ -998,6 +998,7 @@ public:
Activity NPC_TranslateActivity( Activity eNewActivity );
#ifdef MAPBASE
Activity TranslateCrouchActivity( Activity baseAct );
virtual bool CanTranslateCrouchActivity( void ) { return true; }
virtual Activity NPC_BackupActivity( Activity eNewActivity );
#endif
Activity GetActivity( void ) { return m_Activity; }
@ -1020,6 +1021,25 @@ public:
void SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity);
#ifdef MAPBASE
//-----------------------------------------------------
// Returns the gesture variant of an activity (i.e. "ACT_GESTURE_RANGE_ATTACK1")
static Activity GetGestureVersionOfActivity( Activity inActivity );
// Returns the sequence variant of a gesture activity
static Activity GetSequenceVersionOfGesture( Activity inActivity );
//-----------------------------------------------------
virtual bool ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity );
virtual Activity SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity );
void PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence );
int GetFakeSequenceGesture();
void ResetFakeSequenceGesture();
#endif
private:
void AdvanceToIdealActivity(void);
@ -1033,6 +1053,10 @@ private:
Activity m_IdealTranslatedActivity; // Desired actual translated animation state
Activity m_IdealWeaponActivity; // Desired weapon animation state
#ifdef MAPBASE
int m_FakeSequenceGestureLayer; // The gesture layer impersonating a sequence (-1 if invalid)
#endif
CNetworkVar(int, m_iDeathPose );
CNetworkVar(int, m_iDeathFrame );
@ -1217,6 +1241,8 @@ public:
#endif
#ifdef MAPBASE_VSCRIPT
private:
// VScript stuff uses "VScript" instead of just "Script" to avoid
// confusion with NPC_STATE_SCRIPT or StartScripting
HSCRIPT VScriptGetEnemy();
@ -1243,6 +1269,11 @@ public:
int ScriptTranslateActivity( const char *szActivity ) { return TranslateActivity( (Activity)GetActivityID( szActivity ) ); }
int ScriptTranslateActivityID( int iActivity ) { return TranslateActivity( (Activity)iActivity ); }
const char* VScriptGetGestureVersionOfActivity( const char *pszActivity ) { return GetActivityName( GetGestureVersionOfActivity( (Activity)GetActivityID( pszActivity ) ) ); }
int VScriptGetGestureVersionOfActivityID( int iActivity ) { return GetGestureVersionOfActivity( (Activity)iActivity ); }
const char* VScriptGetSequenceVersionOfGesture( const char *pszActivity ) { return GetActivityName( GetSequenceVersionOfGesture( (Activity)GetActivityID( pszActivity ) ) ); }
int VScriptGetSequenceVersionOfGestureID( int iActivity ) { return GetSequenceVersionOfGesture( (Activity)iActivity ); }
const char* VScriptGetSchedule();
int VScriptGetScheduleID();
void VScriptSetSchedule( const char *szSchedule );
@ -1731,8 +1762,8 @@ public:
virtual bool DoHolster(void);
virtual bool DoUnholster(void);
virtual bool ShouldUnholsterWeapon() { return GetState() == NPC_STATE_COMBAT; }
virtual bool CanUnholsterWeapon() { return IsWeaponHolstered(); }
virtual bool ShouldUnholsterWeapon();
virtual bool CanUnholsterWeapon();
void InputGiveWeaponHolstered( inputdata_t &inputdata );
void InputChangeWeapon( inputdata_t &inputdata );
@ -2119,6 +2150,7 @@ public:
COutputEHANDLE m_OnUnholsterWeapon;
COutputEHANDLE m_OnItemPickup;
COutputEHANDLE m_OnItemDrop;
COutputInt m_OnStateChange;
#endif
@ -2206,6 +2238,10 @@ public:
inline void ForceCrouch( void );
inline void ClearForceCrouch( void );
#ifdef MAPBASE
bool CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist = 48.0f );
#endif
protected:
virtual bool Crouch( void );
virtual bool Stand( void );
@ -2265,6 +2301,16 @@ private:
static CAI_GlobalScheduleNamespace gm_SchedulingSymbols;
static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace;
#ifdef MAPBASE
typedef struct
{
Activity sequence;
Activity gesture;
} actlink_t;
static actlink_t gm_ActivityGestureLinks[];
#endif
public:
//----------------------------------------------------
// Debugging tools
@ -2316,6 +2362,7 @@ public:
static ScriptHook_t g_Hook_TranslateSchedule;
static ScriptHook_t g_Hook_GetActualShootPosition;
static ScriptHook_t g_Hook_OverrideMove;
static ScriptHook_t g_Hook_ShouldPlayFakeSequenceGesture;
#endif
private:

View File

@ -980,11 +980,12 @@ bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, floa
// FIXME: add to goal
if (GetHintNode())
{
GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) );
#ifdef MAPBASE
if (GetHintNode()->GetIgnoreFacing() != HIF_NO)
#endif
GetHintNode()->NPCHandleStartNav( this, true );
#else
GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) );
GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() );
#endif
}
return true;
@ -1363,6 +1364,14 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
break;
case TASK_STOP_MOVING:
#ifdef MAPBASE
if ( GetNavType() == NAV_CLIMB )
{
// Don't clear the goal so that the climb can finish
DbgNavMsg( this, "Start TASK_STOP_MOVING with climb workaround\n" );
}
else
#endif
if ( ( GetNavigator()->IsGoalSet() && GetNavigator()->IsGoalActive() ) || GetNavType() == NAV_JUMP )
{
DbgNavMsg( this, "Start TASK_STOP_MOVING\n" );
@ -3015,7 +3024,16 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
case TASK_ITEM_PICKUP:
{
SetIdealActivity( ACT_PICKUP_GROUND );
#ifdef MAPBASE
if (GetTarget() && fabs( GetTarget()->WorldSpaceCenter().z - GetAbsOrigin().z ) >= 12.0f)
{
SetIdealActivity( ACT_PICKUP_RACK );
}
else
#endif
{
SetIdealActivity( ACT_PICKUP_GROUND );
}
}
break;
@ -3339,8 +3357,40 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
// a navigation while in the middle of a climb
if (GetNavType() == NAV_CLIMB)
{
#ifdef MAPBASE
if (GetActivity() != ACT_CLIMB_DISMOUNT)
{
// Try to just pause the climb, but dismount if we're in SCHED_FAIL
if (IsCurSchedule( SCHED_FAIL, false ))
{
GetMotor()->MoveClimbStop();
}
else
{
GetMotor()->MoveClimbPause();
}
TaskComplete();
}
else if (IsActivityFinished())
{
// Dismount complete.
GetMotor()->MoveClimbStop();
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (GetMotor()->MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
SetLocalOrigin( vecTeleportOrigin );
}
TaskComplete();
}
break;
#else
// wait until you reach the end
break;
#endif
}
DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" );
@ -3385,6 +3435,17 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
// If the yaw is locked, this function will not act correctly
Assert( GetMotor()->IsYawLocked() == false );
#ifdef MAPBASE
if ( GetHintNode() && GetHintNode()->OverridesNPCYaw( this ) )
{
// If the yaw is supposed to use that of a hint node, chain to TASK_FACE_HINTNODE
GetMotor()->SetIdealYaw( GetHintNode()->Yaw() );
GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
ChainRunTask( TASK_FACE_HINTNODE, pTask->flTaskData );
break;
}
#endif
Vector vecEnemyLKP = GetEnemyLKP();
if (!FInAimCone( vecEnemyLKP ))
{
@ -4271,6 +4332,15 @@ void CAI_BaseNPC::SetTurnActivity ( void )
float flYD;
flYD = GetMotor()->DeltaIdealYaw();
#ifdef MAPBASE
// Allow AddTurnGesture() to decide this
if (GetMotor()->AddTurnGesture( flYD ))
{
SetIdealActivity( ACT_IDLE );
Remember( bits_MEMORY_TURNING );
return;
}
#else
// FIXME: unknown case, update yaw should catch these
/*
if (GetMotor()->AddTurnGesture( flYD ))
@ -4280,6 +4350,7 @@ void CAI_BaseNPC::SetTurnActivity ( void )
return;
}
*/
#endif
if( flYD <= -80 && flYD >= -100 && SelectWeightedSequence( ACT_90_RIGHT ) != ACTIVITY_NOT_AVAILABLE )
{

View File

@ -420,6 +420,24 @@ bool CAI_BehaviorBase::CanUnholsterWeapon( void )
return m_pBackBridge->BackBridge_CanUnholsterWeapon();
}
//-------------------------------------
bool CAI_BehaviorBase::ShouldPickADeathPose( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_ShouldPickADeathPose();
}
//-------------------------------------
bool CAI_BehaviorBase::CanTranslateCrouchActivity( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_CanTranslateCrouchActivity();
}
#endif
//-------------------------------------

View File

@ -132,6 +132,8 @@ public:
void BridgeHandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
bool BridgeCanUnholsterWeapon( void );
bool BridgeShouldPickADeathPose( void );
bool BridgeCanTranslateCrouchActivity( void );
#endif
virtual void GatherConditions();
@ -220,6 +222,8 @@ protected:
virtual void HandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
virtual bool CanUnholsterWeapon( void );
virtual bool ShouldPickADeathPose( void );
virtual bool CanTranslateCrouchActivity( void );
#endif
virtual bool ShouldAlwaysThink();
@ -370,6 +374,9 @@ public:
#ifdef MAPBASE
// For func_tank behavior
virtual bool BackBridge_CanUnholsterWeapon( void ) = 0;
virtual bool BackBridge_ShouldPickADeathPose( void ) = 0;
virtual bool BackBridge_CanTranslateCrouchActivity( void ) = 0;
#endif
//-------------------------------------
@ -470,6 +477,8 @@ public:
void HandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
bool CanUnholsterWeapon( void );
bool ShouldPickADeathPose( void );
bool CanTranslateCrouchActivity( void );
#endif
bool ShouldAlwaysThink();
@ -534,6 +543,9 @@ private:
#ifdef MAPBASE
// For func_tank behavior
bool BackBridge_CanUnholsterWeapon( void );
bool BackBridge_ShouldPickADeathPose( void );
bool BackBridge_CanTranslateCrouchActivity( void );
#endif
CAI_BehaviorBase **AccessBehaviors();
@ -913,6 +925,20 @@ inline bool CAI_BehaviorBase::BridgeCanUnholsterWeapon( void )
{
return CanUnholsterWeapon();
}
//-----------------------------------------------------------------------------
inline bool CAI_BehaviorBase::BridgeShouldPickADeathPose( void )
{
return ShouldPickADeathPose();
}
//-----------------------------------------------------------------------------
inline bool CAI_BehaviorBase::BridgeCanTranslateCrouchActivity( void )
{
return CanTranslateCrouchActivity();
}
#endif
//-----------------------------------------------------------------------------
@ -1498,6 +1524,22 @@ inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanUnholsterWeapon( void )
{
return BaseClass::CanUnholsterWeapon();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_ShouldPickADeathPose( void )
{
return BaseClass::ShouldPickADeathPose();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanTranslateCrouchActivity( void )
{
return BaseClass::CanTranslateCrouchActivity();
}
#endif
//-------------------------------------
@ -1914,6 +1956,28 @@ inline bool CAI_BehaviorHost<BASE_NPC>::CanUnholsterWeapon( void )
return BaseClass::CanUnholsterWeapon();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::ShouldPickADeathPose( void )
{
if (m_pCurBehavior)
return m_pCurBehavior->BridgeShouldPickADeathPose();
return BaseClass::ShouldPickADeathPose();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::CanTranslateCrouchActivity( void )
{
if (m_pCurBehavior)
return m_pCurBehavior->BridgeCanTranslateCrouchActivity();
return BaseClass::CanTranslateCrouchActivity();
}
#endif
//-------------------------------------

View File

@ -408,10 +408,11 @@ bool CAI_FollowBehavior::SetFollowGoal( CAI_FollowGoal *pGoal, bool fFinishCurSc
SetFollowTarget( pGoal->GetGoalEntity() );
#ifdef MAPBASE
Assert( pGoal->m_iFormation < AIF_NUM_FORMATIONS );
SetParameters( AI_FollowParams_t( (AI_Formations_t)pGoal->m_iFormation, pGoal->m_bNormalMemoryDiscard ) );
#else
Assert( pGoal->m_iFormation == AIF_SIMPLE || pGoal->m_iFormation == AIF_WIDE || pGoal->m_iFormation == AIF_MEDIUM || pGoal->m_iFormation == AIF_SIDEKICK || pGoal->m_iFormation == AIF_VORTIGAUNT );
#endif
SetParameters( AI_FollowParams_t( (AI_Formations_t)pGoal->m_iFormation ) );
#endif
m_hFollowGoalEnt = pGoal;
m_flTimeUpdatedFollowPosition = 0;
return true;
@ -2138,6 +2139,9 @@ bool CAI_FollowBehavior::ShouldAlwaysThink()
BEGIN_DATADESC( CAI_FollowGoal )
DEFINE_KEYFIELD( m_iFormation, FIELD_INTEGER, "Formation" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_bNormalMemoryDiscard, FIELD_BOOLEAN, "NormalMemoryDiscard" ),
#endif
#ifdef HL2_EPISODIC
DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ),

View File

@ -71,6 +71,10 @@ public:
int m_iFormation;
#ifdef MAPBASE
bool m_bNormalMemoryDiscard = false;
#endif
DECLARE_DATADESC();
};

View File

@ -240,6 +240,23 @@ void CAI_StandoffBehavior::SetActive( bool fActive )
{
if ( fActive != m_fActive )
{
#ifdef MAPBASE
// These sentences are only spoken if the standoff behavior is active, so they have to be arranged separately
if ( fActive )
{
m_fActive = fActive;
NotifyChangeBehaviorStatus();
GetOuter()->SpeakSentence( STANDOFF_SENTENCE_BEGIN_STANDOFF );
}
else
{
GetOuter()->SpeakSentence( STANDOFF_SENTENCE_END_STANDOFF );
m_fActive = fActive;
NotifyChangeBehaviorStatus();
}
#else
if ( fActive )
{
GetOuter()->SpeakSentence( STANDOFF_SENTENCE_BEGIN_STANDOFF );
@ -251,6 +268,7 @@ void CAI_StandoffBehavior::SetActive( bool fActive )
m_fActive = fActive;
NotifyChangeBehaviorStatus();
#endif
}
}
@ -517,10 +535,11 @@ int CAI_StandoffBehavior::SelectScheduleCheckCover( void )
{
StandoffMsg( "Regulated to not shoot\n" );
#ifdef MAPBASE
if ( GetHintType() == HINT_TACTICAL_COVER_LOW || GetHintType() == HINT_TACTICAL_COVER_MED )
#else
if ( GetHintType() == HINT_TACTICAL_COVER_LOW )
if ( GetHintType() == HINT_TACTICAL_COVER_MED || GetCoverActivity() == ACT_COVER_MED )
SetPosture( AIP_CROUCHING_MED );
else
#endif
if ( GetHintType() == HINT_TACTICAL_COVER_LOW )
SetPosture( AIP_CROUCHING );
else
SetPosture( AIP_STANDING );
@ -539,7 +558,11 @@ int CAI_StandoffBehavior::SelectScheduleEstablishAim( void )
{
if ( HasCondition( COND_ENEMY_OCCLUDED ) )
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
if ( GetPosture() == AIP_CROUCHING || GetPosture() == AIP_CROUCHING_MED )
#else
if ( GetPosture() == AIP_CROUCHING )
#endif
{
// force a stand up, just in case
GetOuter()->SpeakSentence( STANDOFF_SENTENCE_STAND_CHECK_TARGET );
@ -650,6 +673,15 @@ Activity CAI_MappedActivityBehavior_Temporary::GetMappedActivity( AI_Posture_t p
{
if ( posture != AIP_STANDING )
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
// See UpdateTranslateActivityMap() for more information on what this is for
if ( posture == AIP_CROUCHING_MED )
{
if (activity != ACT_RANGE_ATTACK1)
posture = AIP_CROUCHING;
}
#endif
unsigned short iActivityTranslation = m_ActivityMap.Find( MAKE_ACTMAP_KEY( posture, activity ) );
if ( iActivityTranslation != m_ActivityMap.InvalidIndex() )
{
@ -667,10 +699,28 @@ Activity CAI_StandoffBehavior::NPC_TranslateActivity( Activity activity )
Activity coverActivity = GetCoverActivity();
if ( coverActivity != ACT_INVALID )
{
#ifdef MAPBASE
if ( GetPosture() == AIP_STANDING )
{
if ( coverActivity == ACT_COVER_LOW )
SetPosture( AIP_CROUCHING );
else if ( coverActivity == ACT_COVER_MED )
{
SetPosture( AIP_CROUCHING_MED );
coverActivity = ACT_COVER_LOW;
}
}
else if (coverActivity == ACT_COVER_MED)
coverActivity = ACT_COVER_LOW;
if ( activity == ACT_IDLE )
activity = coverActivity;
#else
if (activity == ACT_IDLE)
activity = coverActivity;
if ( GetPosture() == AIP_STANDING && coverActivity == ACT_COVER_LOW )
SetPosture( AIP_CROUCHING );
#endif
}
Activity result = GetMappedActivity( GetPosture(), activity );
@ -1071,12 +1121,25 @@ void CAI_StandoffBehavior::UnlockHintNode()
Activity CAI_StandoffBehavior::GetCoverActivity()
{
#ifdef MAPBASE
// This does two things:
// A. Allows medium cover nodes to be used, kind of.
// B. GetCoverActivity() already checks everything we checked here.
Activity coveract = GetOuter()->GetCoverActivity( GetHintNode() );
return coveract == ACT_IDLE ? ACT_INVALID : coveract;
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
// GetCoverActivity() already checks everything we checked here.
Activity coverActivity = GetOuter()->GetCoverActivity( GetHintNode() );
if (coverActivity == ACT_COVER_LOW)
{
// Check if this node will block our line of sight if aiming low.
Vector vHintPos, vHintForward, vHintRight;
GetHintNode()->GetPosition( GetHullType(), &vHintPos );
vHintForward = GetHintNode()->GetDirection();
GetHintNode()->GetVectors( NULL, &vHintRight, NULL );
if (GetOuter()->CouldShootIfCrouchingAt( vHintPos, vHintForward, vHintRight ))
{
coverActivity = ACT_COVER_MED;
}
}
return coverActivity == ACT_IDLE ? ACT_INVALID : coverActivity;
#else
CAI_Hint *pHintNode = GetHintNode();
if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW )
@ -1094,6 +1157,14 @@ struct AI_ActivityMapping_t
Activity activity;
const char * pszWeapon;
Activity translation;
#ifdef MAPBASE
Activity backup;
AI_ActivityMapping_t( AI_Posture_t _p, Activity _a, const char *_w, Activity _t, Activity _b = ACT_INVALID )
{
posture = _p; activity = _a; pszWeapon = _w; translation = _t; backup = _b;
}
#endif
};
void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
@ -1107,15 +1178,60 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
{ AIP_CROUCHING, ACT_WALK_AIM, NULL, ACT_WALK_CROUCH_AIM, },
{ AIP_CROUCHING, ACT_RUN_AIM, NULL, ACT_RUN_CROUCH_AIM, },
{ AIP_CROUCHING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#ifdef MAPBASE
{ AIP_CROUCHING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, },
{ AIP_CROUCHING, ACT_COVER_MED, NULL, ACT_COVER_LOW, },
#else
{ AIP_CROUCHING, ACT_RANGE_ATTACK_SMG1, NULL, ACT_RANGE_ATTACK_SMG1_LOW, },
{ AIP_CROUCHING, ACT_RANGE_ATTACK_AR2, NULL, ACT_RANGE_ATTACK_AR2_LOW, },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
//
// ============ Really long explanation that should be in a wiki/documentation article somewhere ~ Blixibon, 10/27/2021 ============
//
// Standoff behavior assumes low attack animations allow NPCs to see over barricades, with ACT_COVER_LOW being their "safely in cover" animation.
// This is why AIP_CROUCHING translates ACT_RANGE_ATTACK1 to its low animation, but translates ACT_IDLE_ANGRY to ACT_COVER_LOW instead of ACT_RANGE_AIM_LOW,
// as this would ideally allow NPCs to pop in and out of cover to shoot.
// This is also why AIP_PEEKING translates ACT_COVER_LOW to ACT_RANGE_AIM_LOW, as it's supposed to force the NPC to peek over their cover.
//
// However, this assumption mainly just applies to metrocops. Citizens' low attacking animations crouch low to the ground (which isn't effective for
// shooting over most barricades) and, while they do have a distinct ACT_COVER_LOW animation with transitions, they are close enough together that popping
// in and out of cover is redundant in most cases. Meanwhile, Combine soldiers have identical ACT_COVER_LOW and ACT_RANGE_AIM_LOW animations, which means
// they do not pop in and out of cover and AIP_PEEKING does nothing. This may be the reason why Combine soldiers occasionally get stuck in cover after a fight.
//
// -------------------------------------------------------------
//
// As part of Mapbase v7.0's NPC activity overhaul, a new "medium cover" activity set has been added. Metrocops' previous "low cover" animation set (which, as
// mentioned before, is different from that of other NPCs) has been retroactively changed to use "medium cover". This was done for a few reasons unrelated to
// standoff behavior, but the important point is that these activities indicate a new cover height. This means we can use them to give standoff behavior more leeway
// for judging which animations to use in various levels of cover.
//
// Standoff behavior can use "medium cover" animations in cover which is too high for the "low" animations, and when the medium cover animations are not available,
// it simply falls back to the "standing" animations, thus resolving the issue with other NPCs not peeking in and out of cover without requiring new medium cover
// animations.
//
// In Mapbase, this is done by changing AIP_PEEKING to use the medium cover animations and adding a new alternate crouching posture posture called "AIP_CROUCHING_MED",
// which only uses the medium cover attack activity and otherwise automatically falls back to AIP_CROUCHING. AIP_CROUCHING_MED is automatically set if the NPC cannot
// get LOS from a regular crouching position.
//
{ AIP_CROUCHING_MED, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, },
//----
{ AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_MED, },
{ AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_MED, },
{ AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY },
{ AIP_PEEKING, ACT_COVER_MED, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY },
{ AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, },
{ AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#else
//----
{ AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, },
{ AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#endif
};
m_ActivityMap.RemoveAll();
@ -1127,7 +1243,7 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 )
{
#ifdef MAPBASE
// Check backup activity
// Check NPC backup activity
if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) || HaveSequenceForActivity( GetOuter()->Weapon_BackupActivity( mappings[i].translation ) ) )
#else
if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) )
@ -1136,6 +1252,14 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() );
m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].translation );
}
#ifdef MAPBASE
// Check activity map backup activity
else if ( mappings[i].backup != ACT_INVALID && HaveSequenceForActivity( mappings[i].backup ) )
{
Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() );
m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].backup );
}
#endif
}
}
}

View File

@ -51,6 +51,9 @@ enum AI_Posture_t
AIP_INDIFFERENT,
AIP_STANDING,
AIP_CROUCHING,
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
AIP_CROUCHING_MED, // See UpdateTranslateActivityMap() for more information on what this is for
#endif
AIP_PEEKING,
};
@ -149,6 +152,14 @@ protected:
// Standoff overrides base AI crouch handling
bool IsCrouching( void ) { return false; }
#ifdef MAPBASE
// Standoff overrides base cover activity translation
bool CanTranslateCrouchActivity( void ) { return false; }
// Don't do death poses while crouching
bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); }
#endif
private:

View File

@ -1640,10 +1640,17 @@ void CAI_BlendedMotor::MaintainTurnActivity( void )
ConVar scene_flatturn( "scene_flatturn", "1" );
#ifdef MAPBASE
ConVar ai_turning_enabled( "ai_turning_enabled", "1", FCVAR_NONE, "Enables NPC turning, which was previously disabled by Valve at some point after 2004 due to a now-unknown major issue." );
#endif
bool CAI_BlendedMotor::AddTurnGesture( float flYD )
{
// some funky bug with human turn gestures, disable for now
#ifdef MAPBASE
if (!ai_turning_enabled.GetBool())
#endif
return false;
// try using a turn gesture

View File

@ -35,6 +35,10 @@ CHintCriteria::CHintCriteria( void )
m_strGroup = NULL_STRING;
m_iFlags = 0;
m_HintTypes.Purge();
#ifdef MAPBASE // From Alien Swarm SDK
m_pfnFilter = NULL;
m_pFilterContext = NULL;
#endif
}
//-----------------------------------------------------------------------------
@ -1117,10 +1121,10 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
facing = bDefaultFacing ? HIF_YES : HIF_NO;
facing = bDefaultFacing ? HIF_NO : HIF_YES;
if (facing == HIF_YES)
pNPC->GetNavigator()->SetArrivalDirection(GetDirection());
if (facing == HIF_NO)
pNPC->GetNavigator()->SetArrivalDirection( GetDirection() );
if (HintActivityName() != NULL_STRING)
{
@ -1139,6 +1143,51 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if this hint should override a NPC's yaw even during regular AI.
//-----------------------------------------------------------------------------
bool CAI_Hint::OverridesNPCYaw( CAI_BaseNPC *pNPC )
{
switch (HintType())
{
case HINT_TACTICAL_COVER_CUSTOM:
case HINT_TACTICAL_COVER_MED:
case HINT_TACTICAL_COVER_LOW:
{
if (pNPC->HasMemory( bits_MEMORY_INCOVER ))
{
// By default, don't override yaw on cover nodes unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
case HINT_PLAYER_ALLY_MOVE_AWAY_DEST:
{
Vector vHintPos;
GetPosition( pNPC, &vHintPos );
if (VectorsAreEqual( vHintPos, pNPC->GetAbsOrigin(), 0.1f ))
{
// By default, don't override yaw on move away destinations unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
}
return false;
}
#endif
//-----------------------------------------------------------------------------
@ -1258,6 +1307,30 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
return false;
}
#ifdef MAPBASE
// Test against generic filter
// (From Alien Swarm SDK)
if ( !hintCriteria.PassesFilter( this ) )
{
REPORTFAILURE( "Failed filter test" );
return false;
}
// (From Alien Swarm SDK)
int nRadius = GetRadius();
if ( nRadius != 0 )
{
// Calculate our distance
float distance = (GetAbsOrigin() - position).LengthSqr();
if ( distance > nRadius * nRadius )
{
REPORTFAILURE( "NPC is not within the node's radius." );
return false;
}
}
#endif
if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) )
{
if ( pNPC == NULL )
@ -1377,10 +1450,18 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
{
trace_t tr;
// Can my bounding box fit there?
#ifdef MAPBASE // From Alien Swarm SDK
Vector vStep( 0, 0, pNPC->StepHeight() );
AI_TraceHull ( GetAbsOrigin() + vStep, GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs() - vStep,
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 0.95 )
#else
AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(),
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 )
#endif
{
REPORTFAILURE( "Node isn't clear." );
return false;
@ -1396,6 +1477,15 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
// Calculate our distance
float distance = (GetAbsOrigin() - position).Length();
#ifdef MAPBASE
// Divide by hint weight
float flWeight = GetHintWeight();
if ( flWeight != 1.0f )
{
distance *= GetHintWeightInverse();
}
#endif
// Must be closer than the current best
if ( distance > *flNearestDistance )
{
@ -1576,6 +1666,14 @@ void CAI_Hint::OnRestore()
m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID );
FixupTargetNode();
#ifdef MAPBASE
if (m_NodeData.flWeight != 0.0f && m_NodeData.flWeight != 1.0f)
{
// Re-invert the weight
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
CAI_Node *pNode = GetNode();
if ( !pNode )
@ -1751,6 +1849,11 @@ void CC_ai_drop_hint( const CCommand &args )
nodeData.fIgnoreFacing = HIF_DEFAULT;
nodeData.minState = NPC_STATE_IDLE;
nodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
nodeData.nRadius = 0; // From Alien Swarm SDK
nodeData.flWeight = 1.0f;
nodeData.flWeightInverse = 1.0f;
#endif
CAI_Hint *pHint = CAI_HintManager::CreateHint( &nodeData, NULL );
if ( pHint )
{

View File

@ -112,6 +112,13 @@ enum Hint_e
// CS port hints
HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100,
#ifdef MAPBASE
// Mapbase hints
// (these start at a high number to avoid potential conflicts with mod hints)
HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity)
#endif
};
const char *GetHintTypeDescription( Hint_e iHintType );
const char *GetHintTypeDescription( CAI_Hint *pHint );
@ -120,6 +127,10 @@ const char *GetHintTypeDescription( CAI_Hint *pHint );
// CHintCriteria
//-----------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
typedef bool (*HintSearchFilterFunc_t)( void *pContext, CAI_Hint *pCandidate );
#endif
class CHintCriteria
{
public:
@ -134,6 +145,11 @@ public:
void SetGroup( string_t group );
string_t GetGroup( void ) const { return m_strGroup; }
#ifdef MAPBASE // From Alien Swarm SDK
void SetFilterFunc( HintSearchFilterFunc_t pfnFilter, void *pContext = NULL ) { m_pfnFilter = pfnFilter; m_pFilterContext = pContext; }
bool PassesFilter( CAI_Hint *pCandidate ) const { return (m_pfnFilter) ? (*m_pfnFilter)(m_pFilterContext, pCandidate) : true; }
#endif
int GetFirstHintType( void ) const { return m_iFirstHintType; }
int GetLastHintType( void ) const { return m_iLastHintType; }
bool MatchesHintType( int hintType ) const;
@ -176,6 +192,11 @@ private:
zoneList_t m_zoneInclude;
zoneList_t m_zoneExclude;
#ifdef MAPBASE
HintSearchFilterFunc_t m_pfnFilter;
void * m_pFilterContext;
#endif
};
class CAI_Node;
@ -310,11 +331,21 @@ public:
int GetNodeId() { return m_NodeData.nNodeID; }
int GetWCId() { return m_NodeData.nWCNodeID; }
#ifdef MAPBASE
int GetRadius() const { return m_NodeData.nRadius; } // From Alien Swarm SDK
float GetHintWeight() const { return m_NodeData.flWeight; }
float GetHintWeightInverse() const { return m_NodeData.flWeightInverse; } // Used to multiply distances
#endif
bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false );
bool IsInNodeFOV( CBaseEntity *pOther );
#ifdef MAPBASE
void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing );
// Returns true if this hint should override a NPC's yaw even during regular AI.
bool OverridesNPCYaw( CAI_BaseNPC *pNPC );
#endif
#ifdef MAPBASE_VSCRIPT

View File

@ -173,6 +173,10 @@ BEGIN_SIMPLE_DATADESC( HintNodeData )
DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ),
DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ),
DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( nRadius, FIELD_INTEGER, "radius" ), // From Alien Swarm SDK
DEFINE_KEYFIELD( flWeight, FIELD_FLOAT, "hintweight" ),
#endif
END_DATADESC()
@ -205,6 +209,17 @@ int CNodeEnt::Spawn( const char *pMapData )
m_NodeData.minState = NPC_STATE_IDLE;
if ( m_NodeData.maxState == NPC_STATE_NONE )
m_NodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
if (m_NodeData.flWeight == 0.0f)
{
m_NodeData.flWeight = 1.0f;
}
else if (m_NodeData.flWeight != 1.0f)
{
// Invert the weight so that it could be used as a direct multiplier for distances, etc.
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
// ---------------------------------------------------------------------------------
// If just a hint node (not used for navigation) just create a hint and bail
// ---------------------------------------------------------------------------------

View File

@ -42,6 +42,11 @@ struct HintNodeData
HintIgnoreFacing_t fIgnoreFacing;
NPC_STATE minState;
NPC_STATE maxState;
#ifdef MAPBASE
int nRadius; // From Alien Swarm SDK
float flWeight;
float flWeightInverse; // Not saved
#endif
int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!)

View File

@ -146,29 +146,6 @@ BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t )
// NOT SAVED nextEMemory
END_DATADESC()
#ifdef MAPBASE_VSCRIPT
#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \
DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \
DEFINE_SCRIPTFUNC( Set##name, "Set " desc )
BEGIN_SCRIPTDESC_ROOT( AI_EnemyInfo_t, "Accessor for information about an enemy." )
DEFINE_SCRIPTFUNC( Enemy, "Get the enemy." )
DEFINE_SCRIPTFUNC( SetEnemy, "Set the enemy." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastKnownLocation, "the enemy's last known location." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastSeenLocation, "the enemy's last seen location." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastSeen, "the last time the enemy was seen." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeFirstSeen, "the first time the enemy was seen." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReacquired, "the last time the enemy was reaquired." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeValidEnemy, "the time at which the enemy can be selected (reaction delay)." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReceivedDamageFrom, "the last time damage was received from this enemy." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeAtFirstHand, "the time at which the enemy was seen firsthand." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( DangerMemory, "the memory of danger position w/o enemy pointer." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( EludedMe, "whether the enemy is not at the last known location." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( Unforgettable, "whether the enemy is unforgettable." )
DEFINE_ENEMY_INFO_SCRIPTFUNCS( MobbedMe, "whether the enemy was part of a mob at some point." )
END_SCRIPTDESC();
#endif
//-----------------------------------------------------------------------------
CAI_Enemies::CAI_Enemies(void)

View File

@ -45,29 +45,6 @@ struct AI_EnemyInfo_t
bool bUnforgettable;
bool bMobbedMe; // True if enemy was part of a mob at some point
#ifdef MAPBASE_VSCRIPT
// Script functions.
#define ENEMY_INFO_SCRIPT_FUNCS(type, name, var) \
type Get##name() { return var; } \
void Set##name( type v ) { var = v; }
HSCRIPT Enemy() { return ToHScript(hEnemy); }
void SetEnemy( HSCRIPT ent ) { hEnemy = ToEnt(ent); }
ENEMY_INFO_SCRIPT_FUNCS( Vector, LastKnownLocation, vLastKnownLocation );
ENEMY_INFO_SCRIPT_FUNCS( Vector, LastSeenLocation, vLastSeenLocation );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastSeen, timeLastSeen );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeFirstSeen, timeFirstSeen );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReacquired, timeLastReacquired );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeValidEnemy, timeValidEnemy );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReceivedDamageFrom, timeLastReceivedDamageFrom );
ENEMY_INFO_SCRIPT_FUNCS( float, TimeAtFirstHand, timeAtFirstHand );
ENEMY_INFO_SCRIPT_FUNCS( bool, DangerMemory, bDangerMemory );
ENEMY_INFO_SCRIPT_FUNCS( bool, EludedMe, bEludedMe );
ENEMY_INFO_SCRIPT_FUNCS( bool, Unforgettable, bUnforgettable );
ENEMY_INFO_SCRIPT_FUNCS( bool, MobbedMe, bMobbedMe );
#endif
DECLARE_SIMPLE_DATADESC();
};

View File

@ -14,6 +14,9 @@
#include "ai_basenpc.h"
#include "ai_localnavigator.h"
#include "ai_moveprobe.h"
#ifdef MAPBASE
#include "ai_hint.h"
#endif
#include "saverestore_utlvector.h"
// memdbgon must be the last include file in a .cpp file!!!
@ -235,18 +238,47 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir
// > code are not reciprocal for all state, and furthermore, stomp
// > other state?
bool bGoingUp = (climbDir.z > 0.01);
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_BOTTOM ) )
{
SetActivity( ACT_CLIMB_MOUNT_BOTTOM );
// Steal m_vecDismount for this
GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else if ( !bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_TOP ) )
{
SetActivity( ACT_CLIMB_MOUNT_TOP );
// Steal m_vecDismount for this
GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else
#endif
if ( fabsf( climbDir.z ) < .1 )
{
SetActivity( GetNavigator()->GetMovementActivity() );
}
else
{
SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
SetActivity( bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
}
m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT );
if (m_nDismountSequence != ACT_INVALID)
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( !bGoingUp )
{
int nBottomDismount = SelectWeightedSequence( ACT_CLIMB_DISMOUNT_BOTTOM );
if (nBottomDismount != ACTIVITY_NOT_AVAILABLE)
m_nDismountSequence = nBottomDismount;
}
#endif
GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount );
}
else
@ -262,6 +294,76 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir
AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( (GetActivity() == ACT_CLIMB_MOUNT_TOP || GetActivity() == ACT_CLIMB_MOUNT_BOTTOM) )
{
if (!GetOuter()->IsActivityFinished())
{
// Wait for the mounting to finish
SetGroundEntity( NULL );
}
else
{
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
SetLocalOrigin( vecTeleportOrigin );
}
// Reset activity and start from the beginning
GetOuter()->ResetActivity();
return MoveClimbExecute( climbDest, climbDir, climbDist, yaw, climbNodesLeft );
}
}
else if ( fabsf( climbDir.z ) > .1 && (GetActivity() != ACT_CLIMB_DISMOUNT && GetActivity() != ACT_CLIMB_DISMOUNT_BOTTOM) )
{
bool bGoingUp = (climbDir.z > -0.01);
if ( GetOuter()->HaveSequenceForActivity( ACT_CLIMB_ALL ) )
{
SetActivity( ACT_CLIMB_ALL );
// TODO: Use UTIL_VecToPitch() instead if move_yaw becomes a true climb yaw and not just an up-down scalar
SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), climbDir.z < 0 ? 180.0 : -180.0 );
}
else
{
Activity desiredActivity = bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN;
if ( GetActivity() != desiredActivity )
{
SetActivity( desiredActivity );
}
}
if (m_nDismountSequence != ACT_INVALID)
{
if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z ))
{
if (bGoingUp)
{
// fixme: No other way to force m_nIdealSequence?
GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else
{
if (GetSequence() != m_nDismountSequence && GetOuter()->GetSequenceActivity( m_nDismountSequence ) == ACT_CLIMB_DISMOUNT_BOTTOM)
{
SetActivity( ACT_CLIMB_DISMOUNT_BOTTOM );
}
}
}
}
}
else if ( climbDir.Length() == 0 && GetOuter()->GetInstantaneousVelocity() <= 0.01 )
{
// The NPC is somehow stuck climbing with no direction or movement.
// This can be caused by NPCs getting stuck in each other and/or being moved away from the ladder.
// In these cases, the NPC has to be made unstuck, or else they may remain in an immobile climbing state forever.
Warning( "%s had to abort climbing due to no direction or movement\n", GetOuter()->GetDebugName() );
return AIMR_ILLEGAL;
}
#else
if ( fabsf( climbDir.z ) > .1 )
{
if ( GetActivity() != ACT_CLIMB_DISMOUNT )
@ -292,13 +394,34 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
}
}
}
#endif
float climbSpeed = GetOuter()->GetInstantaneousVelocity();
if (m_nDismountSequence != ACT_INVALID)
{
// catch situations where the climb mount/dismount finished before reaching goal
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ((GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_DISMOUNT_BOTTOM))
{
SetGroundEntity( NULL );
if (GetOuter()->IsActivityFinished())
{
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
// Just force it to complete
climbDist = 0.0f;
}
climbSpeed = 200.0f;
}
}
#else
climbSpeed = MAX( climbSpeed, 30.0 );
#endif
}
else
{
@ -314,7 +437,7 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
climbDist = 0;
const float climbTime = climbDist / climbSpeed;
SetMoveInterval( GetMoveInterval() - climbTime );
SetLocalOrigin( climbDest );
@ -330,6 +453,20 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
// --------------------------------------------
SetIdealYawAndUpdate( yaw );
#ifdef MAPBASE
// Lock the yaw if we're in position
if ( UTIL_AngleMod( yaw ) == UTIL_AngleMod( GetLocalAngles().y ) )
{
SetYawLocked( true );
}
else if ( IsYawLocked() )
{
// We're in a different position now. Unlock the yaw and update it
SetYawLocked( false );
UpdateYaw( -1 );
}
#endif
return AIMR_OK;
}
@ -340,11 +477,62 @@ void CAI_Motor::MoveClimbStop()
else
SetActivity( ACT_IDLE );
#ifdef MAPBASE
// Unlock desired weapon state so NPCs can unholster their weapons again.
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_IGNORE );
// Unlock yaw
SetYawLocked( false );
#endif
GetOuter()->RemoveFlag( FL_FLY );
SetSmoothedVelocity( vec3_origin );
SetGravity( 1.0 );
}
#ifdef MAPBASE
void CAI_Motor::MoveClimbPause()
{
if (GetActivity() != ACT_CLIMB_DISMOUNT
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
&& GetActivity() != ACT_CLIMB_MOUNT_TOP && GetActivity() != ACT_CLIMB_MOUNT_BOTTOM
#endif
)
{
if ( GetActivity() == ACT_CLIMB_ALL )
{
SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), 0.0f );
}
SetSmoothedVelocity( vec3_origin );
}
else
{
// If already dismounting, do nothing
}
}
//-----------------------------------------------------------------------------
// Purpose: This is part of a hack needed in cases where ladder mount/dismount animations collide with the world and don't move properly.
// It's based off of the same code as scripted_sequence's teleportation fixup, although this function only resolves the bone origin and
// returns whether or not teleportation is necessary, as the teleportation is achieved in different ways for different uses of this code.
//-----------------------------------------------------------------------------
bool CAI_Motor::MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin )
{
QAngle new_angle;
GetOuter()->GetBonePosition( 0, teleportOrigin, new_angle );
// Ensure that there is actually a distance needed to teleport there
if ((GetLocalOrigin() - teleportOrigin).Length2DSqr() > Square( 8.0 ))
{
teleportOrigin.z = GetLocalOrigin().z;
return true;
}
return false;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Motion for jumping
// Input :
@ -651,9 +839,20 @@ void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move )
{
// FIXME: move this up to navigator so that path goals can ignore these overrides.
Vector dir;
float flInfluence = GetFacingDirection( dir );
dir = move.facing * (1 - flInfluence) + dir * flInfluence;
VectorNormalize( dir );
#ifdef MAPBASE
if (IsDeceleratingToGoal() && (GetOuter()->GetHintNode() /*|| GetOuter()->m_hOpeningDoor*/))
{
// Don't let the facing queue interfere with arrival direction in important cases
dir = move.facing;
}
else
#endif
{
float flInfluence = GetFacingDirection( dir );
dir = move.facing * (1 - flInfluence) + dir * flInfluence;
VectorNormalize( dir );
}
// ideal facing direction
float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );

View File

@ -62,6 +62,10 @@ public:
virtual void MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw );
virtual AIMoveResult_t MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft );
virtual void MoveClimbStop();
#ifdef MAPBASE
virtual void MoveClimbPause();
virtual bool MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin );
#endif
//---------------------------------
@ -83,6 +87,9 @@ public:
const Vector & GetCurVel() const { return m_vecVelocity; }
virtual float OverrideMaxYawSpeed( Activity activity ) { return -1; }
#ifdef MAPBASE
virtual
#endif
bool IsDeceleratingToGoal() const { return false; }
//---------------------------------

View File

@ -999,12 +999,23 @@ int CAI_Navigator::GetArrivalSequence( int curSequence )
activity = ACT_IDLE;
}
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ), curSequence );
Activity translatedActivity = GetOuter()->TranslateActivity( activity );
sequence = GetOuter()->SelectWeightedSequence( translatedActivity, curSequence );
if ( sequence == ACT_INVALID )
{
DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence );
#ifdef MAPBASE
if ( translatedActivity == ACT_SCRIPT_CUSTOM_MOVE )
{
// ACT_SCRIPT_CUSTOM_MOVE allows activity translation to resolve into specific sequences
sequence = GetOuter()->GetScriptCustomMoveSequence();
}
else
#endif
{
DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence );
}
}
Assert( sequence != ACT_INVALID );
GetPath()->SetArrivalSequence( sequence );
@ -1642,6 +1653,15 @@ void CAI_Navigator::MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal )
AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() )
pMoveGoal->flags |= AILMG_TARGET_IS_TRANSITION;
#ifdef MAPBASE
// TODO: Better place for this code?
if (pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION && pCurWaypoint->GetNext()->NavType() == NAV_CLIMB)
{
// NPCs need to holster their weapons before climbing.
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED );
}
#endif
}
const Task_t *pCurTask = GetOuter()->GetTask();
@ -2164,11 +2184,26 @@ bool CAI_Navigator::OnMoveBlocked( AIMoveResult_t *pResult )
if (pDoor != NULL)
{
GetOuter()->OpenPropDoorBegin( pDoor );
#ifdef MAPBASE
// Tell the navigation to stop running until we're done.
OnNewGoal();
#endif
*pResult = AIMR_OK;
return true;
}
}
#ifdef MAPBASE
if ( GetOuter()->m_hOpeningDoor )
{
// In the process of opening a door
// Because navigation is now supposed to terminate when a NPC begins opening a door, this code should not be reached.
DbgNavMsg( GetOuter(), "CAI_Navigator::OnMoveBlocked had to check for m_hOpeningDoor\n" );
*pResult = AIMR_OK;
return true;
}
#endif
// Allow the NPC to override this behavior
if ( GetOuter()->OnMoveBlocked( pResult ))
@ -2591,8 +2626,12 @@ bool CAI_Navigator::Move( float flInterval )
if ( GetNavType() == NAV_CLIMB )
{
#ifdef MAPBASE
GetMotor()->MoveClimbPause();
#else
GetMotor()->MoveClimbStop();
SetNavType( NAV_GROUND );
#endif
}
GetMotor()->MoveStop();
AssertMsg( TaskIsRunning() || TaskIsComplete(), ("Schedule stalled!!\n") );
@ -2695,6 +2734,11 @@ void CAI_Navigator::AdvancePath()
if (pDoor != NULL)
{
GetOuter()->OpenPropDoorBegin(pDoor);
#ifdef MAPBASE
// Tell the navigation to stop running until we're done.
OnNewGoal();
#endif
}
else
{
@ -3880,7 +3924,12 @@ bool CAI_Navigator::GetStoppingPath( CAI_WaypointList * pClippedWaypoints )
AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
if ( pCurWaypoint )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
// Since regular climb nav can interrupt itself now, only do this when dismounting
bool bMustCompleteCurrent = ( (pCurWaypoint->NavType() == NAV_CLIMB && (GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_MOUNT_TOP)) || pCurWaypoint->NavType() == NAV_JUMP );
#else
bool bMustCompleteCurrent = ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP );
#endif
float distRemaining = GetMotor()->MinStoppingDist( 0 );
if ( bMustCompleteCurrent )

View File

@ -144,6 +144,31 @@ public:
int m_capabilities; // cache this
};
#ifdef MAPBASE
//-------------------------------------
// Purpose: A version of CNodeFilter which allows hints to influence the result.
//-------------------------------------
class CNodeHintFilter : public CNodeFilter
{
public:
CNodeHintFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : CNodeFilter( pNPC, pos ) {}
CNodeHintFilter( const Vector &pos ) : CNodeFilter( pos ) {}
virtual float NodeDistanceSqr( CAI_Node &node )
{
// Heavier hints are considered closer
if ( node.GetHint() && node.GetHint()->GetHintWeight() != 1.0f && (node.GetHint()->GetGroup() == NULL_STRING || node.GetHint()->GetGroup() == m_pNPC->GetHintGroup()) )
{
return CNodeFilter::NodeDistanceSqr( node ) * node.GetHint()->GetHintWeightInverse();
}
else
{
return CNodeFilter::NodeDistanceSqr( node );
}
}
};
#endif
//-----------------------------------------------------------------------------
// CAI_Network
//-----------------------------------------------------------------------------
@ -351,7 +376,12 @@ int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin,
// First get nodes distances and eliminate those that are beyond
// the maximum allowed distance for local movements
// ---------------------------------------------------------------
#ifdef MAPBASE
// Allow hint weight to influence supposed distance
CNodeHintFilter filter( pNPC, vecOrigin );
#else
CNodeFilter filter( pNPC, vecOrigin );
#endif
#ifdef AI_PERF_MON
m_nPerfStatNN++;
@ -605,8 +635,13 @@ CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw )
CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink )
{
#ifdef MAPBASE // From Alien Swarm SDK
CAI_Node *pSrcNode = GetNode( srcID );
CAI_Node *pDestNode = GetNode( destID );
#else
CAI_Node *pSrcNode = g_pBigAINet->GetNode( srcID );
CAI_Node *pDestNode = g_pBigAINet->GetNode( destID );
#endif
Assert( pSrcNode && pDestNode && pSrcNode != pDestNode );

View File

@ -71,6 +71,8 @@ CON_COMMAND( ai_debug_node_connect, "Debug the attempted connection between two
ConVar g_ai_norebuildgraph( "ai_norebuildgraph", "0" );
#ifdef MAPBASE
ConVar g_ai_norebuildgraphmessage( "ai_norebuildgraphmessage", "0", FCVAR_ARCHIVE, "Stops the \"Node graph out of date\" message from appearing when rebuilding node graph" );
ConVar g_ai_ignore_graph_timestamps( "g_ai_ignore_graph_timestamps", "1", FCVAR_NONE, "Ignores file timestamps when rebuilding nodegraphs, only relying on internal map version differences" );
#endif
@ -986,6 +988,11 @@ bool CAI_NetworkManager::IsAIFileCurrent ( const char *szMapName )
// dvd build process validates and guarantees correctness, timestamps are allowed to be wrong
return true;
}
#ifdef MAPBASE
if (g_ai_ignore_graph_timestamps.GetBool())
return true;
#endif
{
const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" );

View File

@ -411,7 +411,12 @@ int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vT
// --------------------------------------------------------
pNode->Lock( 1.0 );
#ifdef MAPBASE
if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW
|| pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_CUSTOM ) )
#else
if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) )
#endif
{
if ( GetOuter()->GetHintNode() )
{

View File

@ -32,6 +32,9 @@
#include "gib.h"
#include "CRagdollMagnet.h"
#endif
#ifdef MAPBASE_VSCRIPT
#include "mapbase/vscript_funcs_shared.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -1296,7 +1299,7 @@ bool CBaseAnimating::ScriptHookHandleAnimEvent( animevent_t *pEvent )
{
if (m_ScriptScope.IsInitialized() && g_Hook_HandleAnimEvent.CanRunInScope(m_ScriptScope))
{
HSCRIPT hEvent = g_pScriptVM->RegisterInstance( pEvent );
HSCRIPT hEvent = g_pScriptVM->RegisterInstance( reinterpret_cast<scriptanimevent_t*>(pEvent) );
// event
ScriptVariant_t args[] = { hEvent };

View File

@ -388,6 +388,8 @@ private:
void InputSetCycle( inputdata_t &inputdata );
void InputSetPlaybackRate( inputdata_t &inputdata );
public: // From Alien Swarm SDK
#endif
bool CanSkipAnimation( void );

View File

@ -61,11 +61,6 @@
extern int g_interactionBarnacleVictimReleased;
#endif //HL2_DLL
#ifdef MAPBASE
extern acttable_t *GetSMG1Acttable();
extern int GetSMG1ActtableCount();
#endif
extern ConVar weapon_showproficiency;
ConVar ai_show_hull_attacks( "ai_show_hull_attacks", "0" );
@ -1788,7 +1783,14 @@ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info )
// if flagged to drop a health kit
if (HasSpawnFlags(SF_NPC_DROP_HEALTHKIT))
{
CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles() );
CBaseEntity *pItem = CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles() );
if (pItem)
{
#ifdef MAPBASE
if (MyNPCPointer())
MyNPCPointer()->m_OnItemDrop.Set( pItem, pItem, this );
#endif
}
}
// clear the deceased's sound channels.(may have been firing or reloading when killed)
EmitSound( "BaseCombatCharacter.StopWeaponSounds" );
@ -2744,19 +2746,22 @@ Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool we
return activity;
}
acttable_t *pTable = GetSMG1Acttable();
int actCount = GetSMG1ActtableCount();
for ( int i = 0; i < actCount; i++, pTable++ )
acttable_t *pTable = pWeapon->GetBackupActivityList();
if (pTable)
{
if ( activity == pTable->baseAct )
int actCount = pWeapon->GetBackupActivityListCount();
for ( int i = 0; i < actCount; i++, pTable++ )
{
// Don't pick SMG animations we don't actually have an animation for.
if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false)
if ( activity == pTable->baseAct )
{
return activity;
}
// Don't pick SMG animations we don't actually have an animation for.
if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false)
{
return activity;
}
return (Activity)pTable->weaponAct;
return (Activity)pTable->weaponAct;
}
}
}

View File

@ -373,7 +373,12 @@ bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector
//-----------------------------------------------------------------------------
int CBaseCombatWeapon::WeaponRangeAttack1Condition( float flDot, float flDist )
{
#ifdef MAPBASE
// HACKHACK: HasPrimaryAmmo() checks the NPC's reserve ammo counts, which should not be evaluated here if we use clips
if ( UsesPrimaryAmmo() && (UsesClipsForAmmo1() ? !m_iClip1 : !HasPrimaryAmmo()) )
#else
if ( UsesPrimaryAmmo() && !HasPrimaryAmmo() )
#endif
{
return COND_NO_PRIMARY_AMMO;
}

View File

@ -106,6 +106,10 @@ ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTO
ConVar sv_script_think_interval("sv_script_think_interval", "0.1");
#ifdef MAPBASE_VSCRIPT
ConVar ent_text_allow_script( "ent_text_allow_script", "1" );
#endif
// This table encodes edict data.
void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
@ -454,6 +458,17 @@ extern bool g_bDisableEhandleAccess;
//-----------------------------------------------------------------------------
CBaseEntity::~CBaseEntity( )
{
#ifdef MAPBASE_VSCRIPT
// HACKHACK: This is needed to fix a crash when an entity removes itself with Destroy() during its own think function.
// (see https://github.com/mapbase-source/source-sdk-2013/issues/138)
FOR_EACH_VEC( m_ScriptThinkFuncs, i )
{
HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink;
if ( h ) g_pScriptVM->ReleaseScript( h );
}
m_ScriptThinkFuncs.PurgeAndDeleteElements();
#endif // MAPBASE_VSCRIPT
// FIXME: This can't be called from UpdateOnRemove! There's at least one
// case where friction sounds are added between the call to UpdateOnRemove + ~CBaseEntity
PhysCleanupFrictionSounds( this );
@ -1060,6 +1075,28 @@ int CBaseEntity::DrawDebugTextOverlays(void)
offset++;
}
#endif
#ifdef MAPBASE_VSCRIPT
// 'OnEntText' hook inspired by later source games
if (m_ScriptScope.IsInitialized() && g_Hook_OnEntText.CanRunInScope( m_ScriptScope ))
{
if (ent_text_allow_script.GetBool())
{
ScriptVariant_t functionReturn;
if ( g_Hook_OnEntText.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_CSTRING )
{
CUtlStringList outStrings;
V_SplitString( functionReturn.m_pszString, "\n", outStrings );
FOR_EACH_VEC( outStrings, i )
{
EntityText( offset, outStrings[i], 0, 224, 240, 255 );
offset++;
}
}
}
}
#endif
}
if (m_debugOverlays & OVERLAY_VIEWOFFSET)
@ -1349,10 +1386,10 @@ float CBaseEntity::GetMaxOutputDelay( const char *pszOutput )
return 0;
}
void CBaseEntity::CancelEventsByInput( const char *szInput )
{
g_EventQueue.CancelEventsByInput( this, szInput );
}
//void CBaseEntity::CancelEventsByInput( const char *szInput )
//{
// g_EventQueue.CancelEventsByInput( this, szInput );
//}
#endif // MAPBASE_VSCRIPT
CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput )
@ -2209,11 +2246,15 @@ END_DATADESC()
#ifdef MAPBASE_VSCRIPT
ScriptHook_t CBaseEntity::g_Hook_UpdateOnRemove;
ScriptHook_t CBaseEntity::g_Hook_OnEntText;
ScriptHook_t CBaseEntity::g_Hook_VPhysicsCollision;
ScriptHook_t CBaseEntity::g_Hook_FireBullets;
ScriptHook_t CBaseEntity::g_Hook_OnDeath;
ScriptHook_t CBaseEntity::g_Hook_OnKilledOther;
ScriptHook_t CBaseEntity::g_Hook_HandleInteraction;
ScriptHook_t CBaseEntity::g_Hook_ModifyEmitSoundParams;
ScriptHook_t CBaseEntity::g_Hook_ModifySentenceParams;
#endif
BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" )
@ -2348,7 +2389,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
DEFINE_SCRIPTFUNC_NAMED( ScriptAcceptInput, "AcceptInput", "" )
DEFINE_SCRIPTFUNC_NAMED( ScriptFireOutput, "FireOutput", "Fire an entity output" )
DEFINE_SCRIPTFUNC( GetMaxOutputDelay, "Get the longest delay for all events attached to an output" )
DEFINE_SCRIPTFUNC( CancelEventsByInput, "Cancel all I/O events for this entity, match input" )
//DEFINE_SCRIPTFUNC( CancelEventsByInput, "Cancel all I/O events for this entity, match input" ) // Commented out due to unpredictability and unknown risks
DEFINE_SCRIPTFUNC_NAMED( ScriptAddOutput, "AddOutput", "Add an output" )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValue, "GetKeyValue", "Get a keyvalue" )
@ -2460,6 +2501,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
// Hooks
//
DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." )
DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_OnEntText, "OnEntText", FIELD_CSTRING, "Called every frame when ent_text is enabled on the entity. Return a string to be added to the ent_text printout." )
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_VPhysicsCollision, "VPhysicsCollision", FIELD_VOID, "Called for every single VPhysics-related collision experienced by this entity." )
DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT )
@ -2489,6 +2531,14 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
//DEFINE_SCRIPTHOOK_PARAM( "data", FIELD_VARIANT )
DEFINE_SCRIPTHOOK_PARAM( "sourceEnt", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_ModifyEmitSoundParams, "ModifyEmitSoundParams", FIELD_VOID, "Called every time a sound is emitted on this entity, allowing for its parameters to be modified." )
DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CBaseEntity::g_Hook_ModifySentenceParams, "ModifySentenceParams", FIELD_VOID, "Called every time a sentence is emitted on this entity, allowing for its parameters to be modified." )
DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT )
END_SCRIPTHOOK()
#endif
END_SCRIPTDESC();
@ -2604,15 +2654,6 @@ void CBaseEntity::UpdateOnRemove( void )
g_pScriptVM->RemoveInstance( m_hScriptInstance );
m_hScriptInstance = NULL;
#ifdef MAPBASE_VSCRIPT
FOR_EACH_VEC( m_ScriptThinkFuncs, i )
{
HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink;
if ( h ) g_pScriptVM->ReleaseScript( h );
}
m_ScriptThinkFuncs.PurgeAndDeleteElements();
#endif // MAPBASE_VSCRIPT
}
}

View File

@ -610,7 +610,7 @@ public:
#ifdef MAPBASE_VSCRIPT
void ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay );
float GetMaxOutputDelay( const char *pszOutput );
void CancelEventsByInput( const char *szInput );
//void CancelEventsByInput( const char *szInput );
#endif
@ -1537,6 +1537,12 @@ public:
void GenderExpandString( char const *in, char *out, int maxlen );
virtual void ModifyEmitSoundParams( EmitSound_t &params );
#ifdef MAPBASE
// Same as above, but for sentences
// (which don't actually have EmitSound_t params)
virtual void ModifySentenceParams( int &iSentenceIndex, int &iChannel, float &flVolume, soundlevel_t &iSoundlevel, int &iFlags, int &iPitch,
const Vector **pOrigin, const Vector **pDirection, bool &bUpdatePositions, float &soundtime, int &iSpecialDSP, int &iSpeakerIndex );
#endif
static float GetSoundDuration( const char *soundname, char const *actormodel );
@ -2148,11 +2154,15 @@ public:
void ScriptSetTakeDamage( int val ) { m_takedamage = val; }
static ScriptHook_t g_Hook_UpdateOnRemove;
static ScriptHook_t g_Hook_OnEntText;
static ScriptHook_t g_Hook_VPhysicsCollision;
static ScriptHook_t g_Hook_FireBullets;
static ScriptHook_t g_Hook_OnDeath;
static ScriptHook_t g_Hook_OnKilledOther;
static ScriptHook_t g_Hook_HandleInteraction;
static ScriptHook_t g_Hook_ModifyEmitSoundParams;
static ScriptHook_t g_Hook_ModifySentenceParams;
#endif
string_t m_iszVScripts;

View File

@ -95,7 +95,11 @@ BEGIN_DATADESC( CBaseFlex )
END_DATADESC()
#ifdef MAPBASE_VSCRIPT
BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimatingOverlay, "Animated characters who have vertex flex capability." )
#else
BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." )
#endif
DEFINE_SCRIPTFUNC_NAMED( ScriptGetOldestScene, "GetCurrentScene", "Returns the instance of the oldest active scene entity (if any)." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetSceneByIndex, "GetSceneByIndex", "Returns the instance of the scene entity at the specified index." )
END_SCRIPTDESC();

View File

@ -5,9 +5,8 @@
// $NoKeywords: $
//===========================================================================//
#include <string.h>
#include "cbase.h"
#include "colorcorrection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -16,64 +15,6 @@
static const char *s_pFadeInContextThink = "ColorCorrectionFadeInThink";
static const char *s_pFadeOutContextThink = "ColorCorrectionFadeOutThink";
//------------------------------------------------------------------------------
// FIXME: This really should inherit from something more lightweight
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Purpose : Shadow control entity
//------------------------------------------------------------------------------
class CColorCorrection : public CBaseEntity
{
DECLARE_CLASS( CColorCorrection, CBaseEntity );
public:
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
CColorCorrection();
void Spawn( void );
int UpdateTransmitState();
void Activate( void );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
// Inputs
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
void InputSetFadeInDuration ( inputdata_t &inputdata );
void InputSetFadeOutDuration ( inputdata_t &inputdata );
private:
void FadeIn ( void );
void FadeOut ( void );
void FadeInThink( void ); // Fades lookup weight from Cur->MaxWeight
void FadeOutThink( void ); // Fades lookup weight from CurWeight->0.0
float m_flFadeInDuration; // Duration for a full 0->MaxWeight transition
float m_flFadeOutDuration; // Duration for a full Max->0 transition
float m_flStartFadeInWeight;
float m_flStartFadeOutWeight;
float m_flTimeStartFadeIn;
float m_flTimeStartFadeOut;
float m_flMaxWeight;
bool m_bStartDisabled;
CNetworkVar( bool, m_bEnabled );
CNetworkVar( float, m_MinFalloff );
CNetworkVar( float, m_MaxFalloff );
CNetworkVar( float, m_flCurWeight );
CNetworkString( m_netlookupFilename, MAX_PATH );
string_t m_lookupFilename;
};
LINK_ENTITY_TO_CLASS(color_correction, CColorCorrection);
@ -97,12 +38,19 @@ BEGIN_DATADESC( CColorCorrection )
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ),
DEFINE_KEYFIELD( m_bStartDisabled, FIELD_BOOLEAN, "StartDisabled" ),
#ifdef MAPBASE // From Alien Swarm SDK
DEFINE_KEYFIELD( m_bExclusive, FIELD_BOOLEAN, "exclusive" ),
#endif
// DEFINE_ARRAY( m_netlookupFilename, FIELD_CHARACTER, MAX_PATH ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeInDuration", InputSetFadeInDuration ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFadeOutDuration", InputSetFadeOutDuration ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMinFalloff", InputSetMinFalloff ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxFalloff", InputSetMaxFalloff ),
#endif
END_DATADESC()
@ -112,8 +60,18 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE(CColorCorrection, DT_ColorCorrection)
SendPropFloat( SENDINFO(m_MinFalloff) ),
SendPropFloat( SENDINFO(m_MaxFalloff) ),
SendPropFloat( SENDINFO(m_flCurWeight) ),
#ifdef MAPBASE // From Alien Swarm SDK
SendPropFloat( SENDINFO(m_flMaxWeight) ),
SendPropFloat( SENDINFO(m_flFadeInDuration) ),
SendPropFloat( SENDINFO(m_flFadeOutDuration) ),
#endif
SendPropString( SENDINFO(m_netlookupFilename) ),
SendPropBool( SENDINFO(m_bEnabled) ),
#ifdef MAPBASE // From Alien Swarm SDK
SendPropBool( SENDINFO(m_bMaster) ),
SendPropBool( SENDINFO(m_bClientSide) ),
SendPropBool( SENDINFO(m_bExclusive) ),
#endif
END_SEND_TABLE()
@ -132,6 +90,11 @@ CColorCorrection::CColorCorrection() : BaseClass()
m_flTimeStartFadeOut = 0.0f;
m_netlookupFilename.GetForModify()[0] = 0;
m_lookupFilename = NULL_STRING;
#ifdef MAPBASE // From Alien Swarm SDK
m_bMaster = false;
m_bClientSide = false;
m_bExclusive = false;
#endif
}
@ -175,6 +138,11 @@ void CColorCorrection::Activate( void )
{
BaseClass::Activate();
#ifdef MAPBASE // From Alien Swarm SDK (moved to Activate() for save/restore support)
m_bMaster = IsMaster();
m_bClientSide = IsClientSide();
#endif
Q_strncpy( m_netlookupFilename.GetForModify(), STRING( m_lookupFilename ), MAX_PATH );
}
@ -183,6 +151,11 @@ void CColorCorrection::Activate( void )
//-----------------------------------------------------------------------------
void CColorCorrection::FadeIn ( void )
{
#ifdef MAPBASE // From Alien Swarm SDK
if ( m_bClientSide || ( m_bEnabled && m_flCurWeight >= m_flMaxWeight ) )
return;
#endif
m_bEnabled = true;
m_flTimeStartFadeIn = gpGlobals->curtime;
m_flStartFadeInWeight = m_flCurWeight;
@ -194,6 +167,11 @@ void CColorCorrection::FadeIn ( void )
//-----------------------------------------------------------------------------
void CColorCorrection::FadeOut ( void )
{
#ifdef MAPBASE // From Alien Swarm SDK
if ( m_bClientSide || ( !m_bEnabled && m_flCurWeight <= 0.0f ) )
return;
#endif
m_bEnabled = false;
m_flTimeStartFadeOut = gpGlobals->curtime;
m_flStartFadeOutWeight = m_flCurWeight;
@ -230,7 +208,11 @@ void CColorCorrection::FadeInThink( void )
flFadeRatio = clamp ( flFadeRatio, 0.0f, 1.0f );
m_flStartFadeInWeight = clamp ( m_flStartFadeInWeight, 0.0f, 1.0f );
#ifdef MAPBASE
m_flCurWeight = Lerp( flFadeRatio, m_flStartFadeInWeight, m_flMaxWeight.Get() );
#else
m_flCurWeight = Lerp( flFadeRatio, m_flStartFadeInWeight, m_flMaxWeight );
#endif
SetNextThink( gpGlobals->curtime + COLOR_CORRECTION_ENT_THINK_RATE, s_pFadeInContextThink );
}
@ -312,3 +294,94 @@ void CColorCorrection::InputSetFadeOutDuration( inputdata_t& inputdata )
{
m_flFadeOutDuration = inputdata.value.Float();
}
#ifdef MAPBASE
void CColorCorrection::InputSetMinFalloff( inputdata_t& inputdata )
{
m_MinFalloff = inputdata.value.Float();
}
void CColorCorrection::InputSetMaxFalloff( inputdata_t& inputdata )
{
m_MaxFalloff = inputdata.value.Float();
}
#endif
#ifdef MAPBASE // From Alien Swarm SDK
CColorCorrectionSystem s_ColorCorrectionSystem( "ColorCorrectionSystem" );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CColorCorrectionSystem *ColorCorrectionSystem( void )
{
return &s_ColorCorrectionSystem;
}
//-----------------------------------------------------------------------------
// Purpose: Clear out the fog controller.
//-----------------------------------------------------------------------------
void CColorCorrectionSystem::LevelInitPreEntity( void )
{
m_hMasterController = NULL;
ListenForGameEvent( "round_start" );
}
//-----------------------------------------------------------------------------
// Purpose: Find the master controller. If no controller is
// set as Master, use the first controller found.
//-----------------------------------------------------------------------------
void CColorCorrectionSystem::InitMasterController( void )
{
CColorCorrection *pColorCorrection = NULL;
do
{
pColorCorrection = static_cast<CColorCorrection*>( gEntList.FindEntityByClassname( pColorCorrection, "color_correction" ) );
if ( pColorCorrection )
{
if ( m_hMasterController.Get() == NULL )
{
m_hMasterController = pColorCorrection;
}
else
{
if ( pColorCorrection->IsMaster() )
{
m_hMasterController = pColorCorrection;
}
}
}
} while ( pColorCorrection );
}
//-----------------------------------------------------------------------------
// Purpose: On a multiplayer map restart, re-find the master controller.
//-----------------------------------------------------------------------------
void CColorCorrectionSystem::FireGameEvent( IGameEvent *pEvent )
{
InitMasterController();
}
//-----------------------------------------------------------------------------
// Purpose: On level load find the master fog controller. If no controller is
// set as Master, use the first fog controller found.
//-----------------------------------------------------------------------------
void CColorCorrectionSystem::LevelInitPostEntity( void )
{
InitMasterController();
// HACK: Singleplayer games don't get a call to CBasePlayer::Spawn on level transitions.
// CBasePlayer::Activate is called before this is called so that's too soon to set up the fog controller.
// We don't have a hook similar to Activate that happens after LevelInitPostEntity
// is called, or we could just do this in the player itself.
if ( gpGlobals->maxClients == 1 )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( pPlayer && ( pPlayer->m_hColorCorrectionCtrl.Get() == NULL ) )
{
pPlayer->InitColorCorrectionController();
}
}
}
#endif

View File

@ -0,0 +1,147 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013.
// Although technically a new Mapbase file, it only serves to move otherwise identical code,
// so most code and repo conventions will pretend it was always there.
//
// --------------------------------------------------------------------
//
// Purpose: Color correction entity.
//
//=============================================================================//
#ifndef COLOR_CORRECTION_H
#define COLOR_CORRECTION_H
#ifdef _WIN32
#pragma once
#endif
#include <string.h>
#include "cbase.h"
#ifdef MAPBASE // From Alien Swarm SDK
#include "GameEventListener.h"
// Spawn Flags
#define SF_COLORCORRECTION_MASTER 0x0001
#define SF_COLORCORRECTION_CLIENTSIDE 0x0002
#endif
//------------------------------------------------------------------------------
// FIXME: This really should inherit from something more lightweight
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Purpose : Shadow control entity
//------------------------------------------------------------------------------
class CColorCorrection : public CBaseEntity
{
DECLARE_CLASS( CColorCorrection, CBaseEntity );
public:
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
CColorCorrection();
void Spawn( void );
int UpdateTransmitState();
void Activate( void );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
#ifdef MAPBASE // From Alien Swarm SDK
bool IsMaster( void ) const { return HasSpawnFlags( SF_COLORCORRECTION_MASTER ); }
bool IsClientSide( void ) const { return HasSpawnFlags( SF_COLORCORRECTION_CLIENTSIDE ); }
bool IsExclusive( void ) const { return m_bExclusive; }
#endif
// Inputs
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
void InputSetFadeInDuration ( inputdata_t &inputdata );
void InputSetFadeOutDuration ( inputdata_t &inputdata );
#ifdef MAPBASE
void InputSetMinFalloff( inputdata_t &inputdata );
void InputSetMaxFalloff( inputdata_t &inputdata );
#endif
private:
void FadeIn ( void );
void FadeOut ( void );
void FadeInThink( void ); // Fades lookup weight from Cur->MaxWeight
void FadeOutThink( void ); // Fades lookup weight from CurWeight->0.0
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkVar( float, m_flFadeInDuration ); // Duration for a full 0->MaxWeight transition
CNetworkVar( float, m_flFadeOutDuration ); // Duration for a full Max->0 transition
#else
float m_flFadeInDuration; // Duration for a full 0->MaxWeight transition
float m_flFadeOutDuration; // Duration for a full Max->0 transition
#endif
float m_flStartFadeInWeight;
float m_flStartFadeOutWeight;
float m_flTimeStartFadeIn;
float m_flTimeStartFadeOut;
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkVar( float, m_flMaxWeight );
#else
float m_flMaxWeight;
#endif
bool m_bStartDisabled;
CNetworkVar( bool, m_bEnabled );
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkVar( bool, m_bMaster );
CNetworkVar( bool, m_bClientSide );
CNetworkVar( bool, m_bExclusive );
#endif
CNetworkVar( float, m_MinFalloff );
CNetworkVar( float, m_MaxFalloff );
CNetworkVar( float, m_flCurWeight );
CNetworkString( m_netlookupFilename, MAX_PATH );
string_t m_lookupFilename;
};
#ifdef MAPBASE // From Alien Swarm SDK
//=============================================================================
//
// ColorCorrection Controller System. Just a place to store a master controller
//
class CColorCorrectionSystem : public CAutoGameSystem, public CGameEventListener
{
public:
// Creation/Init.
CColorCorrectionSystem( char const *name ) : CAutoGameSystem( name )
{
m_hMasterController = NULL;
}
~CColorCorrectionSystem()
{
m_hMasterController = NULL;
}
virtual void LevelInitPreEntity();
virtual void LevelInitPostEntity();
virtual void FireGameEvent( IGameEvent *pEvent );
CColorCorrection *GetMasterColorCorrection( void ) { return m_hMasterController; }
private:
void InitMasterController( void );
CHandle< CColorCorrection > m_hMasterController;
};
CColorCorrectionSystem *ColorCorrectionSystem( void );
#endif
#endif // COLOR_CORRECTION_H

View File

@ -48,7 +48,11 @@ public:
private:
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkVar( bool, m_bEnabled );
#else
bool m_bEnabled;
#endif
bool m_bStartDisabled;
CNetworkVar( float, m_Weight );
@ -61,7 +65,11 @@ private:
float m_LastExitWeight;
float m_LastExitTime;
#ifdef MAPBASE // From Alien Swarm SDK
CNetworkVar( float, m_FadeDuration );
#else
float m_FadeDuration;
#endif
};
LINK_ENTITY_TO_CLASS(color_correction_volume, CColorCorrectionVolume);
@ -90,6 +98,11 @@ END_DATADESC()
IMPLEMENT_SERVERCLASS_ST_NOBASE(CColorCorrectionVolume, DT_ColorCorrectionVolume)
#ifdef MAPBASE // From Alien Swarm SDK
SendPropBool( SENDINFO(m_bEnabled) ),
SendPropFloat( SENDINFO(m_MaxWeight) ),
SendPropFloat( SENDINFO(m_FadeDuration) ),
#endif
SendPropFloat( SENDINFO(m_Weight) ),
SendPropString( SENDINFO(m_lookupFilename) ),
END_SEND_TABLE()

View File

@ -39,6 +39,9 @@ protected:
CNetworkVar( float, m_flStartTime );
CNetworkVar( int, m_iDesiredOverlay );
CNetworkVar( bool, m_bIsActive );
#ifdef MAPBASE
CNetworkVar( int, m_iOverlayIndex );
#endif
};
LINK_ENTITY_TO_CLASS( env_screenoverlay, CEnvScreenOverlay );
@ -74,6 +77,9 @@ BEGIN_DATADESC( CEnvScreenOverlay )
DEFINE_FIELD( m_iDesiredOverlay, FIELD_INTEGER ),
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_bIsActive, FIELD_BOOLEAN ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_iOverlayIndex, FIELD_INTEGER, "OverlayIndex" ),
#endif
DEFINE_INPUTFUNC( FIELD_VOID, "StartOverlays", InputStartOverlay ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopOverlays", InputStopOverlay ),
@ -93,6 +99,9 @@ IMPLEMENT_SERVERCLASS_ST( CEnvScreenOverlay, DT_EnvScreenOverlay )
SendPropFloat( SENDINFO( m_flStartTime ), 32, SPROP_NOSCALE ),
SendPropInt( SENDINFO( m_iDesiredOverlay ), 5 ),
SendPropBool( SENDINFO( m_bIsActive ) ),
#ifdef MAPBASE
SendPropInt( SENDINFO( m_iOverlayIndex ), 5 ),
#endif
END_SEND_TABLE()
//-----------------------------------------------------------------------------
@ -103,6 +112,9 @@ CEnvScreenOverlay::CEnvScreenOverlay( void )
m_flStartTime = 0;
m_iDesiredOverlay = 0;
m_bIsActive = false;
#ifdef MAPBASE
m_iOverlayIndex = -1;
#endif
}
//-----------------------------------------------------------------------------

View File

@ -5,6 +5,7 @@
//=============================================================================
#include "cbase.h"
#include "env_tonemap_controller.h"
#include "baseentity.h"
#include "entityoutput.h"
#include "convar.h"
@ -16,50 +17,6 @@
ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT, "The HDR tonemap scale. 1 = Use autoexposure, 0 = eyes fully closed, 16 = eyes wide open." );
// 0 - eyes fully closed / fully black
// 1 - nominal
// 16 - eyes wide open / fully white
//-----------------------------------------------------------------------------
// Purpose: Entity that controls player's tonemap
//-----------------------------------------------------------------------------
class CEnvTonemapController : public CPointEntity
{
DECLARE_CLASS( CEnvTonemapController, CPointEntity );
public:
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
void Spawn( void );
int UpdateTransmitState( void );
void UpdateTonemapScaleBlend( void );
// Inputs
void InputSetTonemapScale( inputdata_t &inputdata );
void InputBlendTonemapScale( inputdata_t &inputdata );
void InputSetTonemapRate( inputdata_t &inputdata );
void InputSetAutoExposureMin( inputdata_t &inputdata );
void InputSetAutoExposureMax( inputdata_t &inputdata );
void InputUseDefaultAutoExposure( inputdata_t &inputdata );
void InputSetBloomScale( inputdata_t &inputdata );
void InputUseDefaultBloomScale( inputdata_t &inputdata );
void InputSetBloomScaleRange( inputdata_t &inputdata );
private:
float m_flBlendTonemapStart; // HDR Tonemap at the start of the blend
float m_flBlendTonemapEnd; // Target HDR Tonemap at the end of the blend
float m_flBlendEndTime; // Time at which the blend ends
float m_flBlendStartTime; // Time at which the blend started
CNetworkVar( bool, m_bUseCustomAutoExposureMin );
CNetworkVar( bool, m_bUseCustomAutoExposureMax );
CNetworkVar( bool, m_bUseCustomBloomScale );
CNetworkVar( float, m_flCustomAutoExposureMin );
CNetworkVar( float, m_flCustomAutoExposureMax );
CNetworkVar( float, m_flCustomBloomScale);
CNetworkVar( float, m_flCustomBloomScaleMinimum);
};
LINK_ENTITY_TO_CLASS( env_tonemap_controller, CEnvTonemapController );
BEGIN_DATADESC( CEnvTonemapController )
@ -120,27 +77,72 @@ int CEnvTonemapController::UpdateTransmitState()
return SetTransmitState( FL_EDICT_ALWAYS );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEnvTonemapController::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "TonemapScale" ) )
{
float flTonemapScale = atof( szValue );
if (flTonemapScale != -1.0f)
{
mat_hdr_tonemapscale.SetValue( flTonemapScale );
}
}
else if (FStrEq( szKeyName, "TonemapRate" ))
{
float flTonemapRate = atof( szValue );
if (flTonemapRate != -1.0f)
{
ConVarRef mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate" );
if ( mat_hdr_manual_tonemap_rate.IsValid() )
{
mat_hdr_manual_tonemap_rate.SetValue( flTonemapRate );
}
}
}
else if (FStrEq( szKeyName, "AutoExposureMin" ))
{
float flAutoExposureMin = atof( szValue );
if (flAutoExposureMin != 1.0f)
{
m_flCustomAutoExposureMin = flAutoExposureMin;
m_bUseCustomAutoExposureMin = true;
}
}
else if (FStrEq( szKeyName, "AutoExposureMax" ))
{
float flAutoExposureMax = atof( szValue );
if (flAutoExposureMax != 1.0f)
{
m_flCustomAutoExposureMax = flAutoExposureMax;
m_bUseCustomAutoExposureMax = true;
}
}
else if (FStrEq( szKeyName, "BloomScale" ))
{
float flBloomScale = atof( szValue );
if (flBloomScale != 1.0f)
{
m_flCustomBloomScale = flBloomScale;
m_flCustomBloomScaleMinimum = flBloomScale;
m_bUseCustomBloomScale = true;
}
}
else
return BaseClass::KeyValue( szKeyName, szValue );
return true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Set the tonemap scale to the specified value
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputSetTonemapScale( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputSetTonemapScale\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputSetTonemapScale( inputdata );
return;
}
}
}
float flRemapped = inputdata.value.Float();
mat_hdr_tonemapscale.SetValue( flRemapped );
}
@ -150,10 +152,6 @@ void CEnvTonemapController::InputSetTonemapScale( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputBlendTonemapScale( inputdata_t &inputdata )
{
//Tony; TODO!!! -- tonemap scale blending does _not_ work properly in multiplayer..
if ( ( gpGlobals->maxClients > 1 ) )
return;
char parseString[255];
Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
@ -207,22 +205,6 @@ void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputSetTonemapRate( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputSetTonemapRate\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputSetTonemapRate( inputdata );
return;
}
}
}
// TODO: There should be a better way to do this.
ConVarRef mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate" );
if ( mat_hdr_manual_tonemap_rate.IsValid() )
@ -254,22 +236,6 @@ void CEnvTonemapController::UpdateTonemapScaleBlend( void )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputSetAutoExposureMin( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputSetAutoExposureMin\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputSetAutoExposureMin( inputdata );
return;
}
}
}
m_flCustomAutoExposureMin = inputdata.value.Float();
m_bUseCustomAutoExposureMin = true;
}
@ -279,22 +245,6 @@ void CEnvTonemapController::InputSetAutoExposureMin( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputSetAutoExposureMax( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputSetAutoExposureMax\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputSetAutoExposureMax( inputdata );
return;
}
}
}
m_flCustomAutoExposureMax = inputdata.value.Float();
m_bUseCustomAutoExposureMax = true;
}
@ -304,22 +254,6 @@ void CEnvTonemapController::InputSetAutoExposureMax( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputUseDefaultAutoExposure( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputUseDefaultAutoExposure\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputUseDefaultAutoExposure( inputdata );
return;
}
}
}
m_bUseCustomAutoExposureMin = false;
m_bUseCustomAutoExposureMax = false;
}
@ -329,22 +263,6 @@ void CEnvTonemapController::InputUseDefaultAutoExposure( inputdata_t &inputdata
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputSetBloomScale( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputSetBloomScale\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputSetBloomScale( inputdata );
return;
}
}
}
m_flCustomBloomScale = inputdata.value.Float();
m_flCustomBloomScaleMinimum = m_flCustomBloomScale;
m_bUseCustomBloomScale = true;
@ -355,21 +273,111 @@ void CEnvTonemapController::InputSetBloomScale( inputdata_t &inputdata )
//-----------------------------------------------------------------------------
void CEnvTonemapController::InputUseDefaultBloomScale( inputdata_t &inputdata )
{
//Tony; in multiplayer, we check to see if the activator is a player, if they are, we trigger an input on them, and then get out.
//if there is no activator, or the activator is not a player; ie: LogicAuto, we set the 'global' values.
if ( ( gpGlobals->maxClients > 1 ) )
{
if ( inputdata.pActivator != NULL && inputdata.pActivator->IsPlayer() )
{
// DevMsg("activator is a player: InputUseDefaultBloomScale\n");
CBasePlayer *pPlayer = ToBasePlayer(inputdata.pActivator);
if (pPlayer)
{
pPlayer->InputUseDefaultBloomScale( inputdata );
return;
}
}
}
m_bUseCustomBloomScale = false;
}
#ifdef MAPBASE // From Alien Swarm SDK
//--------------------------------------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( trigger_tonemap, CTonemapTrigger );
BEGIN_DATADESC( CTonemapTrigger )
DEFINE_KEYFIELD( m_tonemapControllerName, FIELD_STRING, "TonemapName" ),
END_DATADESC()
//--------------------------------------------------------------------------------------------------------
void CTonemapTrigger::Spawn( void )
{
AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
BaseClass::Spawn();
InitTrigger();
m_hTonemapController = gEntList.FindEntityByName( NULL, m_tonemapControllerName );
}
//--------------------------------------------------------------------------------------------------------
void CTonemapTrigger::StartTouch( CBaseEntity *other )
{
if ( !PassesTriggerFilters( other ) )
return;
BaseClass::StartTouch( other );
CBasePlayer *player = ToBasePlayer( other );
if ( !player )
return;
player->OnTonemapTriggerStartTouch( this );
}
//--------------------------------------------------------------------------------------------------------
void CTonemapTrigger::EndTouch( CBaseEntity *other )
{
if ( !PassesTriggerFilters( other ) )
return;
BaseClass::EndTouch( other );
CBasePlayer *player = ToBasePlayer( other );
if ( !player )
return;
player->OnTonemapTriggerEndTouch( this );
}
//-----------------------------------------------------------------------------
// Purpose: Clear out the tonemap controller.
//-----------------------------------------------------------------------------
void CTonemapSystem::LevelInitPreEntity( void )
{
m_hMasterController = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: On level load find the master fog controller. If no controller is
// set as Master, use the first fog controller found.
//-----------------------------------------------------------------------------
void CTonemapSystem::LevelInitPostEntity( void )
{
// Overall master controller
CEnvTonemapController *pTonemapController = NULL;
do
{
pTonemapController = static_cast<CEnvTonemapController*>( gEntList.FindEntityByClassname( pTonemapController, "env_tonemap_controller" ) );
if ( pTonemapController )
{
if ( m_hMasterController == NULL )
{
m_hMasterController = pTonemapController;
}
else
{
if ( pTonemapController->IsMaster() )
{
m_hMasterController = pTonemapController;
}
}
}
} while ( pTonemapController );
}
//--------------------------------------------------------------------------------------------------------
CTonemapSystem s_TonemapSystem( "TonemapSystem" );
//--------------------------------------------------------------------------------------------------------
CTonemapSystem *TheTonemapSystem( void )
{
return &s_TonemapSystem;
}
//--------------------------------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,140 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013.
// Although technically a new Mapbase file, it only serves to move otherwise identical code,
// so most code and repo conventions will pretend it was always there.
//
// --------------------------------------------------------------------
//
// Purpose:
//
//=============================================================================//
#ifndef ENV_TONEMAP_CONTROLLER_H
#define ENV_TONEMAP_CONTROLLER_H
#include "triggers.h"
// 0 - eyes fully closed / fully black
// 1 - nominal
// 16 - eyes wide open / fully white
#ifdef MAPBASE // From Alien Swarm SDK
// Spawn Flags
#define SF_TONEMAP_MASTER 0x0001
#endif
//-----------------------------------------------------------------------------
// Purpose: Entity that controls player's tonemap
//-----------------------------------------------------------------------------
class CEnvTonemapController : public CPointEntity
{
DECLARE_CLASS( CEnvTonemapController, CPointEntity );
public:
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
void Spawn( void );
int UpdateTransmitState( void );
void UpdateTonemapScaleBlend( void );
#ifdef MAPBASE
bool IsMaster( void ) const { return HasSpawnFlags( SF_TONEMAP_MASTER ); } // From Alien Swarm SDK
bool KeyValue( const char *szKeyName, const char *szValue );
#endif
// Inputs
void InputSetTonemapScale( inputdata_t &inputdata );
void InputBlendTonemapScale( inputdata_t &inputdata );
void InputSetTonemapRate( inputdata_t &inputdata );
void InputSetAutoExposureMin( inputdata_t &inputdata );
void InputSetAutoExposureMax( inputdata_t &inputdata );
void InputUseDefaultAutoExposure( inputdata_t &inputdata );
void InputSetBloomScale( inputdata_t &inputdata );
void InputUseDefaultBloomScale( inputdata_t &inputdata );
void InputSetBloomScaleRange( inputdata_t &inputdata );
private:
float m_flBlendTonemapStart; // HDR Tonemap at the start of the blend
float m_flBlendTonemapEnd; // Target HDR Tonemap at the end of the blend
float m_flBlendEndTime; // Time at which the blend ends
float m_flBlendStartTime; // Time at which the blend started
#ifdef MAPBASE // From Alien Swarm SDK
public:
#endif
CNetworkVar( bool, m_bUseCustomAutoExposureMin );
CNetworkVar( bool, m_bUseCustomAutoExposureMax );
CNetworkVar( bool, m_bUseCustomBloomScale );
CNetworkVar( float, m_flCustomAutoExposureMin );
CNetworkVar( float, m_flCustomAutoExposureMax );
CNetworkVar( float, m_flCustomBloomScale);
CNetworkVar( float, m_flCustomBloomScaleMinimum);
};
#ifdef MAPBASE // From Alien Swarm SDK
//--------------------------------------------------------------------------------------------------------
class CTonemapTrigger : public CBaseTrigger
{
public:
DECLARE_CLASS( CTonemapTrigger, CBaseTrigger );
DECLARE_DATADESC();
virtual void Spawn( void );
virtual void StartTouch( CBaseEntity *other );
virtual void EndTouch( CBaseEntity *other );
CBaseEntity *GetTonemapController( void ) const;
private:
string_t m_tonemapControllerName;
EHANDLE m_hTonemapController;
};
//--------------------------------------------------------------------------------------------------------
inline CBaseEntity *CTonemapTrigger::GetTonemapController( void ) const
{
return m_hTonemapController.Get();
}
//--------------------------------------------------------------------------------------------------------
// Tonemap Controller System.
class CTonemapSystem : public CAutoGameSystem
{
public:
// Creation/Init.
CTonemapSystem( char const *name ) : CAutoGameSystem( name )
{
m_hMasterController = NULL;
}
~CTonemapSystem()
{
m_hMasterController = NULL;
}
virtual void LevelInitPreEntity();
virtual void LevelInitPostEntity();
CBaseEntity *GetMasterTonemapController( void ) const;
private:
EHANDLE m_hMasterController;
};
//--------------------------------------------------------------------------------------------------------
inline CBaseEntity *CTonemapSystem::GetMasterTonemapController( void ) const
{
return m_hMasterController.Get();
}
//--------------------------------------------------------------------------------------------------------
CTonemapSystem *TheTonemapSystem( void );
#endif
#endif //ENV_TONEMAP_CONTROLLER_H

View File

@ -0,0 +1,153 @@
//--------------------------------------------------------------------------------------------------------
// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved
#include "cbase.h"
#include "fogvolume.h"
#include "collisionutils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CUtlVector< CFogVolume * > TheFogVolumes;
ConVar fog_volume_debug( "fog_volume_debug", "0", 0, "If enabled, prints diagnostic information about the current fog volume" );
//--------------------------------------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS(fog_volume, CFogVolume);
BEGIN_DATADESC( CFogVolume )
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_KEYFIELD( m_fogName, FIELD_STRING, "FogName" ),
DEFINE_KEYFIELD( m_postProcessName, FIELD_STRING, "PostProcessName" ),
DEFINE_KEYFIELD( m_colorCorrectionName, FIELD_STRING, "ColorCorrectionName" ),
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_FIELD( m_hFogController, FIELD_EHANDLE ),
DEFINE_FIELD( m_hPostProcessController, FIELD_EHANDLE ),
DEFINE_FIELD( m_hColorCorrectionController, FIELD_EHANDLE ),
END_DATADESC()
//--------------------------------------------------------------------------------------------------------
CFogVolume *CFogVolume::FindFogVolumeForPosition( const Vector &position )
{
CFogVolume *fogVolume = NULL;
for ( int i=0; i<TheFogVolumes.Count(); ++i )
{
fogVolume = TheFogVolumes[i];
Vector vecRelativeCenter;
fogVolume->CollisionProp()->WorldToCollisionSpace( position, &vecRelativeCenter );
if ( IsBoxIntersectingSphere( fogVolume->CollisionProp()->OBBMins(), fogVolume->CollisionProp()->OBBMaxs(), vecRelativeCenter, 1.0f ) )
{
break;
}
fogVolume = NULL;
}
// This doesn't work well if there are multiple players or multiple fog volume queries per frame; might want to relocate this if that's the case
if ( fog_volume_debug.GetBool() )
{
if ( fogVolume )
{
char fogVolumeName[256];
fogVolume->GetKeyValue( "targetname", fogVolumeName, 256 );
engine->Con_NPrintf( 0, "Fog Volume ""%s"" found at position (%f %f %f)", fogVolumeName, position.x, position.y, position.z );
engine->Con_NPrintf( 1, "Fog: %s, post process: %s, color correct: %s", fogVolume->m_fogName, fogVolume->m_postProcessName, fogVolume->m_colorCorrectionName );
}
else
{
engine->Con_NPrintf( 0, "No Fog Volume found at given position (%f %f %f)", position.x, position.y, position.z );
}
}
return fogVolume;
}
//--------------------------------------------------------------------------------------------------------
CFogVolume::CFogVolume() :
BaseClass(),
m_bDisabled( false ),
m_bInFogVolumesList( false )
{
}
//--------------------------------------------------------------------------------------------------------
CFogVolume::~CFogVolume()
{
RemoveFromGlobalList();
}
//--------------------------------------------------------------------------------------------------------
void CFogVolume::Spawn( void )
{
BaseClass::Spawn();
SetSolid( SOLID_BSP );
SetSolidFlags( FSOLID_NOT_SOLID );
SetModel( STRING( GetModelName() ) );
}
//--------------------------------------------------------------------------------------------------------
void CFogVolume::AddToGlobalList()
{
if ( !m_bInFogVolumesList )
{
TheFogVolumes.AddToTail( this );
m_bInFogVolumesList = true;
}
}
//--------------------------------------------------------------------------------------------------------
void CFogVolume::RemoveFromGlobalList()
{
if ( m_bInFogVolumesList )
{
TheFogVolumes.FindAndRemove( this );
m_bInFogVolumesList = false;
}
}
//----------------------------------------------------------------------------
void CFogVolume::InputEnable( inputdata_t &data )
{
m_bDisabled = false;
AddToGlobalList();
}
//----------------------------------------------------------------------------
void CFogVolume::InputDisable( inputdata_t &data )
{
m_bDisabled = true;
RemoveFromGlobalList();
}
//----------------------------------------------------------------------------
// Called when the level loads or is restored
//----------------------------------------------------------------------------
void CFogVolume::Activate()
{
BaseClass::Activate();
m_hFogController = dynamic_cast< CFogController* >( gEntList.FindEntityByName( NULL, m_fogName ) );
m_hPostProcessController = dynamic_cast< CPostProcessController* >( gEntList.FindEntityByName( NULL, m_postProcessName ) );
m_hColorCorrectionController = dynamic_cast< CColorCorrection* >( gEntList.FindEntityByName( NULL, m_colorCorrectionName ) );
if ( !m_bDisabled )
{
AddToGlobalList();
}
}

View File

@ -0,0 +1,74 @@
//--------------------------------------------------------------------------------------------------------
// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved
#ifndef FOG_VOLUME_H
#define FOG_VOLUME_H
#ifdef _WIN32
#pragma once
#endif
class CFogController;
class CPostProcessController;
class CColorCorrection;
//--------------------------------------------------------------------------------------------------------
// Fog volume entity
class CFogVolume : public CServerOnlyEntity
{
DECLARE_CLASS( CFogVolume, CServerOnlyEntity );
DECLARE_DATADESC();
public:
CFogVolume();
virtual ~CFogVolume();
virtual void Spawn( void );
virtual void Activate();
static CFogVolume *FindFogVolumeForPosition( const Vector &position );
const char *GetFogControllerName() const
{
return STRING( m_fogName );
}
CFogController* GetFogController( ) const
{
return m_hFogController.Get();
}
CPostProcessController* GetPostProcessController( ) const
{
return m_hPostProcessController.Get();
}
CColorCorrection* GetColorCorrectionController( ) const
{
return m_hColorCorrectionController.Get();
}
void InputEnable( inputdata_t &data );
void InputDisable( inputdata_t &data );
private:
string_t m_fogName;
string_t m_postProcessName;
string_t m_colorCorrectionName;
CHandle< CFogController > m_hFogController;
CHandle< CPostProcessController > m_hPostProcessController;
CHandle< CColorCorrection > m_hColorCorrectionController;
bool m_bDisabled;
bool m_bInFogVolumesList;
void AddToGlobalList();
void RemoveFromGlobalList();
};
extern CUtlVector< CFogVolume * > TheFogVolumes;
#endif // FOG_VOLUME_H

View File

@ -221,6 +221,10 @@ bool CBreakable::KeyValue( const char *szKeyName, const char *szValue )
if ( object > 0 && object < ARRAYSIZE(pSpawnObjects) )
m_iszSpawnObject = MAKE_STRING( pSpawnObjects[object] );
#ifdef MAPBASE
// "0" is the default value of a "choices" field in Hammer, representing nothing selected
// atoi() returning 0 may also indicate a failed conversion, so check szValue directly
else if ( FStrEq( szValue, "0" ) )
m_iszSpawnObject = NULL_STRING;
else
m_iszSpawnObject = AllocPooledString(szValue);
#endif

View File

@ -110,6 +110,28 @@ bool CAI_FuncTankBehavior::IsInterruptable( void )
return BaseClass::IsInterruptable();
}
ConVar ai_tank_allow_expanded_npcs( "ai_tank_allow_expanded_npcs", "1", FCVAR_NONE, "Allows Father Grigori, Barney, and vortigaunts to automatically man func_tanks." );
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_FuncTankBehavior::CanManTank( CFuncTank *pTank, bool bForced )
{
if (!bForced)
{
// In order to prevent potential problems in existing maps, Father Grigori, Barney, and vortigaunts can be set to not automatically man func_tanks by default.
if (ai_tank_allow_expanded_npcs.GetBool() == false)
{
const char *pszClass = GetOuter()->GetClassname();
if ( FStrEq( pszClass, "npc_monk" ) || FStrEq( pszClass, "npc_barney" ) || FStrEq( pszClass, "npc_vortigaunt" ) )
return false;
}
}
return true;
}
#endif
//-----------------------------------------------------------------------------

View File

@ -51,6 +51,8 @@ public:
void PrescheduleThink();
#ifdef MAPBASE
bool IsInterruptable( void );
bool CanManTank( CFuncTank *pTank, bool bForced );
#endif
Activity NPC_TranslateActivity( Activity activity );

View File

@ -267,6 +267,10 @@ private:
typedef CHandle<CAvoidSphere> AvoidSphereHandle_t;
float m_flRadius;
#ifdef MAPBASE
string_t m_iszAvoidFilter;
EHANDLE m_hAvoidFilter;
#endif
static CUtlVector< AvoidSphereHandle_t > s_AvoidSpheres;
};

View File

@ -402,7 +402,11 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
#ifdef MAPBASE
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
#else
if ( pNPC->GetBehavior( &pBehavior ) )
#endif
{
m_hController = pNPC;
pBehavior->SetFuncTank( this );
@ -439,7 +443,7 @@ void CFuncTank::InputTeleportNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
if ( pNPC->GetBehavior( &pBehavior ) )
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
{
Vector vecVec;
QAngle angAng;
@ -512,7 +516,7 @@ void CFuncTank::InputForceNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
if ( pNPC->GetBehavior( &pBehavior ) )
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
{
// Set the forced condition
pBehavior->SetCondition( CAI_FuncTankBehavior::COND_FUNCTANK_FORCED );
@ -627,7 +631,11 @@ void CFuncTank::NPC_FindController( void )
continue;
CAI_FuncTankBehavior *pBehavior;
#ifdef MAPBASE
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, false ) )
#else
if ( pNPC->GetBehavior( &pBehavior ) )
#endif
{
// Don't mount the func_tank if your "enemy" is within X feet or it or the npc.
CBaseEntity *pEnemy = pNPC->GetEnemy();

View File

@ -142,7 +142,7 @@ void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
{
// In SP respawns, only create corpse if drawing externally
CBasePlayer *pPlayer = (CBasePlayer*)pEdict;
if ( fCopyCorpse && pPlayer->m_bDrawPlayerModelExternally )
if ( fCopyCorpse && pPlayer->GetDrawPlayerModelExternally() )
{
// make a copy of the dead body for appearances sake
pPlayer->CreateCorpse();

View File

@ -259,6 +259,7 @@ public:
void InputSetHandModel( inputdata_t &inputdata );
void InputSetHandModelSkin( inputdata_t &inputdata );
void InputSetHandModelBodyGroup( inputdata_t &inputdata );
void InputSetPlayerModel( inputdata_t &inputdata );
void InputSetPlayerDrawExternally( inputdata_t &inputdata );
@ -4617,6 +4618,7 @@ BEGIN_DATADESC( CLogicPlayerProxy )
DEFINE_INPUTFUNC( FIELD_STRING, "GetAmmoOnWeapon", InputGetAmmoOnWeapon ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetHandModel", InputSetHandModel ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelSkin", InputSetHandModelSkin ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHandModelBodyGroup", InputSetHandModelBodyGroup ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerModel", InputSetPlayerModel ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawExternally", InputSetPlayerDrawExternally ),
DEFINE_INPUT( m_MaxArmor, FIELD_INTEGER, "SetMaxInputArmor" ),
@ -4659,6 +4661,8 @@ bool CLogicPlayerProxy::KeyValue( const char *szKeyName, const char *szValue )
vm->SetModel(szValue);
else if (FStrEq(szKeyName, "Skin")) // HandsVMSkin
vm->m_nSkin = atoi(szValue);
else if (FStrEq(szKeyName, "Body")) // HandsVMBody
vm->m_nBody = atoi(szValue);
}
return true;
}
@ -5062,6 +5066,17 @@ void CLogicPlayerProxy::InputSetHandModelSkin( inputdata_t &inputdata )
vm->m_nSkin = inputdata.value.Int();
}
void CLogicPlayerProxy::InputSetHandModelBodyGroup( inputdata_t &inputdata )
{
if (!m_hPlayer)
return;
CBasePlayer *pPlayer = static_cast<CBasePlayer*>( m_hPlayer.Get() );
CBaseViewModel *vm = pPlayer->GetViewModel(1);
if (vm)
vm->m_nBody = inputdata.value.Int();
}
void CLogicPlayerProxy::InputSetPlayerModel( inputdata_t &inputdata )
{
if (!m_hPlayer)
@ -5089,6 +5104,6 @@ void CLogicPlayerProxy::InputSetPlayerDrawExternally( inputdata_t &inputdata )
return;
CBasePlayer *pPlayer = static_cast<CBasePlayer*>(m_hPlayer.Get());
pPlayer->m_bDrawPlayerModelExternally = inputdata.value.Bool();
pPlayer->SetDrawPlayerModelExternally( inputdata.value.Bool() );
}
#endif

View File

@ -156,6 +156,8 @@ ConVar npc_alyx_crouch( "npc_alyx_crouch", "1" );
#ifdef MAPBASE
ConVar npc_alyx_interact_manhacks( "npc_alyx_interact_manhacks", "1" );
ConVar npc_alyx_interact_turrets( "npc_alyx_interact_turrets", "0" );
ConVar npc_alyx_allow_fly( "npc_alyx_allow_fly", "0", FCVAR_NONE, "Allows Alyx to use FL_FLY outside of scripted sequences, actbusy, or navigation." );
#endif
// global pointer to Alyx for fast lookups
@ -319,7 +321,9 @@ CNPC_Alyx *CNPC_Alyx::GetAlyx( void )
//=========================================================
bool CNPC_Alyx::CreateBehaviors()
{
#ifndef MAPBASE // Moved to CNPC_PlayerCompanion
AddBehavior( &m_FuncTankBehavior );
#endif
bool result = BaseClass::CreateBehaviors();
return result;
@ -885,7 +889,11 @@ void CNPC_Alyx::GatherConditions()
// ROBIN: This was here to solve a problem in a playtest. We've since found what we think was the cause.
// It's a useful piece of debug to have lying there, so I've left it in.
if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() )
if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled()
#ifdef MAPBASE
&& GetNavType() != NAV_CLIMB && !npc_alyx_allow_fly.GetBool()
#endif
)
{
Warning( "Removed FL_FLY from Alyx, who wasn't running a script or actbusy. Time %.2f, map %s.\n", gpGlobals->curtime, STRING(gpGlobals->mapname) );
RemoveFlag( FL_FLY );
@ -1687,9 +1695,88 @@ Activity CNPC_Alyx::NPC_TranslateActivity( Activity activity )
case ACT_DROP_WEAPON: if ( HasShotgun() ) return (Activity)ACT_DROP_WEAPON_SHOTGUN;
}
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CNPC_Alyx::Weapon_TranslateActivity( Activity activity, bool *pRequired )
{
activity = BaseClass::Weapon_TranslateActivity( activity, pRequired );
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CNPC_Alyx::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon )
{
activity = BaseClass::Weapon_BackupActivity( activity, weaponTranslationWasRequired, pSpecificWeapon );
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
#endif
bool CNPC_Alyx::ShouldDeferToFollowBehavior()
{
return BaseClass::ShouldDeferToFollowBehavior();

View File

@ -91,6 +91,10 @@ public:
bool CanSeeEntityInDarkness( CBaseEntity *pEntity );
bool IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition );
Activity NPC_TranslateActivity ( Activity activity );
#ifdef MAPBASE
Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL );
Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, CBaseCombatWeapon *pSpecificWeapon = NULL );
#endif
bool ShouldDeferToFollowBehavior();
void BuildScheduleTestBits();
bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior );
@ -231,7 +235,9 @@ private:
bool m_bShouldHaveEMP;
#ifndef MAPBASE // Moved to CNPC_PlayerCompanion
CAI_FuncTankBehavior m_FuncTankBehavior;
#endif
COutputEvent m_OnFinishInteractWithObject;
COutputEvent m_OnPlayerUse;

View File

@ -42,6 +42,10 @@
#include "physics_bone_follower.h"
#endif // HL2_EPISODIC
#ifdef MAPBASE
#include "filters.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -5680,6 +5684,9 @@ LINK_ENTITY_TO_CLASS( npc_heli_avoidsphere, CAvoidSphere );
BEGIN_DATADESC( CAvoidSphere )
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_iszAvoidFilter, FIELD_STRING, "AvoidFilter" ),
#endif
END_DATADESC()
@ -5720,6 +5727,18 @@ void CAvoidSphere::Activate( )
{
BaseClass::Activate();
s_AvoidSpheres.AddToTail( this );
#ifdef MAPBASE
m_hAvoidFilter = gEntList.FindEntityByName( NULL, m_iszAvoidFilter, this );
if (m_hAvoidFilter)
{
if (dynamic_cast<CBaseFilter*>(m_hAvoidFilter.Get()) == NULL)
{
Warning( "%s: \"%s\" is not a valid filter", GetDebugName(), m_hAvoidFilter->GetDebugName() );
m_hAvoidFilter = NULL;
}
}
#endif
}
void CAvoidSphere::UpdateOnRemove( )
@ -5746,6 +5765,12 @@ void CAvoidSphere::ComputeAvoidanceForces( CBaseEntity *pEntity, float flEntityR
CAvoidSphere *pSphere = s_AvoidSpheres[i].Get();
const Vector &vecAvoidCenter = pSphere->WorldSpaceCenter();
#ifdef MAPBASE
// Continue if not passing the avoid sphere filter
if ( pSphere->m_hAvoidFilter && !(static_cast<CBaseFilter*>( pSphere->m_hAvoidFilter.Get())->PassesFilter(pSphere, pEntity )) )
continue;
#endif
// NOTE: This test can be thought of sweeping a sphere through space
// and seeing if it intersects the avoidance sphere
float flTotalRadius = flEntityRadius + pSphere->m_flRadius;

View File

@ -689,6 +689,12 @@ bool CNPC_Barnacle::CanPickup( CBaseCombatCharacter *pBCC )
if( FClassnameIs( pBCC, "npc_turret_floor" ) )
return false;
#ifdef MAPBASE
// Don't pickup rollermines
if( FClassnameIs( pBCC, "npc_rollermine" ) )
return false;
#endif
// Don't pick up a dead player or NPC
if( !pBCC->IsAlive() )
return false;

View File

@ -51,8 +51,10 @@ public:
virtual void Precache()
{
#ifndef MAPBASE // This is now done in CNPC_PlayerCompanion::Precache()
// Prevents a warning
SelectModel( );
#endif
BaseClass::Precache();
PrecacheScriptSound( "NPC_Barney.FootstepLeft" );

View File

@ -1278,6 +1278,14 @@ void CNPC_BaseScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarg
myZAccel = flDist / flInterval;
}
#ifdef MAPBASE
if (m_flSpeedModifier != 1.0f)
{
myAccel *= m_flSpeedModifier;
//myZAccel *= m_flSpeedModifier;
}
#endif
MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay );
// calc relative banking targets

View File

@ -407,6 +407,27 @@ BEGIN_DATADESC( CNPC_Citizen )
END_DATADESC()
#ifdef MAPBASE_VSCRIPT
ScriptHook_t CNPC_Citizen::g_Hook_SelectModel;
BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" )
DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this citizen is a medic." )
DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." )
DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." )
DEFINE_SCRIPTFUNC( GetCitizenType, "Gets the citizen's type. 1 = Downtrodden, 2 = Refugee, 3 = Rebel, 4 = Unique" )
DEFINE_SCRIPTFUNC( SetCitizenType, "Sets the citizen's type. 1 = Downtrodden, 2 = Refugee, 3 = Rebel, 4 = Unique" )
BEGIN_SCRIPTHOOK( CNPC_Citizen::g_Hook_SelectModel, "SelectModel", FIELD_CSTRING, "Called when a citizen is selecting a random model. 'model_path' is the directory of the selected model and 'model_head' is the name. The 'gender' parameter uses the 'GENDER_' constants and is based only on the citizen's random head spawnflags. If a full model path string is returned, it will be used as the model instead." )
DEFINE_SCRIPTHOOK_PARAM( "model_path", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "model_head", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "gender", FIELD_INTEGER )
END_SCRIPTHOOK()
END_SCRIPTDESC();
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@ -418,10 +439,11 @@ CSimpleSimTimer CNPC_Citizen::gm_PlayerSquadEvaluateTimer;
bool CNPC_Citizen::CreateBehaviors()
{
BaseClass::CreateBehaviors();
AddBehavior( &m_FuncTankBehavior );
#ifdef MAPBASE
AddBehavior( &m_RappelBehavior );
AddBehavior( &m_PolicingBehavior );
#else // Moved to CNPC_PlayerCompanion
AddBehavior( &m_FuncTankBehavior );
#endif
return true;
@ -431,7 +453,12 @@ bool CNPC_Citizen::CreateBehaviors()
//-----------------------------------------------------------------------------
void CNPC_Citizen::Precache()
{
#ifdef MAPBASE
// CNPC_PlayerCompanion::Precache() is responsible for calling this now
BaseClass::Precache();
#else
SelectModel();
#endif
SelectExpressionType();
if ( !npc_citizen_dont_precache_all.GetBool() )
@ -468,7 +495,9 @@ void CNPC_Citizen::Precache()
}
}
#ifndef MAPBASE // See above
BaseClass::Precache();
#endif
}
//-----------------------------------------------------------------------------
@ -769,6 +798,54 @@ void CNPC_Citizen::SelectModel()
pszModelName = g_ppszRandomHeads[m_iHead];
SetModelName(NULL_STRING);
}
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_SelectModel.CanRunInScope( m_ScriptScope ))
{
gender_t scriptGender;
switch (gender)
{
case 'm':
scriptGender = GENDER_MALE;
break;
case 'f':
scriptGender = GENDER_FEMALE;
break;
default:
scriptGender = GENDER_NONE;
break;
}
const char *pszModelPath = CFmtStr( "models/Humans/%s/", (const char *)(CFmtStr( g_ppszModelLocs[m_Type], (IsMedic()) ? "m" : "" )) );
// model_path, model_head, gender
ScriptVariant_t args[] = { pszModelPath, pszModelName, (int)scriptGender };
ScriptVariant_t returnValue = NULL;
g_Hook_SelectModel.Call( m_ScriptScope, &returnValue, args );
if (returnValue.m_type == FIELD_CSTRING && returnValue.m_pszString[0] != '\0')
{
// Refresh the head if it's different
const char *pszNewHead = strrchr( returnValue.m_pszString, '/' );
if ( pszNewHead && Q_stricmp(pszNewHead+1, pszModelName) != 0 )
{
pszNewHead++;
for ( int i = 0; i < ARRAYSIZE(g_ppszRandomHeads); i++ )
{
if ( Q_stricmp( g_ppszRandomHeads[i], pszModelName ) == 0 )
{
m_iHead = i;
break;
}
}
}
// Just set the model right here
SetModelName( AllocPooledString( returnValue.m_pszString ) );
return;
}
}
#endif
}
Assert( pszModelName || GetModelName() != NULL_STRING );
@ -1466,7 +1543,11 @@ int CNPC_Citizen::SelectScheduleRetrieveItem()
// Been kicked out of the player squad since the time I located the health.
ClearCondition( COND_HEALTH_ITEM_AVAILABLE );
}
#ifdef MAPBASE
else if ( m_FollowBehavior.GetFollowTarget() )
#else
else
#endif
{
CBaseEntity *pBase = FindHealthItem(m_FollowBehavior.GetFollowTarget()->GetAbsOrigin(), Vector( 120, 120, 120 ) );
CItem *pItem = dynamic_cast<CItem *>(pBase);
@ -1932,12 +2013,7 @@ void CNPC_Citizen::RunTask( const Task_t *pTask )
return;
}
// Add imprecision to avoid obvious robotic perfection stationary targets
#ifdef MAPBASE
// More imprecision with low-accuracy citizens
float imprecision = 18*sin(gpGlobals->curtime) + cosh(GetCurrentWeaponProficiency() - 4);
#else
float imprecision = 18*sin(gpGlobals->curtime);
#endif
vecLaserPos.x += imprecision;
vecLaserPos.y += imprecision;
vecLaserPos.z += imprecision;
@ -2014,6 +2090,13 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity )
return ACT_RUN_AIM_AR2_STIMULATED;
if (activity == ACT_WALK_AIM_AR2)
return ACT_WALK_AIM_AR2_STIMULATED;
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
if (activity == ACT_RUN_AIM_PISTOL)
return ACT_RUN_AIM_PISTOL_STIMULATED;
if (activity == ACT_WALK_AIM_PISTOL)
return ACT_WALK_AIM_PISTOL_STIMULATED;
#endif
}
#endif
@ -2278,25 +2361,21 @@ bool CNPC_Citizen::IsManhackMeleeCombatant()
//-----------------------------------------------------------------------------
Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin )
{
#ifdef MAPBASE
// The code below is probably broken. If not, it definitely isn't very effective.
return BaseClass::GetActualShootPosition( shootOrigin );
#else
Vector vecTarget = BaseClass::GetActualShootPosition( shootOrigin );
#ifdef MAPBASE
// If we're firing an RPG at a gunship, aim off to it's side, because we'll auger towards it.
// The gunship RPG code does not appear to be funcitonal, so only set the laser position.
if ( GetActiveWeapon() && EntIsClass(GetActiveWeapon(), gm_isz_class_RPG) && GetEnemy() )
{
CWeaponRPG *pRPG = static_cast<CWeaponRPG*>(GetActiveWeapon());
if ( EntIsClass( GetEnemy(), gm_isz_class_Gunship ) )
pRPG->SetNPCLaserPosition( vecTarget );
}
#else
CWeaponRPG *pRPG = dynamic_cast<CWeaponRPG*>(GetActiveWeapon());
// If we're firing an RPG at a gunship, aim off to it's side, because we'll auger towards it.
if ( pRPG && GetEnemy() )
{
if ( FClassnameIs( GetEnemy(), "npc_combinegunship" ) )
#endif
{
Vector vecRight;
GetVectors( NULL, &vecRight, NULL );
@ -2331,11 +2410,10 @@ Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin )
{
pRPG->SetNPCLaserPosition( vecTarget );
}
}
#endif
return vecTarget;
#endif
}
//-----------------------------------------------------------------------------

View File

@ -277,6 +277,11 @@ public:
virtual void OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior );
#ifdef MAPBASE
int GetCitizenType() { return (int)m_Type; }
void SetCitizenType( int iType ) { m_Type = (CitizenType_t)iType; }
#endif
private:
//-----------------------------------------------------
// Conditions, Schedules, Tasks
@ -365,7 +370,6 @@ private:
#endif
//-----------------------------------------------------
CAI_FuncTankBehavior m_FuncTankBehavior;
#ifdef MAPBASE
CAI_RappelBehavior m_RappelBehavior;
CAI_PolicingBehavior m_PolicingBehavior;
@ -373,6 +377,8 @@ private:
// Rappel
virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); }
void BeginRappel() { m_RappelBehavior.BeginRappel(); }
#else // Moved to CNPC_PlayerCompanion
CAI_FuncTankBehavior m_FuncTankBehavior;
#endif
CHandle<CAI_FollowGoal> m_hSavedFollowGoalEnt;
@ -382,6 +388,10 @@ private:
//-----------------------------------------------------
#ifdef MAPBASE_VSCRIPT
static ScriptHook_t g_Hook_SelectModel;
DECLARE_ENT_SCRIPTDESC();
#endif
DECLARE_DATADESC();
#ifdef _XBOX
protected:

View File

@ -43,6 +43,7 @@ int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared wh
#ifdef MAPBASE
ConVar npc_combine_idle_walk_easy( "npc_combine_idle_walk_easy", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use ACT_WALK_EASY as a walking animation when idle." );
ConVar npc_combine_unarmed_anims( "npc_combine_unarmed_anims", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use unarmed idle/walk animations when they have no weapon." );
ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use \"protected run\" animations." );
ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." );
ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." );
@ -127,8 +128,9 @@ Activity ACT_COMBINE_AR2_ALTFIRE;
Activity ACT_WALK_EASY;
Activity ACT_WALK_MARCH;
#ifdef MAPBASE
Activity ACT_IDLE_UNARMED;
Activity ACT_WALK_UNARMED;
Activity ACT_TURRET_CARRY_IDLE;
Activity ACT_TURRET_CARRY_WALK;
Activity ACT_TURRET_CARRY_RUN;
#endif
// -----------------------------------------------
@ -230,7 +232,9 @@ DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ),
DEFINE_AIGRENADE_DATADESC()
#endif
#ifndef MAPBASE
DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ),
#endif
DEFINE_FIELD( m_fIsElite, FIELD_BOOLEAN ),
#ifndef MAPBASE
DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ),
@ -542,7 +546,12 @@ void CNPC_Combine::GatherConditions()
if( GetState() == NPC_STATE_COMBAT )
{
#ifdef MAPBASE
// Don't override the standoff
if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) && !m_StandoffBehavior.IsActive() )
#else
if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) )
#endif
{
// Soldiers that are standing around doing nothing poll for attack slots so
// that they can respond quickly when one comes available. If they can
@ -746,7 +755,8 @@ Class_T CNPC_Combine::Classify ( void )
//-----------------------------------------------------------------------------
bool CNPC_Combine::IsAltFireCapable( void )
{
return IsElite() || m_bAlternateCapable;
// The base class tells us if we're carrying an alt-fire-able weapon.
return (IsElite() || m_bAlternateCapable) && BaseClass::IsAltFireCapable();
}
//-----------------------------------------------------------------------------
@ -1538,11 +1548,6 @@ void CNPC_Combine::BuildScheduleTestBits( void )
//-----------------------------------------------------------------------------
Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pRequired )
{
// We have differing low animations and ACT_CROUCHIDLE is not friendly to weapon translation.
// ACT_CROUCHIDLE is pretty much deprecated at this point anyway.
if (eNewActivity == ACT_CROUCHIDLE)
eNewActivity = ACT_RANGE_AIM_LOW;
return BaseClass::Weapon_TranslateActivity(eNewActivity, pRequired);
}
@ -1551,12 +1556,6 @@ Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pR
//-----------------------------------------------------------------------------
Activity CNPC_Combine::NPC_BackupActivity( Activity eNewActivity )
{
// Otherwise we move around, T-posing.
if (eNewActivity == ACT_WALK)
return ACT_WALK_UNARMED;
else if (eNewActivity == ACT_RUN)
return ACT_RUN_RIFLE;
// Some models might not contain ACT_COMBINE_BUGBAIT, which the soldier model uses instead of ACT_IDLE_ON_FIRE.
// Contrariwise, soldiers may be called to use ACT_IDLE_ON_FIRE in other parts of the AI and need to translate to ACT_COMBINE_BUGBAIT.
if (eNewActivity == ACT_COMBINE_BUGBAIT)
@ -1628,16 +1627,32 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity )
}
}
#ifdef MAPBASE
else if (!GetActiveWeapon() && npc_combine_unarmed_anims.GetBool() && HaveSequenceForActivity(ACT_IDLE_UNARMED))
else if (!GetActiveWeapon() && !npc_combine_unarmed_anims.GetBool())
{
if (eNewActivity == ACT_IDLE || eNewActivity == ACT_IDLE_ANGRY)
eNewActivity = ACT_IDLE_UNARMED;
eNewActivity = ACT_IDLE_SMG1;
else if (eNewActivity == ACT_WALK)
eNewActivity = ACT_WALK_UNARMED;
eNewActivity = ACT_WALK_RIFLE;
else if (eNewActivity == ACT_RUN)
eNewActivity = ACT_RUN_RIFLE;
}
else if (eNewActivity == ACT_WALK && m_NPCState == NPC_STATE_IDLE && npc_combine_idle_walk_easy.GetBool() && HaveSequenceForActivity(ACT_WALK_EASY))
else if (m_NPCState == NPC_STATE_IDLE && eNewActivity == ACT_WALK)
{
eNewActivity = ACT_WALK_EASY;
if (npc_combine_idle_walk_easy.GetBool())
{
// ACT_WALK_EASY has been replaced with ACT_WALK_RELAXED for weapon translation purposes
eNewActivity = ACT_WALK_RELAXED;
}
else if (GetActiveWeapon())
{
eNewActivity = ACT_WALK_RIFLE;
}
}
if ( eNewActivity == ACT_RUN && ( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) || IsCurSchedule( SCHED_FLEE_FROM_BEST_SOUND ) ) )
{
if ( random->RandomInt( 0, 1 ) && npc_combine_protected_run.GetBool() && HaveSequenceForActivity( ACT_RUN_PROTECTED ) )
eNewActivity = ACT_RUN_PROTECTED;
}
#endif
@ -2022,7 +2037,7 @@ int CNPC_Combine::SelectSchedule( void )
Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter();
#ifdef MAPBASE
// I switched this to IsAltFireCapable() before, but m_bAlternateCapable makes it necessary to use IsElite() again.
// This was switched to IsAltFireCapable() before, but m_bAlternateCapable makes it necessary to use IsElite() again.
#endif
if ( IsElite() )
{
@ -2604,7 +2619,6 @@ int CNPC_Combine::TranslateSchedule( int scheduleType )
#ifdef MAPBASE
// SCHED_COMBINE_WAIT_IN_COVER uses INCOVER, but only gets out of it when the soldier moves.
// That seems to mess up shooting, so this Forget() attempts to fix that.
// I don't know if there's a better workaround.
Forget( bits_MEMORY_INCOVER );
#endif
@ -3687,7 +3701,9 @@ void CNPC_Combine::SetActivity( Activity NewActivity )
{
BaseClass::SetActivity( NewActivity );
#ifndef MAPBASE // CAI_GrenadeUser
m_iLastAnimEventHandled = -1;
#endif
}
//-----------------------------------------------------------------------------
@ -3787,6 +3803,13 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea
{
return WEAPON_PROFICIENCY_GOOD;
}
#ifdef MAPBASE
else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) )
{
// Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer.
return WEAPON_PROFICIENCY_VERY_GOOD;
}
#endif
return BaseClass::CalcWeaponProficiency( pWeapon );
}
@ -3902,7 +3925,12 @@ bool CNPC_Combine::IsRunningApproachEnemySchedule()
bool CNPC_Combine::ShouldPickADeathPose( void )
{
#ifdef MAPBASE
// Check base class as well
return !IsCrouching() && BaseClass::ShouldPickADeathPose();
#else
return !IsCrouching();
#endif
}
//-----------------------------------------------------------------------------
@ -3936,8 +3964,9 @@ DECLARE_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE )
DECLARE_ACTIVITY( ACT_WALK_EASY )
DECLARE_ACTIVITY( ACT_WALK_MARCH )
#ifdef MAPBASE
DECLARE_ACTIVITY( ACT_IDLE_UNARMED )
DECLARE_ACTIVITY( ACT_WALK_UNARMED )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_IDLE )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_WALK )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_RUN )
#endif
DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE )

View File

@ -59,7 +59,7 @@ public:
// Create components
virtual bool CreateComponents();
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
bool CanThrowGrenade( const Vector &vecTarget );
bool CheckCanThrowGrenade( const Vector &vecTarget );
#endif
@ -118,7 +118,7 @@ public:
const char* GetGrenadeAttachment() { return "lefthand"; }
#else
#endif
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
void DelayAltFireAttack( float flDelay );
void DelaySquadAltFireAttack( float flDelay );
#endif
@ -131,7 +131,7 @@ public:
Vector EyeOffset( Activity nActivity );
Vector EyePosition( void );
Vector BodyTarget( const Vector &posSrc, bool bNoisy = true );
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector GetAltFireTarget();
#endif
@ -320,7 +320,7 @@ private:
private:
int m_nKickDamage;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector m_vecTossVelocity;
EHANDLE m_hForcedGrenadeTarget;
#else
@ -334,12 +334,12 @@ private:
// Time Variables
float m_flNextPainSoundTime;
float m_flNextAlertSoundTime;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
float m_flNextGrenadeCheck;
#endif
float m_flNextLostSoundTime;
float m_flAlertPatrolTime; // When to stop doing alert patrol
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
float m_flNextAltFireTime; // Elites only. Next time to begin considering alt-fire attack.
#endif
@ -351,7 +351,7 @@ private:
CAI_Sentence< CNPC_Combine > m_Sentences;
#endif
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
int m_iNumGrenades;
#endif
CAI_AssaultBehavior m_AssaultBehavior;
@ -365,9 +365,11 @@ private:
#endif
public:
#ifndef MAPBASE // CAI_GrenadeUser
int m_iLastAnimEventHandled;
#endif
bool m_fIsElite;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector m_vecAltFireTarget;
#endif

View File

@ -1296,7 +1296,11 @@ void CFastZombie::StartTask( const Task_t *pTask )
CBaseEntity *pEnemy = GetEnemy();
Vector vecJumpDir;
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN )
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
|| GetActivity() == ACT_CLIMB_ALL
#endif
)
{
// Jump off the pipe backwards!
Vector forward;
@ -1449,7 +1453,11 @@ int CFastZombie::TranslateSchedule( int scheduleType )
break;
case SCHED_FASTZOMBIE_UNSTICK_JUMP:
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT )
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
|| (GetActivity() >= ACT_CLIMB_ALL && GetActivity() <= ACT_CLIMB_DISMOUNT_BOTTOM)
#endif
)
{
return SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP;
}
@ -1477,8 +1485,10 @@ int CFastZombie::TranslateSchedule( int scheduleType )
//---------------------------------------------------------
Activity CFastZombie::NPC_TranslateActivity( Activity baseAct )
{
#ifndef MAPBASE // Now covered by CAI_BaseNPC::NPC_BackupActivity
if ( baseAct == ACT_CLIMB_DOWN )
return ACT_CLIMB_UP;
#endif
return BaseClass::NPC_TranslateActivity( baseAct );
}

View File

@ -64,8 +64,10 @@ public:
virtual void Precache()
{
#ifndef MAPBASE // This is now done in CNPC_PlayerCompanion::Precache()
// Prevents a warning
SelectModel( );
SelectModel();
#endif
BaseClass::Precache();
PrecacheScriptSound( "NPC_Fisherman.FootstepLeft" );

View File

@ -261,6 +261,7 @@ BEGIN_DATADESC( CNPC_MetroPolice )
#ifdef MAPBASE
DEFINE_AIGRENADE_DATADESC()
DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ),
DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ),
#endif
END_DATADESC()
@ -517,6 +518,11 @@ CNPC_MetroPolice::CNPC_MetroPolice()
{
#ifdef MAPBASE
m_iGrenadeCapabilities = GRENCAP_GRENADE;
if (ai_grenade_always_drop.GetBool())
{
m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED);
}
#endif
}
@ -882,7 +888,7 @@ void CNPC_MetroPolice::SpeakStandoffSentence( int nSentenceType )
break;
case STANDOFF_SENTENCE_FORCED_TAKE_COVER:
SpeakIfAllowed( TLK_COP_SO_END );
SpeakIfAllowed( TLK_COP_SO_FORCE_COVER );
break;
case STANDOFF_SENTENCE_STAND_CHECK_TARGET:
@ -1008,7 +1014,12 @@ void CNPC_MetroPolice::SpeakSentence( int nSentenceType )
return;
}
#ifdef MAPBASE
// Fixed issues with standoff sentences not playing when they should
if ( m_StandoffBehavior.IsActive() )
#else
if ( GetRunningBehavior() == &m_StandoffBehavior )
#endif
{
SpeakStandoffSentence( nSentenceType );
return;
@ -1526,7 +1537,12 @@ void CNPC_MetroPolice::OnUpdateShotRegulator( )
BaseClass::OnUpdateShotRegulator();
// FIXME: This code (except the burst interval) could be used for all weapon types
#ifdef MAPBASE
// Only if we actually have the pistol out
if ( EntIsClass( GetActiveWeapon(), gm_isz_class_Pistol ) )
#else
if( Weapon_OwnsThisType( "weapon_pistol" ) )
#endif
{
if ( m_nBurstMode == BURST_NOT_ACTIVE )
{
@ -3676,6 +3692,11 @@ void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info )
DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
pHL2GameRules->NPC_DroppedHealth();
}
#ifdef MAPBASE
// Drop grenades if we should
DropGrenadeItemsOnDeath( info, pPlayer );
#endif
}
BaseClass::Event_Killed( info );
@ -3883,6 +3904,15 @@ int CNPC_MetroPolice::SelectScheduleNoDirectEnemy()
return SCHED_METROPOLICE_SMASH_PROP;
}
#ifdef MAPBASE
// If you see your enemy and you're still arming yourself, wait and don't just charge in
// (if your weapon is holstered, you're probably about to arm yourself)
if ( HasCondition( COND_SEE_ENEMY ) && GetWeapon(0) && (IsWeaponHolstered() || FindGestureLayer( TranslateActivity( ACT_ARM ) ) != -1) )
{
return SCHED_COMBAT_FACE;
}
#endif
return SCHED_METROPOLICE_CHASE_ENEMY;
}
@ -4506,34 +4536,7 @@ int CNPC_MetroPolice::SelectBehaviorOverrideSchedule()
//-----------------------------------------------------------------------------
bool CNPC_MetroPolice::IsCrouchedActivity( Activity activity )
{
Activity realActivity = TranslateActivity(activity);
switch ( realActivity )
{
case ACT_RELOAD_LOW:
case ACT_COVER_LOW:
case ACT_COVER_PISTOL_LOW:
case ACT_COVER_SMG1_LOW:
case ACT_RELOAD_SMG1_LOW:
//case ACT_RELOAD_AR2_LOW:
case ACT_RELOAD_PISTOL_LOW:
case ACT_RELOAD_SHOTGUN_LOW:
// These animations aren't actually "low" on metrocops
//case ACT_RANGE_AIM_LOW:
//case ACT_RANGE_AIM_AR2_LOW:
//case ACT_RANGE_AIM_SMG1_LOW:
//case ACT_RANGE_AIM_PISTOL_LOW:
//case ACT_RANGE_ATTACK1_LOW:
//case ACT_RANGE_ATTACK_AR2_LOW:
//case ACT_RANGE_ATTACK_SMG1_LOW:
//case ACT_RANGE_ATTACK_PISTOL_LOW:
//case ACT_RANGE_ATTACK2_LOW:
return true;
}
return false;
return BaseClass::IsCrouchedActivity( activity );
}
//-----------------------------------------------------------------------------
@ -4929,7 +4932,12 @@ int CNPC_MetroPolice::SelectSchedule( void )
// This will cause the cops to run backwards + shoot at the same time
if ( !bHighHealth && !HasBaton() )
{
#ifdef MAPBASE
// Don't do this with the 357 or any weapons which don't use clips
if ( GetActiveWeapon() && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->m_iClassname != gm_isz_class_357 && (GetActiveWeapon()->m_iClip1 <= 5) )
#else
if ( GetActiveWeapon() && (GetActiveWeapon()->m_iClip1 <= 5) )
#endif
{
#ifdef METROPOLICE_USES_RESPONSE_SYSTEM
SpeakIfAllowed( TLK_COP_LOWAMMO );

View File

@ -67,8 +67,12 @@ public:
const char* GetGrenadeAttachment() { return "LHand"; }
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; }
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0 && BaseClass::IsAltFireCapable(); }
virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; }
virtual bool ShouldDropGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_GRENADE) != 0 && BaseClass::ShouldDropGrenades(); }
virtual bool ShouldDropInterruptedGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_INTERRUPTED) != 0 && BaseClass::ShouldDropInterruptedGrenades(); }
virtual bool ShouldDropAltFire() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_ALTFIRE) != 0 && BaseClass::ShouldDropAltFire(); }
#endif
Vector EyeDirection3D( void ) { return CAI_BaseHumanoid::EyeDirection3D(); } // cops don't have eyes
@ -520,6 +524,7 @@ private:
// Determines whether this NPC is allowed to use grenades or alt-fire stuff.
eGrenadeCapabilities m_iGrenadeCapabilities;
eGrenadeDropCapabilities m_iGrenadeDropCapabilities;
#endif
AIHANDLE m_hManhack;

Some files were not shown because too many files have changed in this diff Show More