diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index c8208e6a..0dd620d7 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -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 ) diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index 900f5ab8..6082be1f 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -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; diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 4942cc65..b7ded40f 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -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 diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index f5e0523d..fe159afe 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -296,6 +296,7 @@ public: CScriptScope m_ScriptScope; static ScriptHook_t g_Hook_UpdateOnRemove; + static ScriptHook_t g_Hook_ModifyEmitSoundParams; #endif // IClientUnknown overrides. diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index ced6ced9..57a9c7eb 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -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: //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_baseplayer.h b/sp/src/game/client/c_baseplayer.h index fac6c0dd..5b569595 100644 --- a/sp/src/game/client/c_baseplayer.h +++ b/sp/src/game/client/c_baseplayer.h @@ -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: diff --git a/sp/src/game/client/c_colorcorrection.cpp b/sp/src/game/client/c_colorcorrection.cpp index 6960031d..12e29768 100644 --- a/sp/src/game/client/c_colorcorrection.cpp +++ b/sp/src/game/client/c_colorcorrection.cpp @@ -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 diff --git a/sp/src/game/client/c_colorcorrection.h b/sp/src/game/client/c_colorcorrection.h new file mode 100644 index 00000000..63149a0a --- /dev/null +++ b/sp/src/game/client/c_colorcorrection.h @@ -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 diff --git a/sp/src/game/client/c_colorcorrectionvolume.cpp b/sp/src/game/client/c_colorcorrectionvolume.cpp index 4bbcea94..f7e33708 100644 --- a/sp/src/game/client/c_colorcorrectionvolume.cpp +++ b/sp/src/game/client/c_colorcorrectionvolume.cpp @@ -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 diff --git a/sp/src/game/client/c_effects.cpp b/sp/src/game/client/c_effects.cpp index afd5bcf9..a273cb28 100644 --- a/sp/src/game/client/c_effects.cpp +++ b/sp/src/game/client/c_effects.cpp @@ -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 diff --git a/sp/src/game/client/c_effects.h b/sp/src/game/client/c_effects.h index 6012dff7..5f7e02a5 100644 --- a/sp/src/game/client/c_effects.h +++ b/sp/src/game/client/c_effects.h @@ -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 diff --git a/sp/src/game/client/c_env_screenoverlay.cpp b/sp/src/game/client/c_env_screenoverlay.cpp index 5496d453..a3125882 100644 --- a/sp/src/game/client/c_env_screenoverlay.cpp +++ b/sp/src/game/client/c_env_screenoverlay.cpp @@ -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 { diff --git a/sp/src/game/client/c_point_commentary_node.cpp b/sp/src/game/client/c_point_commentary_node.cpp index 47ea96ef..3314330b 100644 --- a/sp/src/game/client/c_point_commentary_node.cpp +++ b/sp/src/game/client/c_point_commentary_node.cpp @@ -18,6 +18,15 @@ #include "convar.h" #include "hud_closecaption.h" #include "in_buttons.h" +#ifdef MAPBASE +#include "vgui_controls/Label.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/AnimationController.h" +#include "filesystem.h" +#include "scenefilecache/ISceneFileCache.h" +#include "choreoscene.h" +#include "c_sceneentity.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -37,6 +46,15 @@ bool IsInCommentaryMode( void ) static bool g_bTracingVsCommentaryNodes = false; +#ifdef MAPBASE +ConVar commentary_type_force( "commentary_type_force", "-1", FCVAR_NONE, "Forces all commentary nodes to use the specified type." ); +ConVar commentary_type_text_endtime( "commentary_type_text_endtime", "120" ); +ConVar commentary_type_image_endtime( "commentary_type_image_endtime", "120" ); +ConVar commentary_audio_element_below_cc( "commentary_audio_element_below_cc", "1", FCVAR_NONE, "Allows commentary audio elements to display even when CC is enabled (although this is done by inverting their Y axis)" ); +ConVar commentary_audio_element_below_cc_margin( "commentary_audio_element_below_cc_margin", "4" ); +ConVar commentary_combine_speaker_and_printname( "commentary_combine_speaker_and_printname", "1" ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -52,12 +70,26 @@ public: virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); void StartCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); +#ifdef MAPBASE + void StartTextCommentary( C_PointCommentaryNode *pNode, const char *pszText, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); + void StartImageCommentary( C_PointCommentaryNode *pNode, const char *pszImage, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); + void StartSceneCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ); +#endif void StopCommentary( void ); bool IsTheActiveNode( C_PointCommentaryNode *pNode ) { return (pNode == m_hActiveNode); } +#ifdef MAPBASE + void CombineSpeakerAndPrintName( const char *pszPrintName ); + void RepositionCloseCaption(); +#endif + // vgui overrides virtual void Paint( void ); virtual bool ShouldDraw( void ); +#ifdef MAPBASE + virtual void PerformLayout(); + void ResolveBounds( int width, int height ); +#endif private: CHandle m_hActiveNode; @@ -68,6 +100,19 @@ private: wchar_t m_szCount[MAX_COUNT_STRING]; CMaterialReference m_matIcon; bool m_bHiding; +#ifdef MAPBASE + int m_iCommentaryType; + float m_flPanelScale; + float m_flOverrideX; + float m_flOverrideY; + + vgui::Label *m_pLabel; + vgui::ImagePanel *m_pImage; + vgui::HFont m_hFont; + + // HACKHACK: Needed as a failsafe to prevent desync + int m_iCCDefaultY; +#endif // Painting CPanelAnimationVarAliasType( int, m_iBarX, "bar_xpos", "8", "proportional_int" ); @@ -84,24 +129,65 @@ private: CPanelAnimationVarAliasType( int, m_iIconTall, "icon_height", "8", "proportional_int" ); CPanelAnimationVarAliasType( int, m_nIconTextureId, "icon_texture", "vgui/hud/icon_commentary", "textureid" ); +#ifdef MAPBASE + CPanelAnimationVarAliasType( int, m_iTypeAudioX, "type_audio_xpos", "190", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioY, "type_audio_ypos", "350", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioW, "type_audio_wide", "380", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeAudioT, "type_audio_tall", "40", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextX, "type_text_xpos", "180", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextY, "type_text_ypos", "150", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextW, "type_text_wide", "400", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextT, "type_text_tall", "200", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextCountXFR, "type_text_count_xpos_from_right", "10", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTypeTextCountYFB, "type_text_count_ypos_from_bottom", "10", "proportional_int" ); + CPanelAnimationVar( Color, m_TextBackgroundColor, "BackgroundColorTextContent", "0 0 0 224" ); + CPanelAnimationVar( Color, m_TypeTextContentColor, "TextContentColor", "255 230 180 255" ); + CPanelAnimationVar( int, m_iTextBorderSpace, "type_text_border_space", "8" ); +#endif + CPanelAnimationVar( bool, m_bUseScriptBGColor, "use_script_bgcolor", "0" ); +#ifdef MAPBASE + CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "Panel.BgColor" ); + CPanelAnimationVar( Color, m_ForegroundColor, "ForegroundColor", "255 170 0 255" ); +#else CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "0 0 0 0" ); +#endif CPanelAnimationVar( Color, m_BGOverrideColor, "BackgroundOverrideColor", "Panel.BgColor" ); }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -class C_PointCommentaryNode : public C_BaseAnimating +class C_PointCommentaryNode : public C_BaseAnimating, public IChoreoEventCallback { DECLARE_CLASS( C_PointCommentaryNode, C_BaseAnimating ); public: DECLARE_CLIENTCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif virtual void OnPreDataChanged( DataUpdateType_t type ); virtual void OnDataChanged( DataUpdateType_t type ); + void StartAudioCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); +#ifdef MAPBASE + void StartTextCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + void StartImageCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + void StartSceneCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ); + + // From IChoreoEventCallback + virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ); +#else + virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} +#endif + virtual void EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} + virtual void ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) {} + virtual bool CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) { return true; } + + void ClientThink(); + void OnRestore( void ) { BaseClass::OnRestore(); @@ -149,6 +235,11 @@ public: { int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "commentary" ); gHUD.LockRenderGroup( iRenderGroup ); + +#ifdef MAPBASE + // Special commentary localization file (useful for things like text nodes or print names) + g_pVGuiLocalize->AddFile( "resource/commentary_%language%.txt" ); +#endif } if ( g_CommentaryNodes.Find(this) == g_CommentaryNodes.InvalidIndex() ) @@ -168,6 +259,20 @@ public: } } +#ifdef MAPBASE_VSCRIPT // VScript funcs + bool IsActive() { return m_bActive; } + + int GetCommentaryType() { return m_iCommentaryType; } + void SetCommentaryType( int iType ) { m_iCommentaryType = iType; } + + const char *GetCommentaryFile() { return m_iszCommentaryFile; } + void SetCommentaryFile( const char *pszNewFile ) { Q_strncpy( m_iszCommentaryFile, pszNewFile, sizeof( m_iszCommentaryFile ) ); } + const char *GetSpeakers() { return m_iszSpeakers; } + void SetSpeakers( const char *pszSpeakers ) { Q_strncpy( m_iszSpeakers, pszSpeakers, sizeof( m_iszSpeakers ) ); } + const char *GetPrintName() { return m_iszPrintName; } + void SetPrintName( const char *pszPrintName ) { Q_strncpy( m_iszPrintName, pszPrintName, sizeof( m_iszPrintName ) ); } +#endif + public: // Data received from the server bool m_bActive; @@ -181,6 +286,21 @@ public: CSoundPatch *m_sndCommentary; EHANDLE m_hViewPosition; bool m_bRestartAfterRestore; +#ifdef MAPBASE + char m_iszPrintName[MAX_SPEAKER_NAME]; + int m_iCommentaryType; + float m_flPanelScale; + float m_flPanelX; + float m_flPanelY; + + CChoreoScene *m_pScene; + //CHandle m_hScene; + EHANDLE m_hSceneOrigin; +#endif + +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_PreStartCommentaryClient; +#endif }; IMPLEMENT_CLIENTCLASS_DT(C_PointCommentaryNode, DT_PointCommentaryNode, CPointCommentaryNode) @@ -192,6 +312,13 @@ IMPLEMENT_CLIENTCLASS_DT(C_PointCommentaryNode, DT_PointCommentaryNode, CPointCo RecvPropInt( RECVINFO( m_iNodeNumber ) ), RecvPropInt( RECVINFO( m_iNodeNumberMax ) ), RecvPropEHandle( RECVINFO(m_hViewPosition) ), +#ifdef MAPBASE + RecvPropString( RECVINFO( m_iszPrintName ) ), + RecvPropInt( RECVINFO( m_iCommentaryType ) ), + RecvPropFloat( RECVINFO( m_flPanelScale ) ), + RecvPropFloat( RECVINFO( m_flPanelX ) ), + RecvPropFloat( RECVINFO( m_flPanelY ) ), +#endif END_RECV_TABLE() BEGIN_DATADESC( C_PointCommentaryNode ) @@ -200,6 +327,26 @@ BEGIN_DATADESC( C_PointCommentaryNode ) DEFINE_SOUNDPATCH( m_sndCommentary ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t C_PointCommentaryNode::g_Hook_PreStartCommentaryClient; + +BEGIN_ENT_SCRIPTDESC( C_PointCommentaryNode, C_BaseAnimating, "Commentary nodes which play commentary in commentary mode." ) + + 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_SIMPLE_SCRIPTHOOK( C_PointCommentaryNode::g_Hook_PreStartCommentaryClient, "PreStartCommentaryClient", FIELD_BOOLEAN, "Called just before commentary begins on the client. Use this to modify variables or commentary behavior before it begins. Returning false will prevent the commentary from starting." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: @@ -229,6 +376,22 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( m_bActive && pPlayer ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_PreStartCommentaryClient.CanRunInScope( m_ScriptScope )) + { + ScriptVariant_t functionReturn; + if (g_Hook_PreStartCommentaryClient.Call( m_ScriptScope, &functionReturn, NULL ) && functionReturn.m_type == FIELD_BOOLEAN) + { + // Don't play the commentary if it returned false + if (functionReturn.m_bool == false) + { + engine->ServerCmd( "commentary_finishnode\n" ); + return; + } + } + } +#endif + // Use the HDR / Non-HDR version based on whether we're running HDR or not char *pszCommentaryFile; if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE && m_iszCommentaryFileNoHDR && m_iszCommentaryFileNoHDR[0] ) @@ -245,58 +408,33 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) return; } - EmitSound_t es; - es.m_nChannel = CHAN_STATIC; - es.m_pSoundName = pszCommentaryFile; - es.m_SoundLevel = SNDLVL_GUNFIRE; - es.m_nFlags = SND_SHOULDPAUSE; +#ifdef MAPBASE + int iCommentaryType = m_iCommentaryType; + if (commentary_type_force.GetInt() != -1) + iCommentaryType = commentary_type_force.GetInt(); - CBaseEntity *pSoundEntity; - if ( m_hViewPosition ) + switch (iCommentaryType) { - pSoundEntity = m_hViewPosition; - } - else if ( render->GetViewEntity() ) - { - pSoundEntity = cl_entitylist->GetEnt( render->GetViewEntity() ); - es.m_SoundLevel = SNDLVL_NONE; - } - else - { - pSoundEntity = pPlayer; - } - CSingleUserRecipientFilter filter( pPlayer ); - m_sndCommentary = (CSoundEnvelopeController::GetController()).SoundCreate( filter, pSoundEntity->entindex(), es ); - if ( m_sndCommentary ) - { - (CSoundEnvelopeController::GetController()).SoundSetCloseCaptionDuration( m_sndCommentary, -1 ); - (CSoundEnvelopeController::GetController()).Play( m_sndCommentary, 1.0f, 100, m_flStartTime ); - } + case COMMENTARY_TYPE_TEXT: + StartTextCommentary( pszCommentaryFile, pPlayer ); + break; - // Get the duration so we know when it finishes - float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + case COMMENTARY_TYPE_IMAGE: + StartImageCommentary( pszCommentaryFile, pPlayer ); + break; - CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if ( pHudCloseCaption ) - { - // This is where we play the commentary close caption (and lock the other captions out). - // Also, if close captions are off we force a caption in non-English - if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) - { - // Clear the close caption element in preparation - pHudCloseCaption->Reset(); + case COMMENTARY_TYPE_SCENE: + StartSceneCommentary( pszCommentaryFile, pPlayer ); + break; - // Process the commentary caption - pHudCloseCaption->ProcessCaptionDirect( pszCommentaryFile, flDuration ); - - // Find the close caption hud element & lock it - pHudCloseCaption->Lock(); - } + default: + case COMMENTARY_TYPE_AUDIO: + StartAudioCommentary( pszCommentaryFile, pPlayer ); + break; } - - // Tell the HUD element - CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); - pHudCommentary->StartCommentary( this, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +#else + StartAudioCommentary( pszCommentaryFile, pPlayer ); +#endif } else if ( m_bWasActive ) { @@ -312,6 +450,346 @@ void C_PointCommentaryNode::OnDataChanged( DataUpdateType_t updateType ) m_bRestartAfterRestore = false; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartAudioCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + EmitSound_t es; + es.m_nChannel = CHAN_STATIC; + es.m_pSoundName = pszCommentaryFile; + es.m_SoundLevel = SNDLVL_GUNFIRE; + es.m_nFlags = SND_SHOULDPAUSE; + + CBaseEntity *pSoundEntity; + if ( m_hViewPosition ) + { + pSoundEntity = m_hViewPosition; + } + else if ( render->GetViewEntity() ) + { + pSoundEntity = cl_entitylist->GetEnt( render->GetViewEntity() ); + es.m_SoundLevel = SNDLVL_NONE; + } + else + { + pSoundEntity = pPlayer; + } + CSingleUserRecipientFilter filter( pPlayer ); + m_sndCommentary = (CSoundEnvelopeController::GetController()).SoundCreate( filter, pSoundEntity->entindex(), es ); + if ( m_sndCommentary ) + { + (CSoundEnvelopeController::GetController()).SoundSetCloseCaptionDuration( m_sndCommentary, -1 ); + (CSoundEnvelopeController::GetController()).Play( m_sndCommentary, 1.0f, 100, m_flStartTime ); + } + + // Get the duration so we know when it finishes + float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + bool bSubtitlesEnabled = false; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption ) + { + // This is where we play the commentary close caption (and lock the other captions out). + // Also, if close captions are off we force a caption in non-English + if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + // Clear the close caption element in preparation + pHudCloseCaption->Reset(); + + // Process the commentary caption + pHudCloseCaption->ProcessCaptionDirect( pszCommentaryFile, flDuration ); + + // Find the close caption hud element & lock it + pHudCloseCaption->Lock(); + + bSubtitlesEnabled = true; + } + } + + char *pszSpeakers = m_iszSpeakers; + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartCommentary( this, pszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartTextCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + // Get the duration so we know when it finishes + //float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + + // TODO: Determine from text length? + float flDuration = commentary_type_text_endtime.GetFloat(); + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartTextCommentary( this, pszCommentaryFile, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartImageCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + // Get the duration so we know when it finishes + //float flDuration = enginesound->GetSoundDuration( STRING( CSoundEnvelopeController::GetController().SoundGetName( m_sndCommentary ) ) ) ; + + float flDuration = commentary_type_image_endtime.GetFloat(); + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartImageCommentary( this, pszCommentaryFile, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); +} + +extern CChoreoStringPool g_ChoreoStringPool; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartSceneCommentary( const char *pszCommentaryFile, C_BasePlayer *pPlayer ) +{ + char loadfile[MAX_PATH]; + Q_strncpy( loadfile, pszCommentaryFile, sizeof( loadfile ) ); + Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) ); + Q_FixSlashes( loadfile ); + + // + // Raw scene file support + // + void *pBuffer = 0; + size_t bufsize = scenefilecache->GetSceneBufferSize( loadfile ); + if ( bufsize > 0 ) + { + // Definitely in scenes.image + pBuffer = malloc( bufsize ); + if ( !scenefilecache->GetSceneData( pszCommentaryFile, (byte *)pBuffer, bufsize ) ) + { + free( pBuffer ); + } + + + if ( IsBufferBinaryVCD( (char*)pBuffer, bufsize ) ) + { + m_pScene = new CChoreoScene( NULL ); + CUtlBuffer buf( pBuffer, bufsize, CUtlBuffer::READ_ONLY ); + if ( !m_pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) ) + { + Warning( "Unable to restore scene '%s'\n", loadfile ); + delete m_pScene; + m_pScene = NULL; + } + } + } + else if (filesystem->ReadFileEx( loadfile, "MOD", &pBuffer, true )) + { + // Not in scenes.image, but it's a raw file + g_TokenProcessor.SetBuffer((char*)pBuffer); + m_pScene = ChoreoLoadScene( loadfile, this, &g_TokenProcessor, Scene_Printf ); + } + + free( pBuffer ); + + if( m_pScene ) + { + m_pScene->SetPrintFunc( Scene_Printf ); + m_pScene->SetEventCallbackInterface( this ); + } + else + { + // Cancel commentary (TODO: clean up?) + return; + } + + int types[ 2 ]; + types[ 0 ] = CChoreoEvent::SPEAK; + //types[ 1 ] = CChoreoEvent::GENERIC; // TODO: Support for the game_text event? + m_pScene->RemoveEventsExceptTypes( types, 1 ); + + // Iterate events and precache necessary resources + for ( int i = 0; i < m_pScene->GetNumEvents(); i++ ) + { + CChoreoEvent *event = m_pScene->GetEvent( i ); + if ( !event ) + continue; + + // load any necessary data + switch (event->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + { + // Defined in SoundEmitterSystem.cpp + // NOTE: The script entries associated with .vcds are forced to preload to avoid + // loading hitches during triggering + CBaseEntity::PrecacheScriptSound( event->GetParameters() ); + + if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && + event->GetNumSlaves() > 0 ) + { + char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; + if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) ) + { + CBaseEntity::PrecacheScriptSound( tok ); + } + } + } + break; + } + } + + PrecacheScriptSound( "AI_BaseNPC.SentenceStop" ); + + if ( m_hViewPosition ) + { + m_hSceneOrigin = m_hViewPosition; + } + else if ( render->GetViewEntity() ) + { + m_hSceneOrigin = cl_entitylist->GetEnt( render->GetViewEntity() ); + } + else + { + m_hSceneOrigin = pPlayer; + } + + // Get the duration so we know when it finishes + float flDuration = m_pScene->GetDuration(); + + // Add a tiny amount of time at the end to ensure audio doesn't get cut off + flDuration += 0.5f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption ) + { + // This is where we play the commentary close caption (and lock the other captions out). + // Also, if close captions are off we force a caption in non-English + if ( closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + // Clear the close caption element in preparation + pHudCloseCaption->Reset(); + + // Find the close caption hud element & lock it + pHudCloseCaption->Lock(); + } + } + + // Tell the HUD element + CHudCommentary *pHudCommentary = (CHudCommentary *)GET_HUDELEMENT( CHudCommentary ); + pHudCommentary->StartSceneCommentary( this, m_iszSpeakers, m_iNodeNumber, m_iNodeNumberMax, m_flStartTime, m_flStartTime + flDuration ); + + // Start thinking for the scene + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: All events are leading edge triggered +// Input : currenttime - +// *event - +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event ) +{ + Assert( event ); + + if ( !Q_stricmp( event->GetName(), "NULL" ) ) + { + return; + } + + //Msg("Starting event \"%s\" (%s)\n", event->GetName(), event->GetParameters()); + + // load any necessary data + switch (event->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + { + CSingleUserRecipientFilter filter( C_BasePlayer::GetLocalPlayer() ); + + EmitSound_t es; + es.m_nChannel = CHAN_VOICE2; + es.m_flVolume = 1; + es.m_SoundLevel = SNDLVL_GUNFIRE; + //es.m_nFlags = SND_SHOULDPAUSE; + + es.m_bEmitCloseCaption = false; + es.m_pSoundName = event->GetParameters(); + + // Just in case + if (!m_hSceneOrigin) + m_hSceneOrigin = C_BasePlayer::GetLocalPlayer(); + + EmitSound( filter, m_hSceneOrigin->entindex(), es ); + + // Close captioning only on master token no matter what... + // Also, if close captions are off we force a caption in non-English + if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && closecaption.GetBool() || ( !closecaption.GetBool() && !english.GetBool() ) ) + { + char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; + bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ); + if ( validtoken ) + { + CRC32_t tokenCRC; + CRC32_Init( &tokenCRC ); + + char lowercase[ 256 ]; + Q_strncpy( lowercase, tok, sizeof( lowercase ) ); + Q_strlower( lowercase ); + + CRC32_ProcessBuffer( &tokenCRC, lowercase, Q_strlen( lowercase ) ); + CRC32_Final( &tokenCRC ); + + float endtime = event->GetLastSlaveEndTime(); + float durationShort = event->GetDuration(); + float durationLong = endtime - event->GetStartTime(); + float duration = MAX( durationShort, durationLong ); + + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); + if ( hudCloseCaption ) + { + hudCloseCaption->ProcessCaptionDirect( lowercase, duration ); + } + } + + } + } + break; + // TODO: Support for the game_text event? + /* + case CChoreoEvent::GENERIC: + { + + } + break; + */ + } + + event->m_flPrevTime = currenttime; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_PointCommentaryNode::ClientThink() +{ + BaseClass::ClientThink(); + +#ifdef MAPBASE + if (m_iCommentaryType == COMMENTARY_TYPE_SCENE && m_pScene) + { + m_pScene->Think( gpGlobals->curtime - m_flStartTime ); + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } +#endif +} + //----------------------------------------------------------------------------- // Purpose: Shut down the commentary //----------------------------------------------------------------------------- @@ -322,6 +800,19 @@ void C_PointCommentaryNode::StopLoopingSounds( void ) (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndCommentary ); m_sndCommentary = NULL; } + +#ifdef MAPBASE + if ( m_pScene ) + { + delete m_pScene; + m_pScene = NULL; + + // Must do this to terminate audio + // (TODO: This causes problems when players switch from a scene node immediately to a regular audio node) + if (m_hSceneOrigin) + m_hSceneOrigin->EmitSound( "AI_BaseNPC.SentenceStop" ); + } +#endif } //----------------------------------------------------------------------------- @@ -374,6 +865,14 @@ CHudCommentary::CHudCommentary( const char *name ) : vgui::Panel( NULL, "HudComm m_hActiveNode = NULL; m_bShouldPaint = true; + +#ifdef MAPBASE + m_pLabel = new vgui::Label( this, "HudCommentaryTextLabel", L"Textual commentary" ); + m_pImage = new vgui::ImagePanel( this, "HudCommentaryImagePanel" ); + m_pImage->SetShouldScaleImage( true ); + + m_iCCDefaultY = 0; +#endif } void CHudCommentary::ApplySchemeSettings( vgui::IScheme *pScheme ) @@ -384,6 +883,11 @@ void CHudCommentary::ApplySchemeSettings( vgui::IScheme *pScheme ) { SetBgColor( m_BGOverrideColor ); } + +#ifdef MAPBASE + m_pLabel->SetPaintBackgroundType( 2 ); + m_pLabel->SetSize( 0, GetTall() ); +#endif } //----------------------------------------------------------------------------- @@ -405,6 +909,21 @@ void CHudCommentary::Paint() if ( pHudCloseCaption ) { pHudCloseCaption->Reset(); + +#ifdef MAPBASE + // Reset close caption element if needed + if (pHudCloseCaption->IsUsingCommentaryDimensions()) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + //pHudCloseCaption->SetPos( ccX, ccY + m_iTypeAudioT ); + + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY + m_iTypeAudioT, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif } } } @@ -413,6 +932,26 @@ void CHudCommentary::Paint() // Detect the end of the commentary if ( flPercentage >= 1 && m_hActiveNode ) { +#ifdef MAPBASE + // Ensure that the scene is terminated + if (m_iCommentaryType == COMMENTARY_TYPE_SCENE) + m_hActiveNode->StopLoopingSounds(); + + // Reset close caption element if needed + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + //pHudCloseCaption->SetPos( ccX, ccY + m_iTypeAudioT ); + + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY + m_iTypeAudioT, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif + m_hActiveNode = NULL; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HideCommentary" ); @@ -426,27 +965,62 @@ void CHudCommentary::Paint() int x, y, wide, tall; GetBounds( x, y, wide, tall ); - int xOffset = m_iBarX; + int xOffset = m_iBarX; int yOffset = m_iBarY; // Find our fade based on our time shown - Color clr = Color( 255, 170, 0, GetAlpha() ); + Color clr = m_ForegroundColor; +#ifdef MAPBASE + switch (m_iCommentaryType) + { + case COMMENTARY_TYPE_TEXT: + { + // Figure out the size before setting bounds + int lW, lT; + m_pLabel->GetContentSize( lW, lT ); + + lT += (m_iTextBorderSpace * 2); + + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset + (m_iBarWide * m_flPanelScale), yOffset + (lT /** m_flPanelScale*/) ); //m_iTypeTextT - (yOffset /*+ m_iBarTall*/) ); + } break; + + case COMMENTARY_TYPE_IMAGE: + { + // Figure out the size before setting bounds + int iW, iT; + m_pImage->GetSize( iW, iT ); + //vgui::surface()->DrawGetTextureSize( m_pImage->GetImage()->GetID(), iW, iT ); + + iW += (m_iTextBorderSpace * 2); + iT += (m_iTextBorderSpace * 2); + + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset + iW, yOffset + iT ); //m_iTypeTextT - (yOffset /*+ m_iBarTall*/) ); + } break; + + default: + case COMMENTARY_TYPE_SCENE: + case COMMENTARY_TYPE_AUDIO: + { + // Draw the progress bar + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset+(m_iBarWide*m_flPanelScale), yOffset+m_iBarTall ); + vgui::surface()->DrawSetColor( clr ); + vgui::surface()->DrawFilledRect( xOffset+2, yOffset+2, xOffset+(int)(flPercentage*(m_iBarWide*m_flPanelScale))-2, yOffset+m_iBarTall-2 ); + } break; + } +#else // Draw the progress bar vgui::surface()->DrawSetColor( clr ); vgui::surface()->DrawOutlinedRect( xOffset, yOffset, xOffset+m_iBarWide, yOffset+m_iBarTall ); vgui::surface()->DrawSetColor( clr ); vgui::surface()->DrawFilledRect( xOffset+2, yOffset+2, xOffset+(int)(flPercentage*m_iBarWide)-2, yOffset+m_iBarTall-2 ); +#endif // Draw the speaker names - // Get our scheme and font information - vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); - vgui::HFont hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); - if ( !hFont ) - { - hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); - } - vgui::surface()->DrawSetTextFont( hFont ); + vgui::surface()->DrawSetTextFont( m_hFont ); vgui::surface()->DrawSetTextColor( clr ); vgui::surface()->DrawSetTextPos( m_iSpeakersX, m_iSpeakersY ); vgui::surface()->DrawPrintText( m_szSpeakers, wcslen(m_szSpeakers) ); @@ -469,7 +1043,7 @@ void CHudCommentary::Paint() { int w, h; UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ) ); - vgui::surface()->GetTextSize( hFont, wzFinal, w, h ); + vgui::surface()->GetTextSize( m_hFont, wzFinal, w, h ); vgui::surface()->DrawSetTextPos( m_iBarX + m_iBarWide - w, iY ); vgui::surface()->DrawPrintText( wzFinal, wcslen(wzFinal) ); } @@ -478,9 +1052,16 @@ void CHudCommentary::Paint() // Draw the commentary count // Determine our text size, and move that far in from the right hand size (plus the offset) int iCountWide, iCountTall; - vgui::surface()->GetTextSize( hFont, m_szCount, iCountWide, iCountTall ); + vgui::surface()->GetTextSize( m_hFont, m_szCount, iCountWide, iCountTall ); + +#ifdef MAPBASE + if (m_iCommentaryType != COMMENTARY_TYPE_AUDIO && m_iCommentaryType != COMMENTARY_TYPE_SCENE) + vgui::surface()->DrawSetTextPos( wide - m_iTypeTextCountXFR - iCountWide, tall - m_iTypeTextCountYFB - iCountTall ); + else +#endif vgui::surface()->DrawSetTextPos( wide - m_iCountXFR - iCountWide, m_iCountY ); - vgui::surface()->DrawPrintText( m_szCount, wcslen(m_szCount) ); + + vgui::surface()->DrawPrintText( m_szCount, wcslen( m_szCount ) ); // Draw the icon vgui::surface()->DrawSetColor( Color(255,170,0,GetAlpha()) ); @@ -496,12 +1077,136 @@ bool CHudCommentary::ShouldDraw() return ( m_hActiveNode || GetAlpha() > 0 ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::PerformLayout() +{ + BaseClass::PerformLayout(); + + switch (m_iCommentaryType) + { + case COMMENTARY_TYPE_TEXT: + { + int xOffset = m_iBarX; + int yOffset = m_iBarY; + + m_pLabel->SetBounds( + xOffset + m_iTextBorderSpace, yOffset + m_iTextBorderSpace, + (float)(m_iBarWide * m_flPanelScale) - m_iTextBorderSpace, GetTall() ); + + // Figure out the size before setting bounds + int lW, lT; + m_pLabel->GetContentSize( lW, lT ); + + //lT = (float)lT * m_flPanelScale; // Don't affect height when scaling + + m_pLabel->SetTall( lT ); + + lW += (float)((m_iTextBorderSpace * 2) + (xOffset * 2)); + lT += (float)((m_iTextBorderSpace * 2) + (yOffset * 2)); + + ResolveBounds( lW, lT ); + } break; + + case COMMENTARY_TYPE_IMAGE: + { + int xOffset = m_iBarX; + int yOffset = m_iBarY; + + // Figure out the size before setting bounds + int iW, iT; + //m_pImage->GetImage()->GetSize( iW, iT ); + vgui::surface()->DrawGetTextureSize( m_pImage->GetImage()->GetID(), iW, iT ); + if (iW <= 0) + iW = 1; + + int iTargetSize = (m_iBarWide - m_iTextBorderSpace); + iT *= (iTargetSize / iW); + iW = iTargetSize; + + iW = (float)iW * m_flPanelScale; + iT = (float)iT * m_flPanelScale; + + m_pImage->SetBounds( + xOffset + m_iTextBorderSpace, + yOffset + m_iTextBorderSpace, + iW, iT ); + + iW += (float)((m_iTextBorderSpace * 2) + (xOffset * 2)); + iT += (float)((m_iTextBorderSpace * 2) + (yOffset * 2)); + + ResolveBounds( iW, iT ); + } break; + + default: + case COMMENTARY_TYPE_SCENE: + case COMMENTARY_TYPE_AUDIO: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resolves position on screen; Heavily borrows from CHudMessage::XPosition/YPosition +//----------------------------------------------------------------------------- +void CHudCommentary::ResolveBounds( int width, int height ) +{ + int xPos; + int yPos; + + // ====== X ====== + if ( m_flOverrideX == -1 ) + { + xPos = (ScreenWidth() - width) * 0.5f; + } + else + { + if ( m_flOverrideX < 0 ) + xPos = (1.0 + m_flOverrideX) * ScreenWidth() - width; // Align to right + else + xPos = m_flOverrideX * (ScreenWidth() - width); + } + + // Clamp to edge of screen + if ( xPos + width > ScreenWidth() ) + xPos = ScreenWidth() - width; + else if ( xPos < 0 ) + xPos = 0; + + // ====== Y ====== + if ( m_flOverrideY == -1 ) + { + yPos = (ScreenHeight() - height) * 0.5f; + } + else + { + if ( m_flOverrideY < 0 ) + yPos = (1.0 + m_flOverrideY) * ScreenHeight() - height; // Align to bottom + else + yPos = m_flOverrideY * (ScreenHeight() - height); + } + + // Clamp to edge of screen + if ( yPos + height > ScreenHeight() ) + yPos = ScreenHeight() - height; + else if ( yPos < 0 ) + yPos = 0; + + SetBounds( xPos, yPos, width, height ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudCommentary::Init( void ) { m_matIcon.Init( "vgui/hud/icon_commentary", TEXTURE_GROUP_VGUI ); + +#ifdef MAPBASE + SetProportional( true ); +#endif } //----------------------------------------------------------------------------- @@ -511,6 +1216,9 @@ void CHudCommentary::VidInit( void ) { SetAlpha(0); StopCommentary(); +#ifdef MAPBASE + m_iCCDefaultY = 0; +#endif } //----------------------------------------------------------------------------- @@ -525,7 +1233,30 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe m_flStartTime = flStartTime; m_flEndTime = flEndTime; m_bHiding = false; - g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof(m_szSpeakers) ); +#ifdef MAPBASE + m_iCommentaryType = COMMENTARY_TYPE_AUDIO; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; +#endif + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + +#ifdef MAPBASE + SetBounds( m_iTypeAudioX, m_iTypeAudioY, m_iTypeAudioW, m_iTypeAudioT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_BackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } +#endif // Don't draw the element itself if closecaptions are on (and captions are always on in non-english mode) ConVarRef pCVar( "closecaption" ); @@ -537,6 +1268,88 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe { m_bShouldPaint = true; } + +#ifdef MAPBASE + if (!m_bShouldPaint && commentary_audio_element_below_cc.GetBool()) + { + m_bShouldPaint = true; + RepositionCloseCaption(); + } + + if (commentary_combine_speaker_and_printname.GetBool() && pNode && pNode->m_iszPrintName[0] != '\0') + { + CombineSpeakerAndPrintName( pNode->m_iszPrintName ); + } +#endif + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartTextCommentary( C_PointCommentaryNode *pNode, const char *pszText, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_TEXT; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeTextX, m_iTypeTextY, m_iTypeTextW, m_iTypeTextT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_TextBackgroundColor ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_pLabel->SetText( pszText ); + m_pLabel->SetFont( m_hFont ); + m_pLabel->SetWrap( true ); + m_pLabel->SetPaintEnabled( true ); + m_pLabel->SetPaintBackgroundEnabled( false ); + m_pLabel->SetPaintBorderEnabled( false ); + //m_pLabel->SizeToContents(); + m_pLabel->SetContentAlignment( vgui::Label::a_northwest ); + m_pLabel->SetFgColor( m_TypeTextContentColor ); + + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + m_bShouldPaint = true; + + if (commentary_combine_speaker_and_printname.GetBool() && pNode && pNode->m_iszPrintName[0] != '\0') + { + CombineSpeakerAndPrintName( pNode->m_iszPrintName ); + } + SetPaintBackgroundEnabled( m_bShouldPaint ); char sz[MAX_COUNT_STRING]; @@ -555,14 +1368,238 @@ void CHudCommentary::StartCommentary( C_PointCommentaryNode *pNode, char *pszSpe } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartImageCommentary( C_PointCommentaryNode *pNode, const char *pszImage, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_IMAGE; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeTextX, m_iTypeTextY, m_iTypeTextW, m_iTypeTextT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_TextBackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + + m_pImage->SetPaintEnabled( true ); + m_pImage->SetImage( pszImage ); + m_pImage->SetWide( m_iBarWide - m_iTextBorderSpace ); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + m_bShouldPaint = true; + + if (commentary_combine_speaker_and_printname.GetBool() && pNode && pNode->m_iszPrintName[0] != '\0') + { + CombineSpeakerAndPrintName( pNode->m_iszPrintName ); + } + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::StartSceneCommentary( C_PointCommentaryNode *pNode, char *pszSpeakers, int iNode, int iNodeMax, float flStartTime, float flEndTime ) +{ + if ( (flEndTime - flStartTime) <= 0 ) + return; + + m_hActiveNode = pNode; + m_flStartTime = flStartTime; + m_flEndTime = flEndTime; + m_bHiding = false; + m_iCommentaryType = COMMENTARY_TYPE_SCENE; + m_flPanelScale = pNode->m_flPanelScale; + m_flOverrideX = pNode->m_flPanelX; + m_flOverrideY = pNode->m_flPanelY; + g_pVGuiLocalize->ConvertANSIToUnicode( pszSpeakers, m_szSpeakers, sizeof( m_szSpeakers ) ); + + SetBounds( m_iTypeAudioX, m_iTypeAudioY, m_iTypeAudioW, m_iTypeAudioT ); + SetBgColor( m_bUseScriptBGColor ? m_BGOverrideColor : m_BackgroundColor ); + + m_pLabel->SetPaintEnabled( false ); + m_pImage->SetPaintEnabled( false ); + m_pImage->EvictImage(); + + // Get our scheme and font information + vgui::HScheme scheme = GetScheme(); + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + if ( !m_hFont ) + { + m_hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + } + + // Don't draw the element itself if closecaptions are on (and captions are always on in non-english mode) + ConVarRef pCVar( "closecaption" ); + if ( pCVar.IsValid() ) + { + m_bShouldPaint = ( !pCVar.GetBool() && english.GetBool() ); + } + else + { + m_bShouldPaint = true; + } + + if (!m_bShouldPaint && commentary_audio_element_below_cc.GetBool()) + { + m_bShouldPaint = true; + RepositionCloseCaption(); + } + + if (commentary_combine_speaker_and_printname.GetBool() && pNode && pNode->m_iszPrintName[0] != '\0') + { + CombineSpeakerAndPrintName( pNode->m_iszPrintName ); + } + + SetPaintBackgroundEnabled( m_bShouldPaint ); + + char sz[MAX_COUNT_STRING]; + Q_snprintf( sz, sizeof(sz), "%d \\ %d", iNode, iNodeMax ); + g_pVGuiLocalize->ConvertANSIToUnicode( sz, m_szCount, sizeof(m_szCount) ); + + // If the commentary just started, play the commentary fade in. + if ( fabs(flStartTime - gpGlobals->curtime) < 1.0 ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ShowCommentary" ); + } + else + { + // We're reloading a savegame that has an active commentary going in it. Don't fade in. + SetAlpha( 255 ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudCommentary::StopCommentary( void ) { m_hActiveNode = NULL; + +#ifdef MAPBASE + // Reset close caption element if needed + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + //pHudCloseCaption->SetPos( ccX, ccY + m_iTypeAudioT ); + + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY + m_iTypeAudioT, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); + + pHudCloseCaption->SetUsingCommentaryDimensions( false ); + } +#endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::CombineSpeakerAndPrintName( const char *pszPrintName ) +{ + wchar_t *pszLocal = g_pVGuiLocalize->Find( pszPrintName ); + if (m_szSpeakers[0] == '\0' || !m_bShouldPaint) // Use m_bShouldPaint as an indicator of whether or not we use subtitles + { + if (pszPrintName[0] == '#' && pszLocal) + wcsncpy( m_szSpeakers, pszLocal, sizeof( m_szSpeakers ) / sizeof( wchar_t ) ); + else + g_pVGuiLocalize->ConvertANSIToUnicode( pszPrintName, m_szSpeakers, sizeof( m_szSpeakers ) ); + } + else + { + static wchar_t iszPrintNameLocalized[MAX_SPEAKER_NAME]; + + if (pszPrintName[0] == '#' && pszLocal) + wcsncpy( iszPrintNameLocalized, pszLocal, sizeof( iszPrintNameLocalized ) / sizeof( wchar_t ) ); + else + g_pVGuiLocalize->ConvertANSIToUnicode( pszPrintName, iszPrintNameLocalized, sizeof( iszPrintNameLocalized ) ); + + V_snwprintf( m_szSpeakers, sizeof( m_szSpeakers ), L"%ls ~ %ls", m_szSpeakers, iszPrintNameLocalized ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCommentary::RepositionCloseCaption() +{ + // Invert the Y axis + //SetPos( m_iTypeAudioX, ScreenHeight() - m_iTypeAudioY ); + + // Place underneath the close caption element + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption /*&& !pHudCloseCaption->IsUsingCommentaryDimensions()*/) + { + int ccX, ccY; + pHudCloseCaption->GetPos( ccX, ccY ); + + // Save the default position in case we need to do a hard reset + // (this usually happens when players begin commentary before the CC element's return animation command is finished) + if (m_iCCDefaultY == 0) + { + m_iCCDefaultY = ccY; + } + + if (!pHudCloseCaption->IsUsingCommentaryDimensions()) + { + if (m_iCCDefaultY != ccY && !pHudCloseCaption->IsUsingCommentaryDimensions()) + { + DevMsg( "CHudCommentary had to reset misaligned CC element Y (%i) to default Y (%i)\n", ccY, m_iCCDefaultY ); + ccY = m_iCCDefaultY; + } + + ccY -= m_iTypeAudioT; + + // Run this animation command instead of setting the position directly + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_DEACCEL ); + //pHudCloseCaption->SetPos( ccX, ccY ); + + pHudCloseCaption->SetUsingCommentaryDimensions( true ); + } + + SetPos( ccX, ccY + pHudCloseCaption->GetTall() + commentary_audio_element_below_cc_margin.GetInt() ); + + m_flPanelScale = (float)pHudCloseCaption->GetWide() / (float)GetWide(); + SetWide( pHudCloseCaption->GetWide() ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_vguiscreen.h b/sp/src/game/client/c_vguiscreen.h index d304f5c9..71780093 100644 --- a/sp/src/game/client/c_vguiscreen.h +++ b/sp/src/game/client/c_vguiscreen.h @@ -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 ); diff --git a/sp/src/game/client/clientmode_shared.cpp b/sp/src/game/client/clientmode_shared.cpp index 6f2aa4b8..f2e6e31d 100644 --- a/sp/src/game/client/clientmode_shared.cpp +++ b/sp/src/game/client/clientmode_shared.cpp @@ -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 //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/clientmode_shared.h b/sp/src/game/client/clientmode_shared.h index a1389f96..20288db8 100644 --- a/sp/src/game/client/clientmode_shared.h +++ b/sp/src/game/client/clientmode_shared.h @@ -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 m_pCurrentColorCorrection; +#endif }; #endif // CLIENTMODE_NORMAL_H diff --git a/sp/src/game/client/colorcorrectionmgr.cpp b/sp/src/game/client/colorcorrectionmgr.cpp index 770354b0..cf1210ac 100644 --- a/sp/src/game/client/colorcorrectionmgr.cpp +++ b/sp/src/game/client/colorcorrectionmgr.cpp @@ -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 g_ColorCorrectionList; +static CUtlVector 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( 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 diff --git a/sp/src/game/client/colorcorrectionmgr.h b/sp/src/game/client/colorcorrectionmgr.h index 3d5271db..3eba0f8c 100644 --- a/sp/src/game/client/colorcorrectionmgr.h +++ b/sp/src/game/client/colorcorrectionmgr.h @@ -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 diff --git a/sp/src/game/client/fx_impact.cpp b/sp/src/game/client/fx_impact.cpp index 682d5435..41893f7b 100644 --- a/sp/src/game/client/fx_impact.cpp +++ b/sp/src/game/client/fx_impact.cpp @@ -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 ) { diff --git a/sp/src/game/client/fx_impact.h b/sp/src/game/client/fx_impact.h index ad57f7e7..9c6cb875 100644 --- a/sp/src/game/client/fx_impact.h +++ b/sp/src/game/client/fx_impact.h @@ -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 diff --git a/sp/src/game/client/game_controls/baseviewport.cpp b/sp/src/game/client/game_controls/baseviewport.cpp index 0b99ef63..a0986620 100644 --- a/sp/src/game/client/game_controls/baseviewport.cpp +++ b/sp/src/game/client/game_controls/baseviewport.cpp @@ -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") { diff --git a/sp/src/game/client/game_controls/baseviewport.h b/sp/src/game/client/game_controls/baseviewport.h index 4e4c64e8..cbc602a0 100644 --- a/sp/src/game/client/game_controls/baseviewport.h +++ b/sp/src/game/client/game_controls/baseviewport.h @@ -73,6 +73,10 @@ public: public: // IGameEventListener: virtual void FireGameEvent( IGameEvent * event); +#ifdef MAPBASE + bool LoadCustomHudAnimations( const char *pszFile ); + void ReloadHudAnimations( void ); +#endif protected: diff --git a/sp/src/game/client/hl2/hud_credits.cpp b/sp/src/game/client/hl2/hud_credits.cpp index 280e8a44..6520e6b4 100644 --- a/sp/src/game/client/hl2/hud_credits.cpp +++ b/sp/src/game/client/hl2/hud_credits.cpp @@ -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 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(); diff --git a/sp/src/game/client/hud_closecaption.cpp b/sp/src/game/client/hud_closecaption.cpp index 8a5ddd55..9b9c0c11 100644 --- a/sp/src/game/client/hud_closecaption.cpp +++ b/sp/src/game/client/hud_closecaption.cpp @@ -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 &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 &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 diff --git a/sp/src/game/client/hud_closecaption.h b/sp/src/game/client/hud_closecaption.h index 180afe9d..b3bdae06 100644 --- a/sp/src/game/client/hud_closecaption.h +++ b/sp/src/game/client/hud_closecaption.h @@ -111,6 +111,11 @@ public: void PlayRandomCaption(); void InitCaptionDictionary( char const *dbfile ); +#ifdef MAPBASE + void AddAdditionalCaptionDictionary( char const *dbfile, CUtlVector &outPathSymbols ); + void AddCustomCaptionFile( char const *file, CUtlVector &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 diff --git a/sp/src/game/client/iclientmode.h b/sp/src/game/client/iclientmode.h index 2b74f625..38b60047 100644 --- a/sp/src/game/client/iclientmode.h +++ b/sp/src/game/client/iclientmode.h @@ -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; //============================================================================= diff --git a/sp/src/game/client/iviewrender.h b/sp/src/game/client/iviewrender.h index c66061ae..8d797dea 100644 --- a/sp/src/game/client/iviewrender.h +++ b/sp/src/game/client/iviewrender.h @@ -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; diff --git a/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp b/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp index 9d92675e..1cae8ebe 100644 --- a/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp +++ b/sp/src/game/client/mapbase/c_func_fake_worldportal.cpp @@ -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; } } diff --git a/sp/src/game/client/mapbase/c_func_fake_worldportal.h b/sp/src/game/client/mapbase/c_func_fake_worldportal.h index 3fc4418b..1bae2de0 100644 --- a/sp/src/game/client/mapbase/c_func_fake_worldportal.h +++ b/sp/src/game/client/mapbase/c_func_fake_worldportal.h @@ -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 diff --git a/sp/src/game/client/mapbase/c_vgui_text_display.cpp b/sp/src/game/client/mapbase/c_vgui_text_display.cpp index c3847ac0..f0d2032d 100644 --- a/sp/src/game/client/mapbase/c_vgui_text_display.cpp +++ b/sp/src/game/client/mapbase/c_vgui_text_display.cpp @@ -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; diff --git a/sp/src/game/client/ragdoll.cpp b/sp/src/game/client/ragdoll.cpp index 0fc3d0e0..1e044a1d 100644 --- a/sp/src/game/client/ragdoll.cpp +++ b/sp/src/game/client/ragdoll.cpp @@ -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; } diff --git a/sp/src/game/client/view.cpp b/sp/src/game/client/view.cpp index c78c76fd..17988d18 100644 --- a/sp/src/game/client/view.cpp +++ b/sp/src/game/client/view.cpp @@ -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 ); diff --git a/sp/src/game/client/viewdebug.cpp b/sp/src/game/client/viewdebug.cpp index 619993fa..0204a0ab 100644 --- a/sp/src/game/client/viewdebug.cpp +++ b/sp/src/game/client/viewdebug.cpp @@ -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 %s\n" ); + } +} +#endif + // Used to verify frame syncing. void CDebugViewRender::GenerateOverdrawForTesting() { diff --git a/sp/src/game/client/viewrender.cpp b/sp/src/game/client/viewrender.cpp index 29f9c651..c88858d5 100644 --- a/sp/src/game/client/viewrender.cpp +++ b/sp/src/game/client/viewrender.cpp @@ -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() ); diff --git a/sp/src/game/client/viewrender.h b/sp/src/game/client/viewrender.h index 2312c4d5..936a282b 100644 --- a/sp/src/game/client/viewrender.h +++ b/sp/src/game/client/viewrender.h @@ -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; diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index dccaf2a5..405b2083 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -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(); //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/BaseAnimatingOverlay.cpp b/sp/src/game/server/BaseAnimatingOverlay.cpp index c1d7b883..06bf690e 100644 --- a/sp/src/game/server/BaseAnimatingOverlay.cpp +++ b/sp/src/game/server/BaseAnimatingOverlay.cpp @@ -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 + //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/BaseAnimatingOverlay.h b/sp/src/game/server/BaseAnimatingOverlay.h index ac112cbd..38ee7ed1 100644 --- a/sp/src/game/server/BaseAnimatingOverlay.h +++ b/sp/src/game/server/BaseAnimatingOverlay.h @@ -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); diff --git a/sp/src/game/server/BasePropDoor.h b/sp/src/game/server/BasePropDoor.h index d96c75c5..2557bec3 100644 --- a/sp/src/game/server/BasePropDoor.h +++ b/sp/src/game/server/BasePropDoor.h @@ -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. diff --git a/sp/src/game/server/CommentarySystem.cpp b/sp/src/game/server/CommentarySystem.cpp index 39742467..3b4ef12b 100644 --- a/sp/src/game/server/CommentarySystem.cpp +++ b/sp/src/game/server/CommentarySystem.cpp @@ -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; diff --git a/sp/src/game/server/ai_activity.cpp b/sp/src/game/server/ai_activity.cpp index b88c1142..02afb3b7 100644 --- a/sp/src/game/server/ai_activity.cpp +++ b/sp/src/game/server/ai_activity.cpp @@ -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 diff --git a/sp/src/game/server/ai_baseactor.cpp b/sp/src/game/server/ai_baseactor.cpp index 09940829..248c0841 100644 --- a/sp/src/game/server/ai_baseactor.cpp +++ b/sp/src/game/server/ai_baseactor.cpp @@ -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; } diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index e62f7f66..4cf69736 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -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(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( 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; } diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index dd326d80..fe63f36e 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -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: diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index 0d715bf4..61fefb3f 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -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 ) { diff --git a/sp/src/game/server/ai_behavior.cpp b/sp/src/game/server/ai_behavior.cpp index 8822f750..4730e122 100644 --- a/sp/src/game/server/ai_behavior.cpp +++ b/sp/src/game/server/ai_behavior.cpp @@ -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 //------------------------------------- diff --git a/sp/src/game/server/ai_behavior.h b/sp/src/game/server/ai_behavior.h index ce26ca61..f4df0640 100644 --- a/sp/src/game/server/ai_behavior.h +++ b/sp/src/game/server/ai_behavior.h @@ -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::BackBridge_CanUnholsterWeapon( void ) { return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_ShouldPickADeathPose( void ) +{ + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_CanTranslateCrouchActivity( void ) +{ + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- @@ -1914,6 +1956,28 @@ inline bool CAI_BehaviorHost::CanUnholsterWeapon( void ) return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::ShouldPickADeathPose( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeShouldPickADeathPose(); + + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::CanTranslateCrouchActivity( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeCanTranslateCrouchActivity(); + + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- diff --git a/sp/src/game/server/ai_behavior_follow.cpp b/sp/src/game/server/ai_behavior_follow.cpp index bcd254a5..a2ab0cae 100644 --- a/sp/src/game/server/ai_behavior_follow.cpp +++ b/sp/src/game/server/ai_behavior_follow.cpp @@ -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 ), diff --git a/sp/src/game/server/ai_behavior_follow.h b/sp/src/game/server/ai_behavior_follow.h index 84cb0109..097588c6 100644 --- a/sp/src/game/server/ai_behavior_follow.h +++ b/sp/src/game/server/ai_behavior_follow.h @@ -71,6 +71,10 @@ public: int m_iFormation; +#ifdef MAPBASE + bool m_bNormalMemoryDiscard = false; +#endif + DECLARE_DATADESC(); }; diff --git a/sp/src/game/server/ai_behavior_standoff.cpp b/sp/src/game/server/ai_behavior_standoff.cpp index 0792df23..85740876 100644 --- a/sp/src/game/server/ai_behavior_standoff.cpp +++ b/sp/src/game/server/ai_behavior_standoff.cpp @@ -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 } } } diff --git a/sp/src/game/server/ai_behavior_standoff.h b/sp/src/game/server/ai_behavior_standoff.h index c08059e8..2de2a072 100644 --- a/sp/src/game/server/ai_behavior_standoff.h +++ b/sp/src/game/server/ai_behavior_standoff.h @@ -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: diff --git a/sp/src/game/server/ai_blended_movement.cpp b/sp/src/game/server/ai_blended_movement.cpp index d1b32d14..0a11c915 100644 --- a/sp/src/game/server/ai_blended_movement.cpp +++ b/sp/src/game/server/ai_blended_movement.cpp @@ -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 diff --git a/sp/src/game/server/ai_hint.cpp b/sp/src/game/server/ai_hint.cpp index 1f8ec4d1..2e8e90af 100644 --- a/sp/src/game/server/ai_hint.cpp +++ b/sp/src/game/server/ai_hint.cpp @@ -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 ) { diff --git a/sp/src/game/server/ai_hint.h b/sp/src/game/server/ai_hint.h index 60052ded..570f85ef 100644 --- a/sp/src/game/server/ai_hint.h +++ b/sp/src/game/server/ai_hint.h @@ -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 diff --git a/sp/src/game/server/ai_initutils.cpp b/sp/src/game/server/ai_initutils.cpp index 62d5c6ec..cd792798 100644 --- a/sp/src/game/server/ai_initutils.cpp +++ b/sp/src/game/server/ai_initutils.cpp @@ -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 // --------------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_initutils.h b/sp/src/game/server/ai_initutils.h index 22aaf5f3..70bd019c 100644 --- a/sp/src/game/server/ai_initutils.h +++ b/sp/src/game/server/ai_initutils.h @@ -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!) diff --git a/sp/src/game/server/ai_memory.cpp b/sp/src/game/server/ai_memory.cpp index 0af73a89..7ac69311 100644 --- a/sp/src/game/server/ai_memory.cpp +++ b/sp/src/game/server/ai_memory.cpp @@ -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) diff --git a/sp/src/game/server/ai_memory.h b/sp/src/game/server/ai_memory.h index faa482a2..d348c53e 100644 --- a/sp/src/game/server/ai_memory.h +++ b/sp/src/game/server/ai_memory.h @@ -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(); }; diff --git a/sp/src/game/server/ai_motor.cpp b/sp/src/game/server/ai_motor.cpp index 49f3d5f4..f54432ff 100644 --- a/sp/src/game/server/ai_motor.cpp +++ b/sp/src/game/server/ai_motor.cpp @@ -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 ) ); diff --git a/sp/src/game/server/ai_motor.h b/sp/src/game/server/ai_motor.h index d7f14293..29c05f84 100644 --- a/sp/src/game/server/ai_motor.h +++ b/sp/src/game/server/ai_motor.h @@ -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; } //--------------------------------- diff --git a/sp/src/game/server/ai_navigator.cpp b/sp/src/game/server/ai_navigator.cpp index d136c923..c58471d0 100644 --- a/sp/src/game/server/ai_navigator.cpp +++ b/sp/src/game/server/ai_navigator.cpp @@ -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 ) diff --git a/sp/src/game/server/ai_network.cpp b/sp/src/game/server/ai_network.cpp index 16d8a7fa..1323b7ce 100644 --- a/sp/src/game/server/ai_network.cpp +++ b/sp/src/game/server/ai_network.cpp @@ -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 ); diff --git a/sp/src/game/server/ai_networkmanager.cpp b/sp/src/game/server/ai_networkmanager.cpp index f366ee57..2477ec67 100644 --- a/sp/src/game/server/ai_networkmanager.cpp +++ b/sp/src/game/server/ai_networkmanager.cpp @@ -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" ); diff --git a/sp/src/game/server/ai_tacticalservices.cpp b/sp/src/game/server/ai_tacticalservices.cpp index 87399c4f..8bcdcc08 100644 --- a/sp/src/game/server/ai_tacticalservices.cpp +++ b/sp/src/game/server/ai_tacticalservices.cpp @@ -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() ) { diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index cc578df0..5d27044a 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -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(pEvent) ); // event ScriptVariant_t args[] = { hEvent }; diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index b45f96b6..4fb3c47c 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -388,6 +388,8 @@ private: void InputSetCycle( inputdata_t &inputdata ); void InputSetPlaybackRate( inputdata_t &inputdata ); + +public: // From Alien Swarm SDK #endif bool CanSkipAnimation( void ); diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 08d119dc..c3927156 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -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; + } } } diff --git a/sp/src/game/server/basecombatweapon.cpp b/sp/src/game/server/basecombatweapon.cpp index 1135aa82..e49c1c1f 100644 --- a/sp/src/game/server/basecombatweapon.cpp +++ b/sp/src/game/server/basecombatweapon.cpp @@ -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; } diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 0a964058..fd31f792 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -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 } } diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 8f92ba60..ab45b27a 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -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 ¶ms ); +#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; diff --git a/sp/src/game/server/baseflex.cpp b/sp/src/game/server/baseflex.cpp index 8da332cd..3c660a88 100644 --- a/sp/src/game/server/baseflex.cpp +++ b/sp/src/game/server/baseflex.cpp @@ -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(); diff --git a/sp/src/game/server/colorcorrection.cpp b/sp/src/game/server/colorcorrection.cpp index 16d01222..69e6db85 100644 --- a/sp/src/game/server/colorcorrection.cpp +++ b/sp/src/game/server/colorcorrection.cpp @@ -5,9 +5,8 @@ // $NoKeywords: $ //===========================================================================// -#include - #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( 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 diff --git a/sp/src/game/server/colorcorrection.h b/sp/src/game/server/colorcorrection.h new file mode 100644 index 00000000..2e96eb7c --- /dev/null +++ b/sp/src/game/server/colorcorrection.h @@ -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 +#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 diff --git a/sp/src/game/server/colorcorrectionvolume.cpp b/sp/src/game/server/colorcorrectionvolume.cpp index a56cd533..abc55d75 100644 --- a/sp/src/game/server/colorcorrectionvolume.cpp +++ b/sp/src/game/server/colorcorrectionvolume.cpp @@ -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() diff --git a/sp/src/game/server/env_screenoverlay.cpp b/sp/src/game/server/env_screenoverlay.cpp index 07bbf08d..259a6fdc 100644 --- a/sp/src/game/server/env_screenoverlay.cpp +++ b/sp/src/game/server/env_screenoverlay.cpp @@ -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 } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/env_tonemap_controller.cpp b/sp/src/game/server/env_tonemap_controller.cpp index bfd4f5a2..492b2046 100644 --- a/sp/src/game/server/env_tonemap_controller.cpp +++ b/sp/src/game/server/env_tonemap_controller.cpp @@ -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( 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 diff --git a/sp/src/game/server/env_tonemap_controller.h b/sp/src/game/server/env_tonemap_controller.h new file mode 100644 index 00000000..94e3e0f4 --- /dev/null +++ b/sp/src/game/server/env_tonemap_controller.h @@ -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 \ No newline at end of file diff --git a/sp/src/game/server/fogvolume.cpp b/sp/src/game/server/fogvolume.cpp new file mode 100644 index 00000000..f2f9a718 --- /dev/null +++ b/sp/src/game/server/fogvolume.cpp @@ -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; iCollisionProp()->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(); + } +} diff --git a/sp/src/game/server/fogvolume.h b/sp/src/game/server/fogvolume.h new file mode 100644 index 00000000..6bd5880a --- /dev/null +++ b/sp/src/game/server/fogvolume.h @@ -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 \ No newline at end of file diff --git a/sp/src/game/server/func_break.cpp b/sp/src/game/server/func_break.cpp index bb7dff50..fd4839ae 100644 --- a/sp/src/game/server/func_break.cpp +++ b/sp/src/game/server/func_break.cpp @@ -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 diff --git a/sp/src/game/server/hl2/ai_behavior_functank.cpp b/sp/src/game/server/hl2/ai_behavior_functank.cpp index f0a32601..89f79228 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.cpp +++ b/sp/src/game/server/hl2/ai_behavior_functank.cpp @@ -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 //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/ai_behavior_functank.h b/sp/src/game/server/hl2/ai_behavior_functank.h index a9f19496..75d5b8df 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.h +++ b/sp/src/game/server/hl2/ai_behavior_functank.h @@ -51,6 +51,8 @@ public: void PrescheduleThink(); #ifdef MAPBASE bool IsInterruptable( void ); + + bool CanManTank( CFuncTank *pTank, bool bForced ); #endif Activity NPC_TranslateActivity( Activity activity ); diff --git a/sp/src/game/server/hl2/cbasehelicopter.h b/sp/src/game/server/hl2/cbasehelicopter.h index fcf74291..714f62f4 100644 --- a/sp/src/game/server/hl2/cbasehelicopter.h +++ b/sp/src/game/server/hl2/cbasehelicopter.h @@ -267,6 +267,10 @@ private: typedef CHandle AvoidSphereHandle_t; float m_flRadius; +#ifdef MAPBASE + string_t m_iszAvoidFilter; + EHANDLE m_hAvoidFilter; +#endif static CUtlVector< AvoidSphereHandle_t > s_AvoidSpheres; }; diff --git a/sp/src/game/server/hl2/func_tank.cpp b/sp/src/game/server/hl2/func_tank.cpp index 963a0e54..c4462575 100644 --- a/sp/src/game/server/hl2/func_tank.cpp +++ b/sp/src/game/server/hl2/func_tank.cpp @@ -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(); diff --git a/sp/src/game/server/hl2/hl2_client.cpp b/sp/src/game/server/hl2/hl2_client.cpp index 4cd9dd7b..12dc248f 100644 --- a/sp/src/game/server/hl2/hl2_client.cpp +++ b/sp/src/game/server/hl2/hl2_client.cpp @@ -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(); diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index a43cfe32..3e70f4fa 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -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( 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(m_hPlayer.Get()); - pPlayer->m_bDrawPlayerModelExternally = inputdata.value.Bool(); + pPlayer->SetDrawPlayerModelExternally( inputdata.value.Bool() ); } #endif diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.cpp b/sp/src/game/server/hl2/npc_alyx_episodic.cpp index 42c2eb3f..9df7a3ee 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/sp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -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(); diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.h b/sp/src/game/server/hl2/npc_alyx_episodic.h index 6d4eb67b..f24ddf67 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.h +++ b/sp/src/game/server/hl2/npc_alyx_episodic.h @@ -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; diff --git a/sp/src/game/server/hl2/npc_attackchopper.cpp b/sp/src/game/server/hl2/npc_attackchopper.cpp index ddb9752a..ac6efb1c 100644 --- a/sp/src/game/server/hl2/npc_attackchopper.cpp +++ b/sp/src/game/server/hl2/npc_attackchopper.cpp @@ -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(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( 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; diff --git a/sp/src/game/server/hl2/npc_barnacle.cpp b/sp/src/game/server/hl2/npc_barnacle.cpp index 2ffca3bc..d26f702a 100644 --- a/sp/src/game/server/hl2/npc_barnacle.cpp +++ b/sp/src/game/server/hl2/npc_barnacle.cpp @@ -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; diff --git a/sp/src/game/server/hl2/npc_barney.cpp b/sp/src/game/server/hl2/npc_barney.cpp index 93ca92f0..cfb80afc 100644 --- a/sp/src/game/server/hl2/npc_barney.cpp +++ b/sp/src/game/server/hl2/npc_barney.cpp @@ -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" ); diff --git a/sp/src/game/server/hl2/npc_basescanner.cpp b/sp/src/game/server/hl2/npc_basescanner.cpp index a94c77b6..fdae50da 100644 --- a/sp/src/game/server/hl2/npc_basescanner.cpp +++ b/sp/src/game/server/hl2/npc_basescanner.cpp @@ -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 diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index 07ba67da..79ff9a0e 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -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(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(GetActiveWeapon()); - if ( EntIsClass( GetEnemy(), gm_isz_class_Gunship ) ) + pRPG->SetNPCLaserPosition( vecTarget ); + } #else CWeaponRPG *pRPG = dynamic_cast(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 } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_citizen17.h b/sp/src/game/server/hl2/npc_citizen17.h index 35f39a05..5d23429d 100644 --- a/sp/src/game/server/hl2/npc_citizen17.h +++ b/sp/src/game/server/hl2/npc_citizen17.h @@ -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 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: diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index 15c12c76..aedf23ac 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -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 ) diff --git a/sp/src/game/server/hl2/npc_combine.h b/sp/src/game/server/hl2/npc_combine.h index ec541cca..c9af8010 100644 --- a/sp/src/game/server/hl2/npc_combine.h +++ b/sp/src/game/server/hl2/npc_combine.h @@ -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 diff --git a/sp/src/game/server/hl2/npc_fastzombie.cpp b/sp/src/game/server/hl2/npc_fastzombie.cpp index 0e15e2a8..be53f344 100644 --- a/sp/src/game/server/hl2/npc_fastzombie.cpp +++ b/sp/src/game/server/hl2/npc_fastzombie.cpp @@ -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 ); } diff --git a/sp/src/game/server/hl2/npc_fisherman.cpp b/sp/src/game/server/hl2/npc_fisherman.cpp index 4530f261..967c5795 100644 --- a/sp/src/game/server/hl2/npc_fisherman.cpp +++ b/sp/src/game/server/hl2/npc_fisherman.cpp @@ -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" ); diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index bf56dc78..a8579647 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -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 ); diff --git a/sp/src/game/server/hl2/npc_metropolice.h b/sp/src/game/server/hl2/npc_metropolice.h index 1f3f9517..742de7e3 100644 --- a/sp/src/game/server/hl2/npc_metropolice.h +++ b/sp/src/game/server/hl2/npc_metropolice.h @@ -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; diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 8eec2e6d..97901f93 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -34,6 +34,7 @@ #ifdef MAPBASE #include "mapbase/GlobalStrings.h" #include "world.h" +#include "vehicle_base.h" #endif ConVar ai_debug_readiness("ai_debug_readiness", "0" ); @@ -60,6 +61,10 @@ int AE_COMPANION_RELEASE_FLARE; #define COMPANION_MELEE_DIST 64.0 #endif +#ifdef MAPBASE +ConVar ai_allow_new_weapons( "ai_allow_new_weapons", "1", FCVAR_NONE, "Allows companion NPCs to automatically pick up and use weapons they were unable pick up before, i.e. 357s or crossbows." ); +#endif + #define MAX_TIME_BETWEEN_BARRELS_EXPLODING 5.0f #define MAX_TIME_BETWEEN_CONSECUTIVE_PLAYER_KILLS 3.0f @@ -148,6 +153,7 @@ BEGIN_DATADESC( CNPC_PlayerCompanion ) #ifdef MAPBASE DEFINE_AIGRENADE_DATADESC() DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ), + DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ), #endif END_DATADESC() @@ -175,6 +181,19 @@ string_t CNPC_PlayerCompanion::gm_iszAR2Classname; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +CNPC_PlayerCompanion::CNPC_PlayerCompanion() +{ +#ifdef MAPBASE + if (ai_grenade_always_drop.GetBool()) + { + m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED); + } +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + bool CNPC_PlayerCompanion::CreateBehaviors() { #ifdef HL2_EPISODIC @@ -196,6 +215,10 @@ bool CNPC_PlayerCompanion::CreateBehaviors() AddBehavior( &m_FollowBehavior ); AddBehavior( &m_LeadBehavior ); #endif//HL2_EPISODIC + +#ifdef MAPBASE + AddBehavior( &m_FuncTankBehavior ); +#endif return BaseClass::CreateBehaviors(); } @@ -219,6 +242,11 @@ void CNPC_PlayerCompanion::Precache() #endif #endif +#ifdef MAPBASE + // Moved from Spawn() + SelectModel(); +#endif + PrecacheModel( STRING( GetModelName() ) ); #ifdef HL2_EPISODIC @@ -237,7 +265,9 @@ void CNPC_PlayerCompanion::Precache() //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::Spawn() { +#ifndef MAPBASE // Moved to Precache() SelectModel(); +#endif Precache(); @@ -1620,6 +1650,19 @@ Activity CNPC_PlayerCompanion::TranslateActivityReadiness( Activity activity ) continue; } +#ifdef MAPBASE + // If we don't have the readiness activity we selected and there's no backup activity available, break the loop and return the base act. + bool bRequired; + if ( !HaveSequenceForActivity( actremap.mappedActivity ) && !HaveSequenceForActivity( Weapon_TranslateActivity( actremap.mappedActivity, &bRequired ) ) ) + { + Activity backupAct = Weapon_BackupActivity( actremap.mappedActivity, bRequired ); + if ( backupAct != actremap.mappedActivity ) + return backupAct; + else + break; + } +#endif + // We've successfully passed all criteria for remapping this return actremap.mappedActivity; } @@ -1643,21 +1686,6 @@ Activity CNPC_PlayerCompanion::NPC_TranslateActivity( Activity activity ) activity = ACT_RUN_PROTECTED; } -#ifdef COMPANION_HOLSTER_WORKAROUND - if (activity == ACT_DISARM || activity == ACT_ARM) - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon() ? GetActiveWeapon() : m_hWeapons[m_iLastHolsteredWeapon]; - if (pWeapon && pWeapon->WeaponClassify() != WEPCLASS_HANDGUN) - { - switch (activity) - { - case ACT_DISARM: return ACT_DISARM_RIFLE; - case ACT_ARM: return ACT_ARM_RIFLE; - } - } - } -#endif - activity = BaseClass::NPC_TranslateActivity( activity ); if ( activity == ACT_IDLE ) @@ -2726,6 +2754,13 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); } +#ifdef MAPBASE + else if (EntIsClass( pWeapon, gm_isz_class_Pistol ) || EntIsClass( pWeapon, gm_isz_class_357 ) || EntIsClass( pWeapon, gm_isz_class_Crossbow )) + { + // The AI automatically detects these weapons as usable now that there's animations for them, so ensure this behavior can be toggled in situations where that's not desirable + return ai_allow_new_weapons.GetBool(); + } +#endif else { return true; @@ -4179,6 +4214,38 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak } #ifdef MAPBASE +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNPC_PlayerCompanion::Event_Killed( const CTakeDamageInfo &info ) +{ + // For now, allied player companions are set to always drop grenades and other items + // even if the player did not kill them + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (!IsPlayerAlly( pPlayer )) + { + pPlayer = ToBasePlayer( info.GetAttacker() ); + + // See if there's a player in a vehicle instead (from CNPC_CombineS) + if ( !pPlayer ) + { + CPropVehicleDriveable *pVehicle = dynamic_cast( info.GetAttacker() ) ; + if ( pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer() ) + { + pPlayer = assert_cast( pVehicle->GetDriver() ); + } + } + } + + if ( pPlayer != NULL ) + { + // Drop grenades if we should + DropGrenadeItemsOnDeath( info, pPlayer ); + } + + BaseClass::Event_Killed( info ); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) diff --git a/sp/src/game/server/hl2/npc_playercompanion.h b/sp/src/game/server/hl2/npc_playercompanion.h index 81ba080c..ce23058b 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.h +++ b/sp/src/game/server/hl2/npc_playercompanion.h @@ -23,6 +23,7 @@ #endif #ifdef MAPBASE +#include "ai_behavior_functank.h" #include "mapbase/ai_grenade.h" #endif @@ -108,8 +109,10 @@ class CNPC_PlayerCompanion : public CAI_PlayerAlly { DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly ); #endif - public: + + CNPC_PlayerCompanion(); + //--------------------------------- bool CreateBehaviors(); void Precache(); @@ -237,6 +240,7 @@ public: // This is just here to overwrite ai_playerally's TLK_ENEMY_DEAD virtual void OnKilledNPC(CBaseCombatCharacter *pKilled) {} + virtual void Event_Killed( const CTakeDamageInfo &info ); virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); virtual void DoCustomCombatAI( void ); #endif @@ -337,13 +341,18 @@ public: bool AllowReadinessValueChange( void ); #ifdef MAPBASE - 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(); } + private: // Determines whether this NPC is allowed to use grenades or alt-fire stuff. eGrenadeCapabilities m_iGrenadeCapabilities; + eGrenadeDropCapabilities m_iGrenadeDropCapabilities; #endif protected: @@ -424,6 +433,9 @@ protected: CAI_OperatorBehavior m_OperatorBehavior; CAI_PassengerBehaviorCompanion m_PassengerBehavior; CAI_FearBehavior m_FearBehavior; +#endif +#ifdef MAPBASE + CAI_FuncTankBehavior m_FuncTankBehavior; #endif //----------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_rollermine.cpp b/sp/src/game/server/hl2/npc_rollermine.cpp index ec9be919..fc803f1f 100644 --- a/sp/src/game/server/hl2/npc_rollermine.cpp +++ b/sp/src/game/server/hl2/npc_rollermine.cpp @@ -310,6 +310,16 @@ protected: bool IsActive() { return m_flActiveTime > gpGlobals->curtime ? false : true; } + inline float GetForwardSpeed() const + { +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + return m_flForwardSpeed * m_flSpeedModifier; + else +#endif + return m_flForwardSpeed; + } + // INPCInteractive Functions virtual bool CanInteractWith( CAI_BaseNPC *pUser ) { return true; } virtual bool HasBeenInteractedWith() { return m_bHackedByAlyx; } @@ -1313,7 +1323,7 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) Vector vecRight; AngleVectors( QAngle( 0, yaw, 0 ), NULL, &vecRight, NULL ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, -m_flForwardSpeed * 5 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, -GetForwardSpeed() * 5 ); TaskComplete(); return; @@ -1323,6 +1333,8 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) } { + float flForwardSpeed = GetForwardSpeed(); + float yaw = UTIL_VecToYaw( GetNavigator()->GetCurWaypointPos() - GetLocalOrigin() ); Vector vecRight; @@ -1362,17 +1374,17 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.y = -vecVelocity.x; vecCompensate.z = 0; - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); } if( m_bHackedByAlyx ) { // Move faster. - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * 2.0f ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * 2.0f ); } else { - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed ); } } break; @@ -1496,8 +1508,10 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.z = 0; VectorNormalize( vecCompensate ); - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * flTorqueFactor ); + float flForwardSpeed = GetForwardSpeed(); + + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * flTorqueFactor ); // Taunt when I get closer if( !(m_iSoundEventFlags & ROLLERMINE_SE_TAUNT) && UTIL_DistApprox( GetLocalOrigin(), vecTargetPosition ) <= 400 ) @@ -1615,8 +1629,10 @@ void CNPC_RollerMine::RunTask( const Task_t *pTask ) vecCompensate.z = 0; VectorNormalize( vecCompensate ); - m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 ); - m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * flTorqueFactor ); + float flForwardSpeed = GetForwardSpeed(); + + m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, flForwardSpeed * -0.75 ); + m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, flForwardSpeed * flTorqueFactor ); // Once we're near the player, slow & stop if ( GetAbsOrigin().DistToSqr( vecTargetPosition ) < (ROLLERMINE_RETURN_TO_PLAYER_DIST*2.0) ) @@ -2572,6 +2588,12 @@ float CNPC_RollerMine::RollingSpeed() float rollingSpeed = angVel.Length() - 90; rollingSpeed = clamp( rollingSpeed, 1, MAX_ROLLING_SPEED ); rollingSpeed *= (1/MAX_ROLLING_SPEED); +#ifdef MAPBASE + if (m_flSpeedModifier != 1.0f) + { + rollingSpeed *= m_flSpeedModifier; + } +#endif return rollingSpeed; } return 0; diff --git a/sp/src/game/server/hl2/npc_scanner.cpp b/sp/src/game/server/hl2/npc_scanner.cpp index aac717c5..0323afcc 100644 --- a/sp/src/game/server/hl2/npc_scanner.cpp +++ b/sp/src/game/server/hl2/npc_scanner.cpp @@ -2567,6 +2567,14 @@ void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget 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 diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 8dffd6db..839c18d9 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -582,8 +582,11 @@ bool CNPC_Vortigaunt::InnateWeaponLOSCondition( const Vector &ownerPos, const Ve UTIL_PredictedPosition( GetEnemy(), flTimeDelta, &vecNewTargetPos ); #ifdef MAPBASE - // This fix was created by DKY. - // His original comment is below. + // There's apparently a null pointer crash here + if (!GetEnemy()) + return false; + + // The fix below and its accompanying comment were created by DKY. /* @@ -2715,6 +2718,15 @@ void CNPC_Vortigaunt::OnSquishedGrub( const CBaseEntity *pGrub ) //----------------------------------------------------------------------------- void CNPC_Vortigaunt::AimGun( void ) { +#ifdef MAPBASE + // Use base for func_tank + if (m_FuncTankBehavior.IsRunning()) + { + BaseClass::AimGun(); + return; + } +#endif + // If our aim lock is on, don't bother if ( m_flAimDelay >= gpGlobals->curtime ) return; diff --git a/sp/src/game/server/hl2/proto_sniper.cpp b/sp/src/game/server/hl2/proto_sniper.cpp index 64f11d2a..84eba6df 100644 --- a/sp/src/game/server/hl2/proto_sniper.cpp +++ b/sp/src/game/server/hl2/proto_sniper.cpp @@ -250,6 +250,10 @@ public: virtual int SelectSchedule( void ); virtual int TranslateSchedule( int scheduleType ); +#ifdef MAPBASE + Activity NPC_TranslateActivity( Activity eNewActivity ); +#endif + bool KeyValue( const char *szKeyName, const char *szValue ); void PrescheduleThink( void ); @@ -2036,6 +2040,23 @@ int CProtoSniper::TranslateSchedule( int scheduleType ) return BaseClass::TranslateSchedule( scheduleType ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CProtoSniper::NPC_TranslateActivity( Activity eNewActivity ) +{ + // ACT_IDLE is now just the soldier's unarmed idle animation. + // Use a gun-holding animation like what unhidden snipers were using before. + if (!HasSpawnFlags( SF_SNIPER_HIDDEN ) && eNewActivity == ACT_IDLE) + { + eNewActivity = ACT_IDLE_SMG1; + } + + return BaseClass::NPC_TranslateActivity( eNewActivity ); +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CProtoSniper::ScopeGlint() diff --git a/sp/src/game/server/hl2/weapon_357.cpp b/sp/src/game/server/hl2/weapon_357.cpp index 3fa3fab8..8b4fe93f 100644 --- a/sp/src/game/server/hl2/weapon_357.cpp +++ b/sp/src/game/server/hl2/weapon_357.cpp @@ -27,6 +27,11 @@ // CWeapon357 //----------------------------------------------------------------------------- +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeapon357 : public CBaseHLCombatWeapon { DECLARE_CLASS( CWeapon357, CBaseHLCombatWeapon ); @@ -69,6 +74,9 @@ public: void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } #endif DECLARE_SERVERCLASS(); @@ -91,6 +99,22 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeapon357::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_REVOLVER, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_REVOLVER, true }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD, ACT_RELOAD_REVOLVER, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_REVOLVER, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_REVOLVER, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_REVOLVER_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_REVOLVER_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_REVOLVER_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_REVOLVER_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_REVOLVER, false }, + { ACT_WALK, ACT_WALK_REVOLVER, true }, + { ACT_RUN, ACT_RUN_REVOLVER, true }, +#else { ACT_IDLE, ACT_IDLE_PISTOL, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, @@ -105,6 +129,75 @@ acttable_t CWeapon357::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, { ACT_WALK, ACT_WALK_PISTOL, false }, { ACT_RUN, ACT_RUN_PISTOL, false }, +#endif + + // + // Activities ported from weapon_alyxgun below + // + + // Readiness activities (not aiming) +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else + { ACT_WALK_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else + { ACT_RUN_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false }, + { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, + { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_REVOLVER_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_REVOLVER_MED, false }, +#endif }; diff --git a/sp/src/game/server/hl2/weapon_alyxgun.cpp b/sp/src/game/server/hl2/weapon_alyxgun.cpp index 270a503c..538f6312 100644 --- a/sp/src/game/server/hl2/weapon_alyxgun.cpp +++ b/sp/src/game/server/hl2/weapon_alyxgun.cpp @@ -37,20 +37,40 @@ acttable_t CWeaponAlyxGun::m_acttable[] = { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, true }, { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, true }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, true }, +#ifdef MAPBASE + // For non-Alyx NPCs + { ACT_WALK, ACT_WALK_PISTOL, false }, + { ACT_RUN, ACT_RUN_PISTOL, false }, +#endif // Readiness activities (not aiming) +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else { ACT_WALK_RELAXED, ACT_WALK, false },//never aims { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, - + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else { ACT_RUN_RELAXED, ACT_RUN, false },//never aims { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, diff --git a/sp/src/game/server/hl2/weapon_alyxgun.h b/sp/src/game/server/hl2/weapon_alyxgun.h index cc3e862c..4dd95532 100644 --- a/sp/src/game/server/hl2/weapon_alyxgun.h +++ b/sp/src/game/server/hl2/weapon_alyxgun.h @@ -13,6 +13,11 @@ #pragma once #endif +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeaponAlyxGun : public CHLSelectFireMachineGun { DECLARE_DATADESC(); @@ -51,6 +56,11 @@ public: SetTouch(NULL); } +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } +#endif + float m_flTooCloseTimer; DECLARE_ACTTABLE(); diff --git a/sp/src/game/server/hl2/weapon_ar2.cpp b/sp/src/game/server/hl2/weapon_ar2.cpp index 89a16700..e6a32e93 100644 --- a/sp/src/game/server/hl2/weapon_ar2.cpp +++ b/sp/src/game/server/hl2/weapon_ar2.cpp @@ -38,10 +38,6 @@ ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" ); ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "2" ); ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" ); -#ifdef MAPBASE -extern acttable_t *GetSMG1Acttable(); -#endif - //========================================================= //========================================================= @@ -100,14 +96,14 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, { ACT_RUN, ACT_RUN_AR2, true }, - { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR2, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR2, false }, - { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_AR2_LOW, true }, { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false }, { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR2_LOW, false }, - { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_AR2_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #else @@ -160,6 +156,21 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR2_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR2_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR2); diff --git a/sp/src/game/server/hl2/weapon_citizenpackage.cpp b/sp/src/game/server/hl2/weapon_citizenpackage.cpp index cdb9378a..3d82e46c 100644 --- a/sp/src/game/server/hl2/weapon_citizenpackage.cpp +++ b/sp/src/game/server/hl2/weapon_citizenpackage.cpp @@ -23,6 +23,13 @@ acttable_t CWeaponCitizenPackage::m_acttable[] = { { ACT_IDLE, ACT_IDLE_PACKAGE, false }, { ACT_WALK, ACT_WALK_PACKAGE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_PACKAGE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_PACKAGE, false }, + { ACT_WALK_AIM, ACT_WALK_PACKAGE, false }, + { ACT_RUN_AIM, ACT_RUN_PACKAGE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenPackage); @@ -70,5 +77,12 @@ acttable_t CWeaponCitizenSuitcase::m_acttable[] = { { ACT_IDLE, ACT_IDLE_SUITCASE, false }, { ACT_WALK, ACT_WALK_SUITCASE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_SUITCASE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_SUITCASE, false }, + { ACT_WALK_AIM, ACT_WALK_SUITCASE, false }, + { ACT_RUN_AIM, ACT_RUN_SUITCASE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenSuitcase); diff --git a/sp/src/game/server/hl2/weapon_crossbow.cpp b/sp/src/game/server/hl2/weapon_crossbow.cpp index b86b4c05..1ce3329c 100644 --- a/sp/src/game/server/hl2/weapon_crossbow.cpp +++ b/sp/src/game/server/hl2/weapon_crossbow.cpp @@ -542,7 +542,7 @@ public: virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); virtual bool Reload( void ); #ifdef MAPBASE - virtual void Reload_NPC( void ); + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); @@ -638,6 +638,15 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeaponCrossbow::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_CROSSBOW, true }, + { ACT_RELOAD, ACT_RELOAD_CROSSBOW, true }, + { ACT_IDLE, ACT_IDLE_CROSSBOW, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_CROSSBOW, true }, + + { ACT_WALK, ACT_WALK_CROSSBOW, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true }, +#else { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, { ACT_IDLE, ACT_IDLE_SMG1, true }, @@ -645,6 +654,7 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_WALK, ACT_WALK_RIFLE, true }, { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, +#endif // Readiness activities (not aiming) { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims @@ -673,6 +683,24 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims //End readiness activities +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_CROSSBOW, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_CROSSBOW, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_CROSSBOW, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_CROSSBOW_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_CROSSBOW_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_CROSSBOW_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_CROSSBOW_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_CROSSBOW, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#else { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, @@ -686,6 +714,12 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_CROSSBOW_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_CROSSBOW_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrossbow); @@ -785,11 +819,15 @@ bool CWeaponCrossbow::Reload( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CWeaponCrossbow::Reload_NPC( void ) +void CWeaponCrossbow::Reload_NPC( bool bPlaySound ) { - BaseClass::Reload_NPC(); + BaseClass::Reload_NPC( bPlaySound ); - m_nSkin = 0; + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1) + SetBodygroup( iBody, 0 ); + else + m_nSkin = 0; } #endif @@ -939,7 +977,11 @@ void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, m_iClip1--; - m_nSkin = 1; + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1) + SetBodygroup( iBody, 1 ); + else + m_nSkin = 1; WeaponSound( SINGLE_NPC ); WeaponSound( SPECIAL2 ); diff --git a/sp/src/game/server/hl2/weapon_crowbar.cpp b/sp/src/game/server/hl2/weapon_crowbar.cpp index fdbc7943..8a0daacf 100644 --- a/sp/src/game/server/hl2/weapon_crowbar.cpp +++ b/sp/src/game/server/hl2/weapon_crowbar.cpp @@ -42,6 +42,13 @@ acttable_t CWeaponCrowbar::m_acttable[] = { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, + + { ACT_ARM, ACT_ARM_MELEE, false }, + { ACT_DISARM, ACT_DISARM_MELEE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrowbar); diff --git a/sp/src/game/server/hl2/weapon_crowbar.h b/sp/src/game/server/hl2/weapon_crowbar.h index 1890ef6e..26bb961f 100644 --- a/sp/src/game/server/hl2/weapon_crowbar.h +++ b/sp/src/game/server/hl2/weapon_crowbar.h @@ -46,6 +46,12 @@ public: // Animation event virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + private: // Animation event handlers void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index 80bb5090..a04d30b6 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -108,6 +108,12 @@ public: return 0.5f; } +#ifdef MAPBASE + // Pistols are their own backup activities + virtual acttable_t *GetBackupActivityList() { return NULL; } + virtual int GetBackupActivityListCount() { return 0; } +#endif + DECLARE_ACTTABLE(); private: @@ -149,11 +155,126 @@ acttable_t CWeaponPistol::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, { ACT_WALK, ACT_WALK_PISTOL, false }, { ACT_RUN, ACT_RUN_PISTOL, false }, + +#ifdef MAPBASE + // + // Activities ported from weapon_alyxgun below + // + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_PISTOL_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities +#else + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + + { ACT_RUN_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities +#endif + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false }, + { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, + { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, +#endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_PISTOL, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_PISTOL, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_PISTOL, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_PISTOL, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_PISTOL_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_PISTOL_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_PISTOL, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_PISTOL, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_PISTOL, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_PISTOL, false }, +#endif }; IMPLEMENT_ACTTABLE( CWeaponPistol ); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the pistol's activity table. +acttable_t *GetPistolActtable() +{ + return CWeaponPistol::m_acttable; +} + +int GetPistolActtableCount() +{ + return ARRAYSIZE(CWeaponPistol::m_acttable); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/weapon_rpg.cpp b/sp/src/game/server/hl2/weapon_rpg.cpp index 3ae41e28..2d20211d 100644 --- a/sp/src/game/server/hl2/weapon_rpg.cpp +++ b/sp/src/game/server/hl2/weapon_rpg.cpp @@ -1399,6 +1399,11 @@ PRECACHE_WEAPON_REGISTER(weapon_rpg); acttable_t CWeaponRPG::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_RPG_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_RPG_LOW, false }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_RPG, false }, +#endif { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true }, { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true }, @@ -1411,6 +1416,16 @@ acttable_t CWeaponRPG::m_acttable[] = { ACT_RUN, ACT_RUN_RPG, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true }, { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RPG, false }, + { ACT_DISARM, ACT_DISARM_RPG, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_RPG_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_RPG_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponRPG); diff --git a/sp/src/game/server/hl2/weapon_shotgun.cpp b/sp/src/game/server/hl2/weapon_shotgun.cpp index fb9ec215..e0b2e336 100644 --- a/sp/src/game/server/hl2/weapon_shotgun.cpp +++ b/sp/src/game/server/hl2/weapon_shotgun.cpp @@ -108,6 +108,57 @@ END_DATADESC() acttable_t CWeaponShotgun::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Note that ACT_IDLE_SHOTGUN_AGITATED seems to be a stand-in for ACT_IDLE_SHOTGUN on citizens, + // but that isn't acceptable for NPCs which don't use readiness activities. + { ACT_IDLE, ACT_IDLE_SHOTGUN, true }, + + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false }, + { ACT_WALK, ACT_WALK_SHOTGUN, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_SHOTGUN, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, + { ACT_COVER_LOW, ACT_COVER_SHOTGUN_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SHOTGUN_LOW, false }, +#else { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, @@ -153,6 +204,17 @@ acttable_t CWeaponShotgun::m_acttable[] = { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, +#endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_SHOTGUN, true }, + { ACT_DISARM, ACT_DISARM_SHOTGUN, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SHOTGUN_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SHOTGUN_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponShotgun); diff --git a/sp/src/game/server/hl2/weapon_smg1.cpp b/sp/src/game/server/hl2/weapon_smg1.cpp index 97b0e89e..04f7e390 100644 --- a/sp/src/game/server/hl2/weapon_smg1.cpp +++ b/sp/src/game/server/hl2/weapon_smg1.cpp @@ -134,6 +134,21 @@ acttable_t CWeaponSMG1::m_acttable[] = { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SMG1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SMG1_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponSMG1); diff --git a/sp/src/game/server/items.h b/sp/src/game/server/items.h index 53f440e8..6209c15e 100644 --- a/sp/src/game/server/items.h +++ b/sp/src/game/server/items.h @@ -89,9 +89,9 @@ public: #endif #ifdef MAPBASE - // This is in CBaseEntity, but I can't find a use for it anywhere. - // It may have been originally intended for TF2 or some other game-specific item class. Please remove this if it turns out to be something important. - virtual bool IsCombatItem() { return true; } + // This appeared to have no prior use in Source SDK 2013. + // It may have been originally intended for TF2 or some other game-specific item class. + virtual bool IsCombatItem() const { return true; } // Used to access item_healthkit values, etc. from outside of the class virtual float GetItemAmount() { return 1.0f; } diff --git a/sp/src/game/server/mapbase/ai_grenade.cpp b/sp/src/game/server/mapbase/ai_grenade.cpp index e14b47a3..a9ff5302 100644 --- a/sp/src/game/server/mapbase/ai_grenade.cpp +++ b/sp/src/game/server/mapbase/ai_grenade.cpp @@ -11,3 +11,5 @@ int COMBINE_AE_BEGIN_ALTFIRE; int COMBINE_AE_ALTFIRE; + +ConVar ai_grenade_always_drop( "ai_grenade_always_drop", "0", FCVAR_NONE, "Causes non-Combine grenade user NPCs to be allowed to drop grenades, alt-fire items, etc. *IF* the keyvalue has not already been set in Hammer. This is useful for debugging purposes or if a mod which was developed before this feature was introduced wants its NPCs to drop grenades and beyond without recompiling all of the maps." ); diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index 7f1ea8c1..bfcbd4de 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -19,6 +19,9 @@ #include "basegrenade_shared.h" #include "ai_squad.h" #include "GlobalStrings.h" +#include "gameweaponmanager.h" +#include "hl2_gamerules.h" +#include "weapon_physcannon.h" #define COMBINE_AE_GREN_TOSS ( 7 ) @@ -36,7 +39,9 @@ DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ), \ DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), \ DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \ + DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), \ DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \ + DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeGestureAtTarget", InputThrowGrenadeGestureAtTarget ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \ DEFINE_OUTPUT(m_OnThrowGrenade, "OnThrowGrenade"), \ @@ -61,12 +66,22 @@ extern int COMBINE_AE_BEGIN_ALTFIRE; extern int COMBINE_AE_ALTFIRE; +extern ConVar ai_grenade_always_drop; + enum eGrenadeCapabilities { GRENCAP_GRENADE = (1 << 0), GRENCAP_ALTFIRE = (1 << 1), }; +// What grenade/item types NPCs are capable of dropping +enum eGrenadeDropCapabilities +{ + GRENDROPCAP_GRENADE = (1 << 0), + GRENDROPCAP_ALTFIRE = (1 << 1), + GRENDROPCAP_INTERRUPTED = (1 << 2), // Drops grenades when interrupted mid-animation +}; + //----------------------------------------------------------------------------- // Other classes can use this and access some CAI_GrenadeUser functions. //----------------------------------------------------------------------------- @@ -102,17 +117,20 @@ public: m_OnOutOfGrenades.Set( pLastGrenade, pLastGrenade, this ); } - virtual bool IsAltFireCapable() { return false; } + // Use secondary ammo as a way of checking if this is a weapon which can be alt-fired (e.g. AR2 or SMG) + virtual bool IsAltFireCapable() { return (GetActiveWeapon() && GetActiveWeapon()->UsesSecondaryAmmo()); } virtual bool IsGrenadeCapable() { return true; } inline bool HasGrenades() { return m_iNumGrenades > 0; } void InputSetGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() - m_iNumGrenades ); } void InputAddGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() ); } void InputThrowGrenadeAtTarget( inputdata_t &inputdata ); + void InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata ); virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; } void HandleAnimEvent( animevent_t *pEvent ); + void SetActivity( Activity NewActivity ); // Soldiers use "lefthand", cops use "LHand", and citizens use "anim_attachment_LH" virtual const char* GetGrenadeAttachment() { return "anim_attachment_LH"; } @@ -131,6 +149,12 @@ public: // For OnThrowGrenade + point_entity_replace, see grenade_frag.cpp bool UsingOnThrowGrenade() { return m_OnThrowGrenade.NumberOfElements() > 0; } + // For dropping grenades and beyond + void DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer ); + virtual bool ShouldDropGrenades() { return HasGrenades(); } + virtual bool ShouldDropInterruptedGrenades() { return true; } + virtual bool ShouldDropAltFire() { return HasGrenades(); } + protected: void StartTask_FaceAltFireTarget( const Task_t *pTask ); @@ -151,6 +175,9 @@ protected: // We can't have any private saved variables because only derived cla Vector m_vecAltFireTarget; Vector m_vecTossVelocity; + // CNPC_Combine port for determining if we tossed a grenade + int m_iLastAnimEventHandled; + COutputEHANDLE m_OnThrowGrenade; COutputEHANDLE m_OnOutOfGrenades; }; @@ -166,6 +193,8 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) if (this->GetActiveWeapon()) this->GetActiveWeapon()->WeaponSound( SPECIAL1 ); + m_iLastAnimEventHandled = pEvent->event; + //SpeakIfAllowed( TLK_CMB_THROWGRENADE, "altfire:1" ); return; } @@ -185,6 +214,8 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) AddGrenades(-1); + m_iLastAnimEventHandled = pEvent->event; + return; } @@ -221,12 +252,25 @@ void CAI_GrenadeUser::HandleAnimEvent( animevent_t *pEvent ) // wait six seconds before even looking again to see if a grenade can be thrown. m_flNextGrenadeCheck = gpGlobals->curtime + 6; + + m_iLastAnimEventHandled = pEvent->event; + return; } BaseClass::HandleAnimEvent( pEvent ); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::SetActivity( Activity NewActivity ) +{ + BaseClass::SetActivity( NewActivity ); + + m_iLastAnimEventHandled = -1; +} + //----------------------------------------------------------------------------- // Purpose: Force the combine soldier to throw a grenade at the target // If I'm a combine elite, fire my combine ball at the target instead. @@ -252,6 +296,63 @@ void CAI_GrenadeUser::InputThrowGrenadeAtTarget( inputdata_t &inputdat this->ClearSchedule( "Told to throw grenade via input" ); } +//----------------------------------------------------------------------------- +// Purpose: Force the combine soldier to throw a grenade at the target using the gesture animation. +// If I'm a combine elite, fire my combine ball at the target instead. +// Input : &inputdata - +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata ) +{ + // Ignore if we're inside a scripted sequence + //if ( this->GetState() == NPC_STATE_SCRIPT && this->m_hCine ) + // return; + + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); + if ( !pEntity ) + { + DevMsg("%s (%s) received ThrowGrenadeGestureAtTarget input, but couldn't find target entity '%s'\n", this->GetClassname(), this->GetDebugName(), inputdata.value.String() ); + return; + } + + m_hForcedGrenadeTarget = pEntity; + m_flNextGrenadeCheck = 0; + + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + +#ifdef SHARED_COMBINE_ACTIVITIES + if (IsAltFireCapable()) + { + if (FVisible( m_hForcedGrenadeTarget )) + { + m_vecAltFireTarget = vecTarget; + m_hForcedGrenadeTarget = NULL; + + int iLayer = AddGesture( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } + else + { + // If we can, throw a grenade at the target. + // Ignore grenade count / distance / etc + if (CheckCanThrowGrenade( vecTarget )) + { + int iLayer = AddGesture( ACT_GESTURE_COMBINE_THROW_GRENADE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } +#else + Warning("Gesture grenades/alt-fire not supported\n"); +#endif +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template @@ -534,6 +635,105 @@ void CAI_GrenadeUser::ClearAttackConditions() } } +//----------------------------------------------------------------------------- +// Purpose: Drops grenades and alt-fire items on death. Based on code from npc_combines.cpp and npc_combine.cpp +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer ) +{ + // Elites drop alt-fire ammo, so long as they weren't killed by dissolving. + if( IsAltFireCapable() && ShouldDropAltFire() ) + { + CBaseEntity *pItem; + if (this->GetActiveWeapon() && FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" )) + pItem = this->DropItem( "item_ammo_smg1_grenade", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + else + pItem = this->DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter() + RandomVector( -4, 4 ), RandomAngle( 0, 360 ) ); + + if ( pItem ) + { + IPhysicsObject *pObj = pItem->VPhysicsGetObject(); + + if ( pObj ) + { + Vector vel = RandomVector( -64.0f, 64.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pObj->AddVelocity( &vel, &angImp ); + } + + if( info.GetDamageType() & DMG_DISSOLVE ) + { + CBaseAnimating *pAnimating = dynamic_cast(pItem); + + if( pAnimating ) + { + pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + else + { + WeaponManager_AddManaged( pItem ); + } + } + } + + if ( IsGrenadeCapable() ) + { + if ( ShouldDropGrenades() ) + { + CHalfLife2 *pHL2GameRules = static_cast(g_pGameRules); + + // Attempt to drop a grenade + if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) ) + { + this->DropItem( "weapon_frag", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + pHL2GameRules->NPC_DroppedGrenade(); + } + } + + // if I was killed before I could finish throwing my grenade, drop + // a grenade item that the player can retrieve. + if (GetActivity() == ACT_RANGE_ATTACK2 && ShouldDropInterruptedGrenades()) + { + if( m_iLastAnimEventHandled != COMBINE_AE_GREN_TOSS ) + { + // Drop the grenade as an item. + Vector vecStart; + this->GetAttachment( GetGrenadeAttachment(), vecStart ); + + CBaseEntity *pItem = DropItem( "weapon_frag", vecStart, RandomAngle(0,360) ); + + if ( pItem ) + { + IPhysicsObject *pObj = pItem->VPhysicsGetObject(); + + if ( pObj ) + { + Vector vel; + vel.x = random->RandomFloat( -100.0f, 100.0f ); + vel.y = random->RandomFloat( -100.0f, 100.0f ); + vel.z = random->RandomFloat( 800.0f, 1200.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pObj->AddVelocity( &vel, &angImp ); + } + + // In the Citadel we need to dissolve this + if ( PlayerHasMegaPhysCannon() && GlobalEntity_GetCounter("super_phys_gun") != 1 ) + { + CBaseCombatWeapon *pWeapon = static_cast(pItem); + + pWeapon->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + } + } + } +} + //----------------------------------------------------------------------------- // Purpose: Task helpers //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/mapbase/func_fake_worldportal.cpp b/sp/src/game/server/mapbase/func_fake_worldportal.cpp index 3184d2fa..6430d5df 100644 --- a/sp/src/game/server/mapbase/func_fake_worldportal.cpp +++ b/sp/src/game/server/mapbase/func_fake_worldportal.cpp @@ -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) // //===========================================================================// @@ -54,6 +54,7 @@ public: void InputSetSkyMode( inputdata_t &inputdata ) { m_iSkyMode = inputdata.value.Int(); } void InputSetRenderTarget( inputdata_t &inputdata ) { m_iszRenderTarget = inputdata.value.StringID(); } void InputSetFogController( inputdata_t &inputdata ) { m_hFogController = inputdata.value.Entity(); if (m_hFogController) { m_iszFogController = m_hFogController->GetEntityName(); } } + void InputSetScale( inputdata_t &inputdata ) { m_flScale = inputdata.value.Float(); } private: @@ -83,6 +84,7 @@ BEGIN_DATADESC( CFuncFakeWorldPortal ) DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSkyMode", InputSetSkyMode ), DEFINE_INPUTFUNC( FIELD_STRING, "SetRenderTarget", InputSetRenderTarget ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetFogController", InputSetFogController ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetScale", InputSetScale ), END_DATADESC() diff --git a/sp/src/game/server/mapbase/logic_substring.cpp b/sp/src/game/server/mapbase/logic_substring.cpp new file mode 100644 index 00000000..7c7d153b --- /dev/null +++ b/sp/src/game/server/mapbase/logic_substring.cpp @@ -0,0 +1,110 @@ +//====================== By Holly Liberatore / MoofEMP ======================// +// +// Purpose: Takes a string parameter and returns a substring defined by keyvalues +// +//===========================================================================// + +#include "cbase.h" + +#define SF_SUBSTRING_START_DISABLED (1 << 0) + +class CLogicSubstring : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicSubstring, CLogicalEntity ); + DECLARE_DATADESC(); + + CLogicSubstring( void ) { } + + void InputDisable( inputdata_t &inputData ); + void InputEnable( inputdata_t &inputData ); + void InputInValue( inputdata_t &inputData ); + void InputSetLength( inputdata_t &inputData ); + void InputSetStartPos( inputdata_t &inputData ); + + void Spawn(void); + +private: + int m_nLength; + int m_nStartPos; + + bool m_bEnabled; + + COutputString m_OutValue; +}; + +LINK_ENTITY_TO_CLASS( logic_substring, CLogicSubstring ); + +BEGIN_DATADESC( CLogicSubstring ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_KEYFIELD(m_nLength, FIELD_INTEGER, "length" ), + DEFINE_KEYFIELD(m_nStartPos, FIELD_INTEGER, "startPos" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_STRING, "InValue", InputInValue ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetLength", InputSetLength ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetStartPos", InputSetStartPos ), + + DEFINE_OUTPUT( m_OutValue, "OutValue" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Disable or enable the entity (disabling prevents any input functions from running) +//----------------------------------------------------------------------------- +void CLogicSubstring::InputDisable( inputdata_t &inputData ) { m_bEnabled = false; } +void CLogicSubstring::InputEnable ( inputdata_t &inputData ) { m_bEnabled = true ; } + +//----------------------------------------------------------------------------- +// Purpose: Trim substring from input +// Output: Substring +//----------------------------------------------------------------------------- +void CLogicSubstring::InputInValue( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + int inputLength = Q_strlen(inputData.value.String()); + int startPosCheck = m_nStartPos < 0 ? inputLength + m_nStartPos : m_nStartPos; + if( startPosCheck < 0 ) + { + startPosCheck = 0; + } + int lengthCheck = (m_nLength < 0 || m_nLength > inputLength - startPosCheck ? inputLength - startPosCheck : m_nLength) + 1; + if( lengthCheck < 1 || startPosCheck > inputLength ) + { + m_OutValue.Set( MAKE_STRING(""), inputData.pActivator, this ); + return; + } + char* strOutValue = (char*)malloc( lengthCheck ); + Q_strncpy( strOutValue, inputData.value.String() + startPosCheck, lengthCheck ); + m_OutValue.Set( AllocPooledString(strOutValue), inputData.pActivator, this ); + free(strOutValue); +} + +//----------------------------------------------------------------------------- +// Purpose: Setter methods for keyvalues +//----------------------------------------------------------------------------- +void CLogicSubstring::InputSetLength( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + m_nLength = inputData.value.Int(); +} + +void CLogicSubstring::InputSetStartPos( inputdata_t &inputData ) +{ + if( !m_bEnabled ) return; + + m_nStartPos = inputData.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to spawnflags when entity spawns +//----------------------------------------------------------------------------- +void CLogicSubstring::Spawn( void ) +{ + m_bEnabled = !HasSpawnFlags( SF_SUBSTRING_START_DISABLED ); +} diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index 7efac526..524b1e75 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -59,6 +59,10 @@ #include "env_zoom.h" #include "rumble_shared.h" #include "gamestats.h" +#ifdef MAPBASE // From Alien Swarm SDK +#include "env_tonemap_controller.h" +#include "fogvolume.h" +#endif #include "npcevent.h" #include "datacache/imdlcache.h" #include "hintsystem.h" @@ -459,8 +463,13 @@ BEGIN_DATADESC( CBasePlayer ) // Inputs DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetHUDVisibility", InputSetHUDVisibility ), - DEFINE_INPUTFUNC( FIELD_STRING, "SetFogController", InputSetFogController ), +#ifdef MAPBASE // From Alien Swarm SDK (kind of) + DEFINE_INPUTFUNC( FIELD_INPUT, "SetFogController", InputSetFogController ), DEFINE_INPUTFUNC( FIELD_INPUT, "SetPostProcessController", InputSetPostProcessController ), + DEFINE_INPUTFUNC( FIELD_INPUT, "SetColorCorrectionController", InputSetColorCorrectionController ), +#else + DEFINE_INPUTFUNC( FIELD_STRING, "SetFogController", InputSetFogController ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "HandleMapEvent", InputHandleMapEvent ), #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetSuppressAttacks", InputSetSuppressAttacks ), @@ -474,9 +483,10 @@ BEGIN_DATADESC( CBasePlayer ) DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ), +#ifdef MAPBASE // From Alien Swarm SDK DEFINE_FIELD( m_hPostProcessCtrl, FIELD_EHANDLE ), - - + DEFINE_FIELD( m_hColorCorrectionCtrl, FIELD_EHANDLE ), +#endif // DEFINE_FIELD( m_nBodyPitchPoseParam, FIELD_INTEGER ), // DEFINE_ARRAY( m_StepSoundCache, StepSoundCache_t, 2 ), @@ -3831,7 +3841,7 @@ void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) // Movement hook for VScript if (m_ScriptScope.IsInitialized() && g_Hook_PlayerRunCommand.CanRunInScope(m_ScriptScope)) { - HSCRIPT hCmd = g_pScriptVM->RegisterInstance( ucmd ); + HSCRIPT hCmd = g_pScriptVM->RegisterInstance( reinterpret_cast(ucmd) ); // command ScriptVariant_t args[] = { hCmd }; @@ -4668,6 +4678,55 @@ void CBasePlayer::ForceOrigin( const Vector &vecOrigin ) m_vForcedOrigin = vecOrigin; } +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::OnTonemapTriggerStartTouch( CTonemapTrigger *pTonemapTrigger ) +{ + m_hTriggerTonemapList.FindAndRemove( pTonemapTrigger ); + m_hTriggerTonemapList.AddToTail( pTonemapTrigger ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::OnTonemapTriggerEndTouch( CTonemapTrigger *pTonemapTrigger ) +{ + m_hTriggerTonemapList.FindAndRemove( pTonemapTrigger ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::UpdateTonemapController( void ) +{ + // For now, Mapbase uses Tony Sergi's Source 2007 tonemap fixes. + // Alien Swarm SDK tonemap controller code copies the parameters instead. + + CEnvTonemapController *pController = NULL; + + if (m_hTriggerTonemapList.Count() > 0) + { + pController = static_cast(m_hTriggerTonemapList.Tail()->GetTonemapController()); + } + else if (TheTonemapSystem()->GetMasterTonemapController()) + { + pController = static_cast(TheTonemapSystem()->GetMasterTonemapController()); + } + + if (pController) + { + //m_hTonemapController = TheTonemapSystem()->GetMasterTonemapController(); + + if (pController->m_bUseCustomAutoExposureMax) + m_Local.m_TonemapParams.m_flAutoExposureMax = pController->m_flCustomAutoExposureMax; + + if (pController->m_bUseCustomAutoExposureMin) + m_Local.m_TonemapParams.m_flAutoExposureMin = pController->m_flCustomAutoExposureMin; + + if (pController->m_bUseCustomBloomScale) + m_Local.m_TonemapParams.m_flBloomScale = pController->m_flCustomBloomScale; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -4675,6 +4734,11 @@ void CBasePlayer::PostThink() { m_vecSmoothedVelocity = m_vecSmoothedVelocity * SMOOTHING_FACTOR + GetAbsVelocity() * ( 1 - SMOOTHING_FACTOR ); +#ifdef MAPBASE // From Alien Swarm SDK + UpdateTonemapController(); + UpdateFXVolume(); +#endif + if ( !g_fGameOver && !m_iPlayerLocked ) { if ( IsAlive() ) @@ -5151,7 +5215,9 @@ void CBasePlayer::Spawn( void ) // Initialize the fog and postprocess controllers. InitFogController(); +#ifdef MAPBASE // From Alien Swarm SDK InitPostProcessController(); +#endif m_DmgTake = 0; m_DmgSave = 0; @@ -8676,6 +8742,7 @@ void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct SendPropInt ( SENDINFO( m_spawnflags ), 3, SPROP_UNSIGNED, SendProxy_ShiftPlayerSpawnflags ), SendPropBool ( SENDINFO( m_bDrawPlayerModelExternally ) ), + SendPropBool ( SENDINFO( m_bInTriggerFall ) ), #endif END_SEND_TABLE() @@ -8715,8 +8782,11 @@ void SendProxy_ShiftPlayerSpawnflags( const SendProp *pProp, const void *pStruct SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), SendPropString (SENDINFO(m_szLastPlaceName) ), +#ifdef MAPBASE // From Alien Swarm SDK // Postprocess data - SendPropEHandle( SENDINFO( m_hPostProcessCtrl ) ), + SendPropEHandle ( SENDINFO(m_hPostProcessCtrl) ), + SendPropEHandle ( SENDINFO(m_hColorCorrectionCtrl) ), +#endif #if defined USES_ECON_ITEMS SendPropUtlVector( SENDINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, SendPropEHandle( NULL, 0 ) ), @@ -9432,7 +9502,19 @@ void CBasePlayer::InputSetSuppressAttacks( inputdata_t &inputdata ) void CBasePlayer::InputSetFogController( inputdata_t &inputdata ) { // Find the fog controller with the given name. +#ifdef MAPBASE // From Alien Swarm SDK + CFogController *pFogController = NULL; + if ( inputdata.value.FieldType() == FIELD_EHANDLE ) + { + pFogController = dynamic_cast( inputdata.value.Entity().Get() ); + } + else + { + pFogController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); + } +#else CFogController *pFogController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); +#endif if ( pFogController ) { m_Local.m_PlayerFog.m_hCtrl.Set( pFogController ); @@ -9448,6 +9530,7 @@ void CBasePlayer::InitFogController( void ) m_Local.m_PlayerFog.m_hCtrl = FogSystem()->GetMasterFogController(); } +#ifdef MAPBASE // From Alien Swarm SDK //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -9460,25 +9543,57 @@ void CBasePlayer::InitPostProcessController( void ) //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -void CBasePlayer::InputSetPostProcessController( inputdata_t& inputdata ) +void CBasePlayer::InitColorCorrectionController( void ) { - // Find the postprocess controller with the given name. - CPostProcessController* pController = NULL; - if (inputdata.value.FieldType() == FIELD_EHANDLE) + m_hColorCorrectionCtrl = ColorCorrectionSystem()->GetMasterColorCorrection(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetPostProcessController( inputdata_t &inputdata ) +{ + // Find the fog controller with the given name. + CPostProcessController *pController = NULL; + if ( inputdata.value.FieldType() == FIELD_EHANDLE ) { - pController = dynamic_cast(inputdata.value.Entity().Get()); + pController = dynamic_cast( inputdata.value.Entity().Get() ); } else { - pController = dynamic_cast(gEntList.FindEntityByName( NULL, inputdata.value.String() )); + pController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); } - if (pController) + if ( pController ) { m_hPostProcessCtrl.Set( pController ); } } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetColorCorrectionController( inputdata_t &inputdata ) +{ + // Find the fog controller with the given name. + CColorCorrection *pController = NULL; + if ( inputdata.value.FieldType() == FIELD_EHANDLE ) + { + pController = dynamic_cast( inputdata.value.Entity().Get() ); + } + else + { + pController = dynamic_cast( gEntList.FindEntityByName( NULL, inputdata.value.String() ) ); + } + + if ( pController ) + { + m_hColorCorrectionCtrl.Set( pController ); + } + +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pEntity - @@ -10149,4 +10264,71 @@ uint64 CBasePlayer::GetSteamIDAsUInt64( void ) return steamIDForPlayer.ConvertToUint64(); return 0; } -#endif // NO_STEAM \ No newline at end of file +#endif // NO_STEAM + +#ifdef MAPBASE // From Alien Swarm SDK +//-------------------------------------------------------------------------------------------------------- +void CBasePlayer::UpdateFXVolume( void ) +{ + CFogController *pFogController = NULL; + CPostProcessController *pPostProcessController = NULL; + CColorCorrection* pColorCorrectionEnt = NULL; + + Vector eyePos; + CBaseEntity *pViewEntity = GetViewEntity(); + if ( pViewEntity ) + { + eyePos = pViewEntity->GetAbsOrigin(); + } + else + { + eyePos = EyePosition(); + } + + CFogVolume *pFogVolume = CFogVolume::FindFogVolumeForPosition( eyePos ); + if ( pFogVolume ) + { + pFogController = pFogVolume->GetFogController(); + pPostProcessController = pFogVolume->GetPostProcessController(); + pColorCorrectionEnt = pFogVolume->GetColorCorrectionController(); + + if ( !pFogController ) + { + pFogController = FogSystem()->GetMasterFogController(); + } + + if ( !pPostProcessController ) + { + pPostProcessController = PostProcessSystem()->GetMasterPostProcessController(); + } + + if ( !pColorCorrectionEnt ) + { + pColorCorrectionEnt = ColorCorrectionSystem()->GetMasterColorCorrection(); + } + } + else if ( TheFogVolumes.Count() > 0 ) + { + // If we're not in a fog volume, clear our fog volume, if the map has any. + // This will get us back to using the master fog controller. + pFogController = FogSystem()->GetMasterFogController(); + pPostProcessController = PostProcessSystem()->GetMasterPostProcessController(); + pColorCorrectionEnt = ColorCorrectionSystem()->GetMasterColorCorrection(); + } + + if ( pFogController && m_Local.m_PlayerFog.m_hCtrl.Get() != pFogController ) + { + m_Local.m_PlayerFog.m_hCtrl.Set( pFogController ); + } + + if ( pPostProcessController ) + { + m_hPostProcessCtrl.Set( pPostProcessController ); + } + + if ( pColorCorrectionEnt ) + { + m_hColorCorrectionCtrl.Set( pColorCorrectionEnt ); + } +} +#endif diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index 03709ade..05bf335b 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -88,6 +88,10 @@ class CNavArea; class CHintSystem; class CAI_Expresser; +#ifdef MAPBASE // From Alien Swarm SDK +class CTonemapTrigger; +#endif + #if defined USES_ECON_ITEMS class CEconWearable; #endif // USES_ECON_ITEMS @@ -658,6 +662,8 @@ public: #ifdef MAPBASE bool ShouldUseVisibilityCache( CBaseEntity *pEntity ); + + void UpdateFXVolume( void ); // From Alien Swarm SDK #endif public: @@ -884,9 +890,18 @@ public: void InitFogController( void ); void InputSetFogController( inputdata_t &inputdata ); +#ifdef MAPBASE // From Alien Swarm SDK + void OnTonemapTriggerStartTouch( CTonemapTrigger *pTonemapTrigger ); + void OnTonemapTriggerEndTouch( CTonemapTrigger *pTonemapTrigger ); + CUtlVector< CHandle< CTonemapTrigger > > m_hTriggerTonemapList; + CNetworkHandle( CPostProcessController, m_hPostProcessCtrl ); // active postprocessing controller + CNetworkHandle( CColorCorrection, m_hColorCorrectionCtrl ); // active FXVolume color correction void InitPostProcessController( void ); - void InputSetPostProcessController( inputdata_t& inputdata ); + void InputSetPostProcessController( inputdata_t &inputdata ); + void InitColorCorrectionController( void ); + void InputSetColorCorrectionController( inputdata_t &inputdata ); +#endif // Used by env_soundscape_triggerable to manage when the player is touching multiple // soundscape triggers simultaneously. @@ -943,7 +958,7 @@ public: #endif #ifdef MAPBASE - bool m_bInTriggerFall; + CNetworkVar( bool, m_bInTriggerFall ); #endif private: @@ -1031,6 +1046,13 @@ protected: float m_fReplayEnd; // time to stop replay mode int m_iReplayEntity; // follow this entity in replay +#ifdef MAPBASE // From Alien Swarm SDK + // For now, Mapbase uses Tony Sergi's Source 2007 tonemap fixes. + // Alien Swarm SDK tonemap controller code copies the parameters instead. + virtual void UpdateTonemapController( void ); + //CNetworkHandle( CBaseEntity, m_hTonemapController ); +#endif + private: void HandleFuncTrain(); @@ -1147,7 +1169,8 @@ public: int m_nNumCrateHudHints; #ifdef MAPBASE - CNetworkVar( bool, m_bDrawPlayerModelExternally ); + bool GetDrawPlayerModelExternally( void ) { return m_bDrawPlayerModelExternally; } + void SetDrawPlayerModelExternally( bool bToggle ) { m_bDrawPlayerModelExternally.Set( bToggle ); } #endif private: @@ -1188,6 +1211,10 @@ private: // Player name char m_szNetname[MAX_PLAYER_NAME_LENGTH]; +#ifdef MAPBASE + CNetworkVar( bool, m_bDrawPlayerModelExternally ); +#endif + protected: // HACK FOR TF2 Prediction friend class CTFGameMovementRecon; diff --git a/sp/src/game/server/playerlocaldata.h b/sp/src/game/server/playerlocaldata.h index 587dbd1a..f9ceef70 100644 --- a/sp/src/game/server/playerlocaldata.h +++ b/sp/src/game/server/playerlocaldata.h @@ -15,7 +15,10 @@ #include "playernet_vars.h" #include "networkvar.h" #include "fogcontroller.h" +#ifdef MAPBASE // From Alien Swarm SDK #include "postprocesscontroller.h" +#include "colorcorrection.h" +#endif //----------------------------------------------------------------------------- // Purpose: Player specific data ( sent only to local player, too ) diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index 9263628b..18df0f7b 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -44,6 +44,7 @@ #ifdef MAPBASE #include "mapbase/GlobalStrings.h" #include "collisionutils.h" +#include "vstdlib/ikeyvaluessystem.h" // From Alien Swarm SDK #endif // memdbgon must be the last include file in a .cpp file!!! @@ -387,8 +388,15 @@ int CBaseProp::ParsePropData( void ) return PARSE_FAILED_NO_DATA; } +#ifdef MAPBASE // From Alien Swarm SDK + static int keyPropData = KeyValuesSystem()->GetSymbolForString( "prop_data" ); + + // Do we have a props section? + KeyValues *pkvPropData = modelKeyValues->FindKey( keyPropData ); +#else // Do we have a props section? KeyValues *pkvPropData = modelKeyValues->FindKey("prop_data"); +#endif if ( !pkvPropData ) { modelKeyValues->deleteThis(); @@ -1200,6 +1208,17 @@ int CBreakableProp::OnTakeDamage( const CTakeDamageInfo &inputInfo ) { m_hLastAttacker.Set( info.GetAttacker() ); } +#ifdef MAPBASE // From Alien Swarm SDK + else if ( info.GetAttacker() ) + { + CBaseEntity *attacker = info.GetAttacker(); + CBaseEntity *attackerOwner = attacker->GetOwnerEntity(); + if ( attackerOwner && attackerOwner->MyCombatCharacterPointer() ) + { + m_hLastAttacker.Set( attackerOwner ); + } + } +#endif float flPropDamage = GetBreakableDamage( info, assert_cast(this) ); info.SetDamage( flPropDamage ); @@ -1999,9 +2018,16 @@ BEGIN_DATADESC( CDynamicProp ) DEFINE_KEYFIELD( m_bDisableBoneFollowers, FIELD_BOOLEAN, "DisableBoneFollowers" ), DEFINE_FIELD( m_bUseHitboxesForRenderBox, FIELD_BOOLEAN ), DEFINE_FIELD( m_nPendingSequence, FIELD_SHORT ), +#ifdef MAPBASE // From Alien Swarm SDK + DEFINE_KEYFIELD( m_bUpdateAttachedChildren, FIELD_BOOLEAN, "updatechildren" ), + DEFINE_KEYFIELD( m_bHoldAnimation, FIELD_BOOLEAN, "HoldAnimation" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_STRING, "SetAnimation", InputSetAnimation ), +#ifdef MAPBASE // From Alien Swarm SDK + DEFINE_INPUTFUNC( FIELD_STRING, "SetAnimationNoReset", InputSetAnimationNoReset ), +#endif DEFINE_INPUTFUNC( FIELD_STRING, "SetDefaultAnimation", InputSetDefaultAnimation ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), @@ -2226,9 +2252,8 @@ void CDynamicProp::CreateBoneFollowers() pBone = pBone->GetNextKey(); } } - - modelKeyValues->deleteThis(); } + modelKeyValues->deleteThis(); // if we got here, we don't have a bone follower section, but if we have a ragdoll // go ahead and create default bone followers for it @@ -2258,7 +2283,11 @@ bool CDynamicProp::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& } } } +#ifdef MAPBASE // From Alien Swarm SDK + return BaseClass::TestCollision( ray, mask, trace ); +#else return false; +#endif } @@ -2366,10 +2395,23 @@ void CDynamicProp::AnimThink( void ) } else { +#ifdef MAPBASE // From Alien Swarm SDK + if ( m_iszDefaultAnim != NULL_STRING && m_bHoldAnimation == false ) + { + PropSetAnim( STRING( m_iszDefaultAnim ) ); + } + + // We need to wait for an animation change to come in + if ( m_bHoldAnimation ) + { + SetNextThink( gpGlobals->curtime + 0.1f ); + } +#else if (m_iszDefaultAnim != NULL_STRING) { PropSetAnim( STRING( m_iszDefaultAnim ) ); } +#endif } } } @@ -2381,6 +2423,17 @@ void CDynamicProp::AnimThink( void ) StudioFrameAdvance(); DispatchAnimEvents(this); m_BoneFollowerManager.UpdateBoneFollowers(this); + +#ifdef MAPBASE // From Alien Swarm SDK + // Update any SetParentAttached children + if ( m_bUpdateAttachedChildren ) + { + for ( CBaseEntity *pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() ) + { + pChild->PhysicsTouchTriggers(); + } + } +#endif } @@ -2419,6 +2472,19 @@ void CDynamicProp::InputSetAnimation( inputdata_t &inputdata ) PropSetAnim( inputdata.value.String() ); } +#ifdef MAPBASE // From Alien Swarm SDK +//------------------------------------------------------------------------------ +// Purpose: Set the animation unless the prop is already set to this particular animation +//------------------------------------------------------------------------------ +void CDynamicProp::InputSetAnimationNoReset( inputdata_t &inputdata ) +{ + if ( GetSequence() != LookupSequence( inputdata.value.String() ) ) + { + PropSetAnim( inputdata.value.String() ); + } +} +#endif + //------------------------------------------------------------------------------ // Purpose: //------------------------------------------------------------------------------ @@ -4099,6 +4165,11 @@ BEGIN_DATADESC(CBasePropDoor) DEFINE_KEYFIELD(m_SoundClose, FIELD_SOUNDNAME, "soundcloseoverride"), DEFINE_KEYFIELD(m_ls.sLockedSound, FIELD_SOUNDNAME, "soundlockedoverride"), DEFINE_KEYFIELD(m_ls.sUnlockedSound, FIELD_SOUNDNAME, "soundunlockedoverride"), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_flNPCOpenDistance, FIELD_FLOAT, "opendistoverride"), + DEFINE_KEYFIELD(m_eNPCOpenFrontActivity, FIELD_INTEGER, "openfrontactivityoverride"), + DEFINE_KEYFIELD(m_eNPCOpenBackActivity, FIELD_INTEGER, "openbackactivityoverride"), +#endif DEFINE_KEYFIELD(m_SlaveName, FIELD_STRING, "slavename" ), DEFINE_FIELD(m_bLocked, FIELD_BOOLEAN), //DEFINE_KEYFIELD(m_flBlockDamage, FIELD_FLOAT, "dmg"), @@ -4143,6 +4214,11 @@ END_SEND_TABLE() CBasePropDoor::CBasePropDoor( void ) { m_hMaster = NULL; +#ifdef MAPBASE + m_flNPCOpenDistance = -1; + m_eNPCOpenFrontActivity = ACT_INVALID; + m_eNPCOpenBackActivity = ACT_INVALID; +#endif } //----------------------------------------------------------------------------- @@ -4340,6 +4416,32 @@ void CBasePropDoor::CalcDoorSounds() { strSoundLocked = AllocPooledString( pkvHardwareData->GetString( "locked" ) ); strSoundUnlocked = AllocPooledString( pkvHardwareData->GetString( "unlocked" ) ); + +#ifdef MAPBASE + if (m_eNPCOpenFrontActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_front" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenFrontActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenFrontActivity == ACT_INVALID) + m_eNPCOpenFrontActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + if (m_eNPCOpenBackActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_back" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenBackActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenBackActivity == ACT_INVALID) + m_eNPCOpenBackActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + + if (m_flNPCOpenDistance == -1) + m_flNPCOpenDistance = pkvHardwareData->GetFloat( "npc_distance", 32.0 ); +#endif } // If any sounds were missing, try the "defaults" block. @@ -5940,6 +6042,11 @@ void CPropDoorRotating::DoorResume( void ) AngularMove( m_angGoal, m_flSpeed ); } +#ifdef MAPBASE +ConVar ai_door_enable_acts( "ai_door_enable_acts", "1", FCVAR_NONE, "Enables the new door-opening activities." ); +ConVar ai_door_open_dist_override( "ai_door_open_dist_override", "-1", FCVAR_NONE, "Overrides the distance from a door a NPC has to navigate to in order to open a door." ); +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : vecMoveDir - @@ -5961,20 +6068,37 @@ void CPropDoorRotating::GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata) Vector vecNPCOrigin = pNPC->GetAbsOrigin(); +#ifdef MAPBASE + float flPosOffset = ai_door_open_dist_override.GetFloat() >= 0.0f ? ai_door_open_dist_override.GetFloat() : GetNPCOpenDistance(); +#else + float flPosOffset = 64; +#endif + if (pNPC->GetAbsOrigin().Dot(vecForward) > GetAbsOrigin().Dot(vecForward)) { // In front of the door relative to the door's forward vector. - opendata.vecStandPos += vecForward * 64; + opendata.vecStandPos += vecForward * flPosOffset; opendata.vecFaceDir = -vecForward; +#ifdef MAPBASE + opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenFrontActivity(); +#endif } else { // Behind the door relative to the door's forward vector. - opendata.vecStandPos -= vecForward * 64; + opendata.vecStandPos -= vecForward * flPosOffset; opendata.vecFaceDir = vecForward; +#ifdef MAPBASE + opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenBackActivity(); +#endif } +#ifdef MAPBASE + if (opendata.eActivity == ACT_INVALID) + opendata.eActivity = ACT_OPEN_DOOR; +#else opendata.eActivity = ACT_OPEN_DOOR; +#endif } @@ -6006,6 +6130,14 @@ int CPropDoorRotating::DrawDebugTextOverlays(void) EntityText( text_offset, tempstr, 0); text_offset++; +#ifdef MAPBASE // From Alien Swarm SDK + if ( IsDoorLocked() ) + { + EntityText( text_offset, "LOCKED", 0); + text_offset++; + } +#endif + if ( IsDoorOpen() ) { Q_strncpy(tempstr, "DOOR STATE: OPEN", sizeof(tempstr)); diff --git a/sp/src/game/server/props.h b/sp/src/game/server/props.h index 121dd5c3..ebd87c4d 100644 --- a/sp/src/game/server/props.h +++ b/sp/src/game/server/props.h @@ -329,6 +329,9 @@ public: // Input handlers void InputSetAnimation( inputdata_t &inputdata ); +#ifdef MAPBASE // From Alien Swarm SDK + void InputSetAnimationNoReset( inputdata_t &inputdata ); +#endif void InputSetDefaultAnimation( inputdata_t &inputdata ); void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); @@ -345,6 +348,9 @@ public: int m_iTransitionDirection; // Random animations +#ifdef MAPBASE // From Alien Swarm SDK + bool m_bHoldAnimation; +#endif bool m_bRandomAnimator; float m_flNextRandAnim; float m_flMinRandAnimTime; @@ -353,6 +359,9 @@ public: bool m_bStartDisabled; bool m_bDisableBoneFollowers; +#ifdef MAPBASE // From Alien Swarm SDK + bool m_bUpdateAttachedChildren; // For props with children on attachment points, update their child touches as we animate +#endif CNetworkVar( bool, m_bUseHitboxesForRenderBox ); diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index 5cef9cc0..ce2a1bc6 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -4284,7 +4284,7 @@ CBaseEntity *CSceneEntity::FindNamedEntity( const char *name, CBaseEntity *pActo #ifdef MAPBASE const char *GetFirstSoundInScene(const char *pszScene) { - const char *soundName; + const char *soundName = NULL; SceneCachedData_t sceneData; if ( scenefilecache->GetSceneCachedData( pszScene, &sceneData ) ) { diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 9c943105..80ece7cf 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -30,6 +30,8 @@ $Project $File "env_dof_controller.h" $File "logic_playmovie.cpp" $File "movie_display.cpp" + $File "fogvolume.cpp" + $File "fogvolume.h" $File "ai_expresserfollowup.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.h" [$NEW_RESPONSE_SYSTEM] @@ -69,6 +71,7 @@ $Project $File "mapbase\GlobalStrings.h" $File "mapbase\logic_externaldata.cpp" $File "mapbase\logic_skill.cpp" + $File "mapbase\logic_substring.cpp" $File "mapbase\point_advanced_finder.cpp" $File "mapbase\point_copy_size.cpp" $File "mapbase\point_damageinfo.cpp" diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index 03e23cb7..2361f8fa 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -1279,7 +1279,7 @@ void CTriggerLook::Touch(CBaseEntity *pOther) VectorNormalize(vTargetDir); float fDotPr = DotProduct(vLookDir,vTargetDir); - if (fDotPr > m_flFieldOfView && (!m_bUseLOS || pOther->FVisible(pOther))) + if (fDotPr > m_flFieldOfView && (!m_bUseLOS || pOther->FVisible(m_hLookTargets[i]))) { hLookingAtEntity = m_hLookTargets[i]; break; @@ -3100,6 +3100,7 @@ BEGIN_DATADESC( CTriggerCamera ) #ifdef MAPBASE DEFINE_KEYFIELD( m_fov, FIELD_FLOAT, "fov" ), DEFINE_KEYFIELD( m_fovSpeed, FIELD_FLOAT, "fov_rate" ), + DEFINE_KEYFIELD( m_flTrackSpeed, FIELD_FLOAT, "trackspeed" ), DEFINE_KEYFIELD( m_bDontSetPlayerView, FIELD_BOOLEAN, "DontSetPlayerView" ), #endif @@ -3110,6 +3111,10 @@ BEGIN_DATADESC( CTriggerCamera ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOVRate", InputSetFOVRate ), + + //DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), // Defined by base class + DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetAttachment", InputSetTargetAttachment ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTrackSpeed", InputSetTrackSpeed ), #endif // Function Pointers @@ -3138,6 +3143,7 @@ CTriggerCamera::CTriggerCamera() { m_fov = 90; m_fovSpeed = 1; + m_flTrackSpeed = 40.0f; } //------------------------------------------------------------------------------ @@ -3259,6 +3265,61 @@ void CTriggerCamera::InputSetFOVRate( inputdata_t &inputdata ) { m_fovSpeed = inputdata.value.Float(); } + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTarget( inputdata_t &inputdata ) +{ + BaseClass::InputSetTarget( inputdata ); + + if ( FStrEq(STRING(m_target), "!player") ) + { + AddSpawnFlags( SF_CAMERA_PLAYER_TARGET ); + m_hTarget = m_hPlayer; + } + else + { + RemoveSpawnFlags( SF_CAMERA_PLAYER_TARGET ); + m_hTarget = GetNextTarget(); + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTargetAttachment( inputdata_t &inputdata ) +{ + m_iszTargetAttachment = inputdata.value.StringID(); + m_iAttachmentIndex = 0; + + if (m_hTarget) + { + if ( m_iszTargetAttachment != NULL_STRING ) + { + if ( !m_hTarget->GetBaseAnimating() ) + { + Warning("%s tried to target an attachment (%s) on target %s, which has no model.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) ); + } + else + { + m_iAttachmentIndex = m_hTarget->GetBaseAnimating()->LookupAttachment( STRING(m_iszTargetAttachment) ); + if ( m_iAttachmentIndex <= 0 ) + { + Warning("%s could not find attachment %s on target %s.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) ); + } + } + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CTriggerCamera::InputSetTrackSpeed( inputdata_t &inputdata ) +{ + m_flTrackSpeed = inputdata.value.Float(); +} #endif //----------------------------------------------------------------------------- @@ -3436,12 +3497,14 @@ void CTriggerCamera::Enable( void ) #ifdef MAPBASE if (!m_bDontSetPlayerView) #endif - pPlayer->SetViewEntity( this ); - - // Hide the player's viewmodel - if ( pPlayer->GetActiveWeapon() ) { - pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW ); + pPlayer->SetViewEntity( this ); + + // Hide the player's viewmodel + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW ); + } } // Only track if we have a target @@ -3487,11 +3550,16 @@ void CTriggerCamera::Disable( void ) pBasePlayer->RemoveSolidFlags( FSOLID_NOT_SOLID ); } - if (!m_bDontSetPlayerView) - pBasePlayer->SetViewEntity( NULL ); + if ( HasSpawnFlags( SF_CAMERA_PLAYER_TAKECONTROL ) ) + { + pBasePlayer->EnableControl( TRUE ); + } - pBasePlayer->EnableControl(TRUE); - pBasePlayer->m_Local.m_bDrawViewmodel = true; + if (!m_bDontSetPlayerView) + { + pBasePlayer->SetViewEntity( NULL ); + pBasePlayer->m_Local.m_bDrawViewmodel = true; + } } if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) ) @@ -3626,7 +3694,11 @@ void CTriggerCamera::FollowTarget( ) dy = dy - 360; QAngle vecAngVel; +#ifdef MAPBASE + vecAngVel.Init( dx * m_flTrackSpeed * gpGlobals->frametime, dy * m_flTrackSpeed * gpGlobals->frametime, GetLocalAngularVelocity().z ); +#else vecAngVel.Init( dx * 40 * gpGlobals->frametime, dy * 40 * gpGlobals->frametime, GetLocalAngularVelocity().z ); +#endif SetLocalAngularVelocity(vecAngVel); } diff --git a/sp/src/game/server/triggers.h b/sp/src/game/server/triggers.h index 4aacad13..47bf82be 100644 --- a/sp/src/game/server/triggers.h +++ b/sp/src/game/server/triggers.h @@ -300,6 +300,10 @@ public: #ifdef MAPBASE void InputSetFOV( inputdata_t &inputdata ); void InputSetFOVRate( inputdata_t &inputdata ); + + void InputSetTarget( inputdata_t &inputdata ); + void InputSetTargetAttachment( inputdata_t &inputdata ); + void InputSetTrackSpeed( inputdata_t &inputdata ); #endif private: @@ -323,6 +327,7 @@ private: #ifdef MAPBASE float m_fov; float m_fovSpeed; + float m_flTrackSpeed; bool m_bDontSetPlayerView; #endif diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index 6927b0f1..76d66a83 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -37,10 +37,16 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #endif // VMPROFILE +#ifdef MAPBASE_VSCRIPT +static ScriptHook_t g_Hook_OnEntityCreated; +static ScriptHook_t g_Hook_OnEntitySpawned; +static ScriptHook_t g_Hook_OnEntityDeleted; +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -class CScriptEntityIterator +class CScriptEntityIterator : public IEntityListener { public: #ifdef MAPBASE_VSCRIPT @@ -115,6 +121,48 @@ public: { return ToHScript( gEntList.FindEntityClassNearestFacing( origin, facing, threshold, const_cast(classname) ) ); } + + void EnableEntityListening() + { + // Start getting entity updates! + gEntList.AddListenerEntity( this ); + } + + void DisableEntityListening() + { + // Stop getting entity updates! + gEntList.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 OnEntitySpawned( CBaseEntity *pEntity ) + { + if ( g_pScriptVM ) + { + // entity + ScriptVariant_t args[] = { ScriptVariant_t( pEntity->GetScriptInstance() ) }; + g_Hook_OnEntitySpawned.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 ); + } + }; #endif private: } g_ScriptEntityIterator; @@ -138,6 +186,21 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETO #ifdef MAPBASE_VSCRIPT DEFINE_SCRIPTFUNC( FindByClassnameWithinBox, "Find entities by class name within an AABB. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) DEFINE_SCRIPTFUNC( FindByClassNearestFacing, "Find the nearest entity along the facing direction from the given origin within the angular threshold with the given classname." ) + + 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_OnEntitySpawned, "OnEntitySpawned", FIELD_VOID, "Called when an entity spawns. 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() #endif END_SCRIPTDESC(); diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 76a855d9..6b10c6fe 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -132,12 +132,18 @@ void Hack_FixEscapeChars( char *str ) #ifdef MAPBASE static const ConVar *pHostTimescale; +static ConVar host_pitchscale( "host_pitchscale", "-1", FCVAR_REPLICATED, "If greater than 0, controls the pitch scale of sounds instead of host_timescale." ); static float GetSoundPitchScale() { static ConVarRef sv_cheats( "sv_cheats" ); if (sv_cheats.GetBool()) - return pHostTimescale->GetFloat(); + { + if (host_pitchscale.GetFloat() > 0.0f) + return host_pitchscale.GetFloat(); + else + return pHostTimescale->GetFloat(); + } return 1.0f; } @@ -1450,6 +1456,13 @@ void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, soundOrigins ); if ( bSwallowed ) return; + + CBaseEntity *pEntity = UTIL_EntityByIndex( iEntIndex ); + if ( pEntity ) + { + pEntity->ModifySentenceParams( iSentenceIndex, iChannel, flVolume, iSoundlevel, iFlags, iPitch, + &pOrigin, &pDirection, bUpdatePositions, soundtime, iSpecialDSP, iSpeakerIndex ); + } enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, flVolume, iSoundlevel, iFlags, iPitch * GetSoundPitchScale(), iSpecialDSP, pOrigin, pDirection, &soundOrigins, bUpdatePositions, soundtime, iSpeakerIndex ); diff --git a/sp/src/game/shared/activitylist.cpp b/sp/src/game/shared/activitylist.cpp index 9e986cb7..ecee2eee 100644 --- a/sp/src/game/shared/activitylist.cpp +++ b/sp/src/game/shared/activitylist.cpp @@ -2296,15 +2296,22 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 ); REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 ); - //REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR2_LOW ); #endif #ifdef SHARED_COMBINE_ACTIVITIES REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE ); REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK2 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP ); @@ -2314,9 +2321,133 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER ); #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_REVOLVER ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_CROSSBOW ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_AIM_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_AIM_PISTOL ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_SHOTGUN_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_RPG ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_MELEE ); + + REGISTER_SHARED_ACTIVITY( ACT_RUN_PACKAGE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SUITCASE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_MELEE ); REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_MELEE ); +#endif + +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_ALL ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_IDLE ); + + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_TOP ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_BOTTOM ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_PISTOL ); #endif AssertMsg( g_HighestActivity == LAST_SHARED_ACTIVITY - 1, "Not all activities from ai_activity.h registered in activitylist.cpp" ); diff --git a/sp/src/game/shared/ai_activity.h b/sp/src/game/shared/ai_activity.h index 44b8f62d..ad39c548 100644 --- a/sp/src/game/shared/ai_activity.h +++ b/sp/src/game/shared/ai_activity.h @@ -13,42 +13,42 @@ #ifdef MAPBASE -// Mapbase adds a few shared activities. // -// These used to be placed in between existing activities, as outside of the code activities are based off of strings. -// This seemed like a bad idea, but no problems arose at the time. -// I later discovered that apparently some things in MP use the direct integers instead of the enum names. -// I retroactively put all custom activities at the bottom of the enum instead. -// Their placements in activitylist.cpp and ai_activity.cpp have not been changed. +// Mapbase adds many new shared activities, primarily for NPCs. +// +// These are at the bottom of the enum to prevent disruptions in the order of existing activities. +// // AR2 ACTIVITY FIX -// You know all of those AR2 activities that citizens and combine soldiers have? -// Yeah, those are unused. It appears Valve forgot to implement them. +// Citizens and Combine soldiers have several activities for the SMG1 and AR2 which differ from each other. +// Across both NPCs, there are around 20-40 different AR2 animations. Most of these animations are similar to the +// SMG1 animations, except their hand positions are adjusted to use the AR2 instead. // -// What could be 20-40 different animations on two different characters are not even defined in code. -// I didn't even realize they were unused until I saw ACT_RELOAD_AR2, so I don't blame them for never realizing this. -// They work surprisingly well for probably never being tested in-game. +// Unfortunately, the vast majority of the AR2 animations in those models are not declared as real activities in +// code. The AR2 instead falls back to its SMG1 animation counterparts. +// This is thought to be an oversight which dates back to late in Half-Life 2's development. // -// 1 = Add activities directly -// 2 = Add activities as custom activities (todo) -// -// 2 should only be preferable if adding them like this breaks something. +// This preprocessor declares those activities and implements them on the AR2. In-game, they work surprisingly well +// despite presumably never being tested in-game during HL2's development. #define AR2_ACTIVITY_FIX 1 -// COMPANION HOLSTER WORKAROUND -// I introduced a separate holster/unholster animation to male_shared -// and female_shared and I realized it might conflict with Alyx's animation. -// -// I came up with a solution--ACT_ARM_RIFLE and its disarm counterpart--to solve it. -// I didn't think about the fact I could've named them the same as Alyx's so her animations would overwrite it... -// ...so this has been deactivated. -//#define COMPANION_HOLSTER_WORKAROUND 1 - // SHARED COMBINE ACTIVITIES -// This turns ACT_COMBINE_AR2_ALTFIRE and ACT_COMBINE_THROW_GRENADE into shared activities. -// This is necessary so other NPCs to use them without having to rely on a bunch of custom activities. +// This turns ACT_COMBINE_AR2_ALTFIRE, ACT_COMBINE_THROW_GRENADE, and their new gesture counterparts into shared activities. +// This is necessary for other NPCs to use them without having to rely on private custom activities declared through the AI definition system. #define SHARED_COMBINE_ACTIVITIES 1 +// EXPANDED HL2 WEAPON ACTIVITIES +// This enables a bunch of new activities for Half-Life 2 weapons, including new 357 animations and readiness activities for pistols. +#define EXPANDED_HL2_WEAPON_ACTIVITIES 1 + +// EXPANDED NAVIGATION ACTIVITIES +// This enables some new navigation-related activities. +#define EXPANDED_NAVIGATION_ACTIVITIES 1 + +// EXPANDED HL2 COVER ACTIVITIES +// This enables some new cover-related activities. +#define EXPANDED_HL2_COVER_ACTIVITIES 1 + #endif #define ACTIVITY_NOT_AVAILABLE -1 @@ -2169,16 +2169,23 @@ typedef enum ACT_RUN_AIM_AR2, ACT_RELOAD_AR2, - //ACT_RELOAD_AR2_LOW, + ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2, + + ACT_COVER_AR2_LOW, #endif #ifdef SHARED_COMBINE_ACTIVITIES ACT_COMBINE_THROW_GRENADE, ACT_COMBINE_AR2_ALTFIRE, - // New gesture-based signals as activities for people who want to use them + // Gesture versions for existing Combine signal and grenade activities + ACT_GESTURE_COMBINE_THROW_GRENADE, + ACT_GESTURE_COMBINE_AR2_ALTFIRE, + ACT_GESTURE_SPECIAL_ATTACK1, + ACT_GESTURE_SPECIAL_ATTACK2, + ACT_GESTURE_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_GROUP, @@ -2188,9 +2195,143 @@ typedef enum ACT_GESTURE_SIGNAL_TAKECOVER, #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Revolver (357) + ACT_IDLE_REVOLVER, + ACT_IDLE_ANGRY_REVOLVER, + ACT_WALK_REVOLVER, + ACT_RUN_REVOLVER, + ACT_WALK_AIM_REVOLVER, + ACT_RUN_AIM_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER, + ACT_RELOAD_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER_LOW, + ACT_RELOAD_REVOLVER_LOW, + ACT_COVER_REVOLVER_LOW, + ACT_RANGE_AIM_REVOLVER_LOW, + ACT_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_GESTURE_RELOAD_REVOLVER, + + // Crossbow + ACT_IDLE_CROSSBOW, + ACT_IDLE_ANGRY_CROSSBOW, + ACT_WALK_CROSSBOW, + ACT_RUN_CROSSBOW, + ACT_WALK_AIM_CROSSBOW, + ACT_RUN_AIM_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW, + ACT_RELOAD_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW_LOW, + ACT_RELOAD_CROSSBOW_LOW, + ACT_COVER_CROSSBOW_LOW, + ACT_RANGE_AIM_CROSSBOW_LOW, + ACT_GESTURE_RANGE_ATTACK_CROSSBOW, + ACT_GESTURE_RELOAD_CROSSBOW, + + // Pistol + ACT_IDLE_PISTOL_RELAXED, + ACT_IDLE_PISTOL_STIMULATED, + ACT_WALK_PISTOL_RELAXED, + ACT_WALK_PISTOL_STIMULATED, + ACT_RUN_PISTOL_RELAXED, + ACT_RUN_PISTOL_STIMULATED, + + ACT_IDLE_AIM_PISTOL_STIMULATED, + ACT_WALK_AIM_PISTOL_STIMULATED, + ACT_RUN_AIM_PISTOL_STIMULATED, + + ACT_WALK_CROUCH_PISTOL, + ACT_WALK_CROUCH_AIM_PISTOL, + ACT_RUN_CROUCH_PISTOL, + ACT_RUN_CROUCH_AIM_PISTOL, + + // Shotgun + ACT_IDLE_SHOTGUN, + ACT_WALK_SHOTGUN, + ACT_RUN_SHOTGUN, + + ACT_COVER_SHOTGUN_LOW, + ACT_RANGE_AIM_SHOTGUN_LOW, + + ACT_WALK_SHOTGUN_RELAXED, + ACT_WALK_SHOTGUN_STIMULATED, + ACT_RUN_SHOTGUN_RELAXED, + ACT_RUN_SHOTGUN_STIMULATED, + + ACT_IDLE_AIM_SHOTGUN_STIMULATED, + ACT_WALK_AIM_SHOTGUN_STIMULATED, + ACT_RUN_AIM_SHOTGUN_STIMULATED, + + // RPG + ACT_RANGE_AIM_RPG_LOW, + ACT_RANGE_ATTACK_RPG_LOW, + ACT_GESTURE_RANGE_ATTACK_RPG, + + // Melee + ACT_WALK_MELEE, + ACT_RUN_MELEE, + + // Citizen accessories + ACT_RUN_PACKAGE, + ACT_RUN_SUITCASE, + + // Holster/Unholster ACT_ARM_RIFLE, + ACT_ARM_SHOTGUN, + ACT_ARM_RPG, + ACT_ARM_MELEE, ACT_DISARM_RIFLE, + ACT_DISARM_SHOTGUN, + ACT_DISARM_RPG, + ACT_DISARM_MELEE, +#endif + +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + ACT_CLIMB_ALL, // An actual blend animation which uses pose parameters for direction + ACT_CLIMB_IDLE, + + ACT_CLIMB_MOUNT_TOP, + ACT_CLIMB_MOUNT_BOTTOM, + ACT_CLIMB_DISMOUNT_BOTTOM, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + // Crouch Cover Medium + ACT_RANGE_ATTACK1_MED, + ACT_RANGE_ATTACK2_MED, + ACT_RANGE_AIM_MED, + + ACT_RANGE_ATTACK_AR2_MED, + ACT_RANGE_ATTACK_SMG1_MED, + ACT_RANGE_ATTACK_SHOTGUN_MED, + ACT_RANGE_ATTACK_PISTOL_MED, + ACT_RANGE_ATTACK_RPG_MED, + ACT_RANGE_ATTACK_REVOLVER_MED, + ACT_RANGE_ATTACK_CROSSBOW_MED, + + ACT_RANGE_AIM_AR2_MED, + ACT_RANGE_AIM_SMG1_MED, + ACT_RANGE_AIM_SHOTGUN_MED, + ACT_RANGE_AIM_PISTOL_MED, + ACT_RANGE_AIM_RPG_MED, + ACT_RANGE_AIM_REVOLVER_MED, + ACT_RANGE_AIM_CROSSBOW_MED, + + // Wall Cover (for use in custom cover hints) + ACT_COVER_WALL_R, + ACT_COVER_WALL_L, + ACT_COVER_WALL_LOW_R, + ACT_COVER_WALL_LOW_L, + + ACT_COVER_WALL_R_RIFLE, + ACT_COVER_WALL_L_RIFLE, + ACT_COVER_WALL_LOW_R_RIFLE, + ACT_COVER_WALL_LOW_L_RIFLE, + + ACT_COVER_WALL_R_PISTOL, + ACT_COVER_WALL_L_PISTOL, + ACT_COVER_WALL_LOW_R_PISTOL, + ACT_COVER_WALL_LOW_L_PISTOL, #endif // this is the end of the global activities, private per-monster activities start here. diff --git a/sp/src/game/shared/ai_responsesystem_new.cpp b/sp/src/game/shared/ai_responsesystem_new.cpp index 6c3301bb..9b519902 100644 --- a/sp/src/game/shared/ai_responsesystem_new.cpp +++ b/sp/src/game/shared/ai_responsesystem_new.cpp @@ -531,6 +531,9 @@ public: virtual void LevelInitPostEntity() { +#ifdef MAPBASE + if (!rr_enhanced_saverestore.GetBool() || gpGlobals->eLoadType != MapLoad_Transition) +#endif ResetResponseGroups(); } @@ -567,6 +570,16 @@ public: virtual void LevelInitPostEntity() { +#ifdef MAPBASE + // CInstancedResponseSystem is not a CAutoGameSystem, so this needs to be called manually. + // The same could've been accomplished by making CInstancedResponseSystem derive from CAutoGameSystem, + // but their instanced nature would've complicated things a lot. + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + m_InstancedSystems[i]->LevelInitPostEntity(); + } +#endif } virtual void Release() diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 9f3b9be4..3e23ab0f 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -1046,7 +1046,13 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassify() Activity idleact = ActivityOverride(ACT_IDLE_ANGRY, NULL); switch (idleact) { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_REVOLVER: +#endif case ACT_IDLE_ANGRY_PISTOL: return WEPCLASS_HANDGUN; +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_CROSSBOW: // For now, crossbows are rifles +#endif case ACT_IDLE_ANGRY_SMG1: case ACT_IDLE_ANGRY_AR2: return WEPCLASS_RIFLE; case ACT_IDLE_ANGRY_SHOTGUN: return WEPCLASS_SHOTGUN; @@ -1077,6 +1083,11 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) return WEPCLASS_INVALID; } +#ifdef HL2_DLL +extern acttable_t *GetSMG1Acttable(); +extern int GetSMG1ActtableCount(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1084,12 +1095,32 @@ bool CBaseCombatWeapon::SupportsBackupActivity(Activity activity) { // Derived classes should override this. - // Pistol and melee users should not use SMG animations for missing pistol activities. - if (WeaponClassify() == WEPCLASS_HANDGUN || IsMeleeWeapon()) +#ifdef HL2_DLL + // Melee users should not use SMG animations for missing activities. + if (IsMeleeWeapon() && GetBackupActivityList() == GetSMG1Acttable()) return false; +#endif return true; } + +acttable_t *CBaseCombatWeapon::GetBackupActivityList() +{ +#ifdef HL2_DLL + return GetSMG1Acttable(); +#else + return NULL; +#endif +} + +int CBaseCombatWeapon::GetBackupActivityListCount() +{ +#ifdef HL2_DLL + return GetSMG1ActtableCount(); +#else + return 0; +#endif +} #endif @@ -2339,6 +2370,28 @@ bool CBaseCombatWeapon::Reload( void ) return DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::Reload_NPC( bool bPlaySound ) +{ + if (bPlaySound) + WeaponSound( RELOAD_NPC ); + + if (UsesClipsForAmmo1()) + { + m_iClip1 = GetMaxClip1(); + } + else + { + // For weapons which don't use clips, give the owner ammo. + if (GetOwner()) + GetOwner()->SetAmmoCount( GetDefaultClip1(), m_iPrimaryAmmoType ); + } +} +#endif + //========================================================= void CBaseCombatWeapon::WeaponIdle( void ) { diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index 2d19727d..abfdb3b0 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -230,6 +230,8 @@ public: static WeaponClass_t WeaponClassFromString(const char *str); virtual bool SupportsBackupActivity(Activity activity); + virtual acttable_t *GetBackupActivityList(); + virtual int GetBackupActivityListCount(); #endif virtual void Equip( CBaseCombatCharacter *pOwner ); @@ -319,7 +321,7 @@ public: bool ReloadsSingly( void ) const; #ifdef MAPBASE // Originally created for the crossbow, can be used to add special NPC reloading behavior - virtual void Reload_NPC( void ) { WeaponSound(RELOAD_NPC); m_iClip1 = GetMaxClip1(); } + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual bool AutoFiresFullClip( void ) { return false; } diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index 6b802a25..cd3854dd 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2393,8 +2393,67 @@ void CBaseEntity::ModifyEmitSoundParams( EmitSound_t ¶ms ) params.m_pSoundName = GameRules()->TranslateEffectForVisionFilter( "sounds", params.m_pSoundName ); } #endif + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ModifyEmitSoundParams.CanRunInScope( m_ScriptScope )) + { + HSCRIPT hParams = g_pScriptVM->RegisterInstance( reinterpret_cast(¶ms) ); + + // params + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hParams ) }; + g_Hook_ModifyEmitSoundParams.Call( m_ScriptScope, &functionReturn, args ); + + g_pScriptVM->RemoveInstance( hParams ); + } +#endif } +#if defined(MAPBASE) && defined(GAME_DLL) +void CBaseEntity::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 ) +{ +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ModifySentenceParams.CanRunInScope( m_ScriptScope )) + { + // This is a bit of a hack, but for consistency with ModifyEmitSoundParams, put them into an EmitSound_t params + ScriptEmitSound_t params; + params.m_pSoundName = engine->SentenceNameFromIndex( iSentenceIndex ); + params.m_nChannel = iChannel; + params.m_flVolume = flVolume; + params.m_SoundLevel = iSoundlevel; + params.m_nFlags = iFlags; + params.m_nPitch = iPitch; + params.m_pOrigin = *pOrigin; + params.m_flSoundTime = soundtime; + params.m_nSpecialDSP = iSpecialDSP; + params.m_nSpeakerEntity = iSpeakerIndex; + + HSCRIPT hParams = g_pScriptVM->RegisterInstance( ¶ms ); + + // params + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ScriptVariant_t( hParams ) }; + if (g_Hook_ModifySentenceParams.Call( m_ScriptScope, &functionReturn, args )) + { + iSentenceIndex = engine->SentenceIndexFromName( params.m_pSoundName ); + iChannel = params.m_nChannel; + flVolume = params.m_flVolume; + iSoundlevel = params.m_SoundLevel; + iFlags = params.m_nFlags; + iPitch = params.m_nPitch; + *pOrigin = params.m_pOrigin; + soundtime = params.m_flSoundTime; + iSpecialDSP = params.m_nSpecialDSP; + iSpeakerIndex = params.m_nSpeakerEntity; + } + + g_pScriptVM->RemoveInstance( hParams ); + } +#endif +} +#endif + //----------------------------------------------------------------------------- // These methods encapsulate MOVETYPE_FOLLOW, which became obsolete //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/baseviewmodel_shared.cpp b/sp/src/game/shared/baseviewmodel_shared.cpp index b7fef9b7..47acecd6 100644 --- a/sp/src/game/shared/baseviewmodel_shared.cpp +++ b/sp/src/game/shared/baseviewmodel_shared.cpp @@ -289,6 +289,16 @@ void CBaseViewModel::AddEffects( int nEffects ) SetControlPanelsActive( false ); } +#ifdef MAPBASE + // Apply effect changes to any viewmodel children as well + // (fixes hand models) + for (CBaseEntity *pChild = FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer()) + { + if (pChild->GetClassname()[0] == 'h') + pChild->AddEffects( nEffects ); + } +#endif + BaseClass::AddEffects( nEffects ); } @@ -302,6 +312,16 @@ void CBaseViewModel::RemoveEffects( int nEffects ) SetControlPanelsActive( true ); } +#ifdef MAPBASE + // Apply effect changes to any viewmodel children as well + // (fixes hand models) + for (CBaseEntity *pChild = FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer()) + { + if (pChild->GetClassname()[0] == 'h') + pChild->RemoveEffects( nEffects ); + } +#endif + BaseClass::RemoveEffects( nEffects ); } @@ -424,6 +444,21 @@ void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePos g_ClientVirtualReality.OverrideViewModelTransform( vmorigin, vmangles, pWeapon && pWeapon->ShouldUseLargeViewModelVROverride() ); } +#ifdef MAPBASE + // Flip the view if we should be flipping + if (ShouldFlipViewModel()) + { + Vector vecOriginDiff = (eyePosition - vmorigin); + QAngle angAnglesDiff = (eyeAngles - vmangles); + + vmorigin.x = (eyePosition.x + vecOriginDiff.x); + vmorigin.y = (eyePosition.y + vecOriginDiff.y); + + vmangles.y = (eyeAngles.y + angAnglesDiff.y); + vmangles.z = (eyeAngles.z + angAnglesDiff.z); + } +#endif + SetLocalOrigin( vmorigin ); SetLocalAngles( vmangles ); diff --git a/sp/src/game/shared/gamemovement.cpp b/sp/src/game/shared/gamemovement.cpp index d77d0c89..46afe4f2 100644 --- a/sp/src/game/shared/gamemovement.cpp +++ b/sp/src/game/shared/gamemovement.cpp @@ -3907,13 +3907,11 @@ void CGameMovement::CheckFalling( void ) return; #ifdef MAPBASE -#ifdef GAME_DLL // Let's hope we could work without transmitting to the client... if ( player->m_bInTriggerFall ) { - // This lets the fall damage functions do their magic without having to change them. + // This value lets the existing fall damage functions ensure a fatal fall. player->m_Local.m_flFallVelocity += (PLAYER_FATAL_FALL_SPEED + PLAYER_LAND_ON_FLOATING_OBJECT); } -#endif #endif if ( !IsDead() && player->m_Local.m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHOLD ) @@ -3921,7 +3919,11 @@ void CGameMovement::CheckFalling( void ) bool bAlive = true; float fvol = 0.5; +#ifdef MAPBASE + if ( player->GetWaterLevel() > 0 && !player->m_bInTriggerFall ) +#else if ( player->GetWaterLevel() > 0 ) +#endif { // They landed in water. } diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp index 1d68b529..992bd036 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -81,6 +81,11 @@ acttable_t CWeaponStunStick::m_acttable[] = #endif { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_MELEE, false }, + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponStunStick); diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.h b/sp/src/game/shared/hl2mp/weapon_stunstick.h index da25999c..93688915 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.h +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.h @@ -83,6 +83,12 @@ public: float GetDamageForActivity( Activity hitActivity ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + CWeaponStunStick( const CWeaponStunStick & ); private: diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 0f3db5b8..877cf9ff 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -23,6 +23,7 @@ #include "panelmetaclassmgr.h" #include "c_soundscape.h" #include "hud_macros.h" +#include "clientmode_shared.h" #else #include "soundscape_system.h" #include "AI_ResponseSystem.h" @@ -47,27 +48,12 @@ extern ISoundEmitterSystemBase *soundemitterbase; ConVar mapbase_load_default_manifest("mapbase_load_default_manifest", "1", FCVAR_ARCHIVE, "Should we automatically load our default manifest file? (\"maps/%mapname%_manifest.txt\")"); -ConVar mapbase_load_soundscripts("mapbase_load_soundscripts", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscripts? e.g. \"maps/mapname_level_sounds.txt\""); - -//ConVar mapbase_load_propdata("mapbase_load_propdata", "1", FCVAR_ARCHIVE, "Should we load map-specific propdata files? e.g. \"maps/mapname_propdata.txt\""); - -//ConVar mapbase_load_soundscapes("mapbase_load_soundscapes", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscapes? e.g. \"maps/mapname_soundscapes.txt\""); - -ConVar mapbase_load_localization( "mapbase_load_localization", "1", FCVAR_ARCHIVE, "Should we load map-specific localized text files? e.g. \"maps/mapname_english.txt\"" ); - -ConVar mapbase_load_surfaceprops( "mapbase_load_surfaceprops", "1", FCVAR_ARCHIVE, "Should we load map-specific surfaceproperties files? e.g. \"maps/mapname_surfaceproperties.txt\"" ); - #ifdef GAME_DLL // This constant should change with each Mapbase update ConVar mapbase_version( "mapbase_version", MAPBASE_VERSION, FCVAR_NONE, "The version of Mapbase currently being used in this mod's server.dll" ); -ConVar mapbase_load_sentences("mapbase_load_sentences", "1", FCVAR_ARCHIVE, "Should we load map-specific sentences? e.g. \"maps/mapname_sentences.txt\""); - -ConVar mapbase_load_talker("mapbase_load_talker", "1", FCVAR_ARCHIVE, "Should we load map-specific talker files? e.g. \"maps/mapname_talker.txt\""); ConVar mapbase_flush_talker("mapbase_flush_talker", "1", FCVAR_NONE, "Normally, when a map with custom talker files is unloaded, the response system resets to rid itself of the custom file(s). Turn this convar off to prevent that from happening."); -ConVar mapbase_load_actbusy("mapbase_load_actbusy", "1", FCVAR_ARCHIVE, "Should we load map-specific actbusy files? e.g. \"maps/mapname_actbusy.txt\""); - extern void MapbaseGameLog_Init(); extern void ParseCustomActbusyFile(const char *file); @@ -81,8 +67,11 @@ static bool g_bMapContainsCustomTalker; // This constant should change with each Mapbase update ConVar mapbase_version_client( "mapbase_version_client", MAPBASE_VERSION, FCVAR_NONE, "The version of Mapbase currently being used in this mod's client.dll" ); -//ConVar mapbase_load_cc("mapbase_load_cc", "1", FCVAR_ARCHIVE, "Should we load map-specific closed captioning? e.g. \"maps/mapname_closecaption_english.txt\" and \"maps/mapname_closecaption_english.dat\""); +// This is from the vgui_controls library +extern vgui::HScheme g_iCustomClientSchemeOverride; +bool g_bUsingCustomHudAnimations = false; +bool g_bUsingCustomHudLayout = false; #endif extern void AddSurfacepropFile( const char *pFileName, IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem ); @@ -101,11 +90,14 @@ enum MANIFEST_LOCALIZATION, MANIFEST_SURFACEPROPS, #ifdef CLIENT_DLL - //MANIFEST_CLOSECAPTION, + MANIFEST_CLOSECAPTION, MANIFEST_VGUI, + MANIFEST_CLIENTSCHEME, + MANIFEST_HUDANIMATIONS, + MANIFEST_HUDLAYOUT, #else MANIFEST_TALKER, - MANIFEST_SENTENCES, + //MANIFEST_SENTENCES, MANIFEST_ACTBUSY, #endif @@ -115,25 +107,35 @@ enum struct ManifestType_t { + ManifestType_t( const char *_string, const char *cvarname, const char *cvardesc ) : cvar( cvarname, "1", FCVAR_ARCHIVE, cvardesc ) + { + string = _string; + } + //int type; const char *string; - ConVar *cvar; + ConVar cvar; }; +#define DECLARE_MANIFEST_TYPE(name, cvar, desc) { #name, ConVar(#cvar, "1", FCVAR_ARCHIVE, #desc) } + // KEEP THS IN SYNC WITH THE ENUM! static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = { - { "soundscripts", &mapbase_load_soundscripts }, - //{ "propdata", &mapbase_load_propdata }, - //{ "soundscapes", &mapbase_load_soundscapes }, - { "localization", &mapbase_load_localization }, - { "surfaceprops", &mapbase_load_surfaceprops }, + { "soundscripts", "mapbase_load_soundscripts", "Should we load map-specific soundscripts? e.g. \"maps/_level_sounds.txt\"" }, + //{ "propdata", "mapbase_load_propdata", "Should we load map-specific soundscripts? e.g. \"maps/_level_sounds.txt\"" }, + //{ "soundscapes", "mapbase_load_soundscapes", "Should we load map-specific soundscapes? e.g. \"maps/_soundscapes.txt\"" }, + { "localization", "mapbase_load_localization", "Should we load map-specific localized text files? e.g. \"maps/_english.txt\"" }, + { "surfaceprops", "mapbase_load_surfaceprops", "Should we load map-specific surfaceproperties files? e.g. \"maps/_surfaceproperties.txt\"" }, #ifdef CLIENT_DLL - //{ "closecaption", &mapbase_load_cc }, - { "vgui", NULL }, + { "closecaption", "mapbase_load_closecaption", "Should we load map-specific closed captioning? e.g. \"maps/_closecaption_english.txt\" and \"maps/_closecaption_english.dat\"" }, + { "vgui", "mapbase_load_vgui", "Should we load map-specific VGUI screens? e.g. \"maps/_screens.txt\"" }, + { "clientscheme", "mapbase_load_clientscheme", "Should we load map-specific ClientScheme.res overrides? e.g. \"maps/_clientscheme.res\"" }, + { "hudanimations", "mapbase_load_hudanimations", "Should we load map-specific HUD animation overrides? e.g. \"maps/_hudanimations.txt\"" }, + { "hudlayout", "mapbase_load_hudlayout", "Should we load map-specific HUD layout overrides? e.g. \"maps/_hudlayout.res\"" }, #else - { "talker", &mapbase_load_talker }, - { "sentences", &mapbase_load_sentences }, - { "actbusy", &mapbase_load_actbusy }, + { "talker", "mapbase_load_talker", "Should we load map-specific talker files? e.g. \"maps/_talker.txt\"" }, + //{ "sentences", "mapbase_load_sentences", "Should we load map-specific sentences? e.g. \"maps/_sentences.txt\"" }, + { "actbusy", "mapbase_load_actbusy", "Should we load map-specific actbusy files? e.g. \"maps/_actbusy.txt\"" }, #endif }; @@ -262,6 +264,46 @@ public: g_MapName = NULL; RefreshCustomTalker(); + +#ifdef CLIENT_DLL + CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption ); + FOR_EACH_VEC( m_CloseCaptionFileNames, i ) + { + hudCloseCaption->RemoveCaptionDictionary( m_CloseCaptionFileNames[i] ); + } + m_CloseCaptionFileNames.RemoveAll(); + + if (g_iCustomClientSchemeOverride != 0 || g_bUsingCustomHudAnimations || g_bUsingCustomHudLayout) + { + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Mapbase: Reloading client mode and viewport scheme\n" ); + + // TODO: We currently have no way of actually cleaning up custom schemes upon level unload. + // That may or may not be sustainable if there's a ton of custom schemes loaded at once + g_iCustomClientSchemeOverride = 0; + + g_bUsingCustomHudAnimations = false; + g_bUsingCustomHudLayout = false; + + // Reload scheme + ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); + if ( mode ) + { + mode->ReloadScheme(); + + // We need to reload default values, so load a special "hudlayout_mapbase.res" file that only contains + // default Mapbase definitions identical to the defaults in the code + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + KeyValuesAD pConditions( "conditions" ); + g_pClientMode->ComputeVguiResConditions( pConditions ); + + // reload the .res file from disk + pViewport->LoadControlSettings( "scripts/hudlayout_mapbase.res", NULL, NULL, pConditions ); + } + } + } +#endif } bool RefreshMapName() @@ -284,9 +326,6 @@ public: } #ifdef CLIENT_DLL - bool m_bInitializedRTs = false; - CUtlVector m_CameraTextures; - //----------------------------------------------------------------------------- // Initialize custom RT textures if necessary //----------------------------------------------------------------------------- @@ -389,8 +428,11 @@ public: case MANIFEST_LOCALIZATION: { g_pVGuiLocalize->AddFile( value, "MOD", true ); } break; case MANIFEST_SURFACEPROPS: { AddSurfacepropFile( value, physprops, filesystem ); } break; #ifdef CLIENT_DLL - //case MANIFEST_CLOSECAPTION: { todo } break; - case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; + case MANIFEST_CLOSECAPTION: { ManifestLoadCustomCloseCaption( value ); } break; + case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break; + case MANIFEST_CLIENTSCHEME: { ManifestLoadCustomScheme( value ); } break; + case MANIFEST_HUDANIMATIONS: { ManifestLoadCustomHudAnimations( value ); } break; + case MANIFEST_HUDLAYOUT: { ManifestLoadCustomHudLayout( value ); } break; //case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break; #else case MANIFEST_TALKER: { @@ -398,7 +440,7 @@ public: LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value ); } break; //case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break; - case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; + //case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break; case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break; #endif } @@ -457,7 +499,7 @@ public: { if (FStrEq(name, gm_szManifestFileStrings[i].string)) { - if (!gm_szManifestFileStrings[i].cvar || gm_szManifestFileStrings[i].cvar->GetBool()) + if (gm_szManifestFileStrings[i].cvar.GetBool()) { LoadFromValue(value, i, bDontWarn); } @@ -467,23 +509,101 @@ public: } } -#ifdef MAPBASE_VSCRIPT - void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } +private: - void LoadSoundscriptFile( const char *szScript ) { LoadFromValue(szScript, MANIFEST_SOUNDSCRIPTS, false); } -#ifndef CLIENT_DLL - void LoadTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); } - void LoadActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); } +#ifdef CLIENT_DLL + void ManifestLoadCustomCloseCaption( const char *pszFile ) + { + if (GET_HUDELEMENT( CHudCloseCaption )) + (GET_HUDELEMENT( CHudCloseCaption ))->AddCustomCaptionFile( pszFile, m_CloseCaptionFileNames ); + } + + // Custom scheme loading + void ManifestLoadCustomScheme( const char *pszFile ) + { + g_iCustomClientSchemeOverride = vgui::scheme()->LoadSchemeFromFile( pszFile, "CustomClientScheme" ); + + // Reload scheme + ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal(); + if ( mode ) + { + mode->ReloadScheme(); + } + } + + void ManifestLoadCustomHudAnimations( const char *pszFile ) + { + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + g_bUsingCustomHudAnimations = true; + if (!pViewport->LoadCustomHudAnimations( pszFile )) + { + g_bUsingCustomHudAnimations = false; + CGWarning( 0, CON_GROUP_MAPBASE_MISC, "Custom HUD animations file \"%s\" failed to load\n", pszFile ); + pViewport->ReloadHudAnimations(); + } + else + { + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Loaded custom HUD animations file \"%s\"\n", pszFile );; + } + } + } + + void ManifestLoadCustomHudLayout( const char *pszFile ) + { + CBaseViewport *pViewport = dynamic_cast(g_pClientMode->GetViewport()); + if (pViewport) + { + g_bUsingCustomHudLayout = true; + + KeyValuesAD pConditions( "conditions" ); + g_pClientMode->ComputeVguiResConditions( pConditions ); + + // reload the .res file from disk + pViewport->LoadControlSettings( pszFile, NULL, NULL, pConditions ); + + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Loaded custom HUD layout file \"%s\"\n", pszFile );; + } + } +#endif + +public: + + void LoadCustomSoundscriptFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_SOUNDSCRIPTS, false ); } + void LoadCustomLocalizationFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_LOCALIZATION, false ); } + void LoadCustomSurfacePropsFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_SURFACEPROPS, false ); } +#ifdef CLIENT_DLL + void LoadCustomCloseCaptionFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_CLOSECAPTION, false ); } + void LoadCustomVGUIFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_VGUI, false ); } + void LoadCustomClientSchemeFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_CLIENTSCHEME, false ); } + void LoadCustomHUDAnimationsFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_HUDANIMATIONS, false ); } + void LoadCustomHUDLayoutFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_HUDLAYOUT, false ); } +#else + void LoadCustomTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); } + void LoadCustomActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); } #endif const char *GetModName() { return g_iszGameName; } bool IsCoreMapbase() { return g_bMapbaseCore; } +#ifdef MAPBASE_VSCRIPT + void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } + virtual void RegisterVScript() { g_pScriptVM->RegisterInstance( this, "Mapbase" ); } #endif + +private: + +#ifdef CLIENT_DLL + bool m_bInitializedRTs = false; + CUtlVector m_CameraTextures; + + CUtlVector m_CloseCaptionFileNames; +#endif }; CMapbaseSystem g_MapbaseSystem; @@ -496,14 +616,32 @@ END_DATADESC() #ifdef MAPBASE_VSCRIPT BEGIN_SCRIPTDESC_ROOT( CMapbaseSystem, SCRIPT_SINGLETON "All-purpose Mapbase system primarily used for map-specific files." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddManifestFile, "AddManifestFile", "Loads a manifest file." ) - DEFINE_SCRIPTFUNC( LoadSoundscriptFile, "Loads a custom soundscript file." ) -#ifndef CLIENT_DLL - DEFINE_SCRIPTFUNC( LoadTalkerFile, "Loads a custom talker file." ) - DEFINE_SCRIPTFUNC( LoadActbusyFile, "Loads a custom actbusy file." ) + DEFINE_SCRIPTFUNC( LoadCustomSoundscriptFile, "Loads a custom soundscript file." ) + DEFINE_SCRIPTFUNC( LoadCustomLocalizationFile, "Loads a custom localization file." ) + DEFINE_SCRIPTFUNC( LoadCustomSurfacePropsFile, "Loads a custom surface properties file." ) +#ifdef CLIENT_DLL + DEFINE_SCRIPTFUNC( LoadCustomCloseCaptionFile, "Loads a custom closed captions file." ) + DEFINE_SCRIPTFUNC( LoadCustomVGUIFile, "Loads a custom VGUI definitions file." ) + DEFINE_SCRIPTFUNC( LoadCustomClientSchemeFile, "Loads a custom ClientScheme.res override file." ) + DEFINE_SCRIPTFUNC( LoadCustomHUDAnimationsFile, "Loads a custom HUD animations override file." ) + DEFINE_SCRIPTFUNC( LoadCustomHUDLayoutFile, "Loads a custom HUD layout override file." ) +#else + DEFINE_SCRIPTFUNC( LoadCustomTalkerFile, "Loads a custom talker file." ) + DEFINE_SCRIPTFUNC( LoadCustomActbusyFile, "Loads a custom actbusy file." ) #endif + DEFINE_SCRIPTFUNC( GetModName, "Gets the name of the mod. This is the name which shows up on Steam, RPC, etc." ) DEFINE_SCRIPTFUNC( IsCoreMapbase, "Indicates whether this is one of the original Mapbase mods or just a separate mod using its code." ) + + // Legacy + DEFINE_SCRIPTFUNC_NAMED( LoadCustomSoundscriptFile, "LoadSoundscriptFile", SCRIPT_HIDE ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC_NAMED( LoadCustomTalkerFile, "LoadTalkerFile", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( LoadCustomActbusyFile, "LoadActbusyFile", SCRIPT_HIDE ) +#endif + END_SCRIPTDESC(); #endif diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp index db694201..0bcdd8ae 100644 --- a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -9,6 +9,7 @@ #include "activitylist.h" #include "in_buttons.h" #include "rope_shared.h" +#include "eventlist.h" #ifdef CLIENT_DLL #include "c_ai_basenpc.h" #else @@ -311,6 +312,17 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, MOVETYPE_OBSERVER, "Move type used in GetMoveType(), etc." ); ScriptRegisterConstant( g_pScriptVM, MOVETYPE_CUSTOM, "Move type used in GetMoveType(), etc." ); + // + // Animation Stuff + // + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SERVER, "Animation event flag which indicates an event is supposed to be serverside only." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SCRIPTED, "Animation event flag with an unknown purpose." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_SHARED, "Animation event flag which indicates an event is supposed to be shared between the server and client." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_WEAPON, "Animation event flag which indicates an event is part of a weapon." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_CLIENT, "Animation event flag which indicates an event is supposed to be clientside only." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_FACEPOSER, "Animation event flag with an unknown purpose. Presumably related to Faceposer." ); + ScriptRegisterConstant( g_pScriptVM, AE_TYPE_NEWEVENTSYSTEM, "Animation event flag which indicates an event is using the new system. This is often used by class-specific events from NPCs." ); + // // Ropes // @@ -327,9 +339,75 @@ void RegisterSharedScriptConstants() ScriptRegisterConstantNamed( g_pScriptVM, Vector( ROPE_GRAVITY ), "ROPE_GRAVITY", "Default rope gravity vector." ); + // + // Sounds + // + ScriptRegisterConstant( g_pScriptVM, CHAN_REPLACE, "The sound channel used when playing sounds through console commands." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_AUTO, "The default generic sound channel." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_WEAPON, "The sound channel for player and NPC weapons." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE, "The sound channel used for dialogue, voice lines, etc." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_ITEM, "The sound channel used for generic physics impact sounds, health/suit chargers, +use sounds." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_BODY, "The sound channel used for clothing, ragdoll impacts, footsteps, knocking/pounding/punching etc." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_STREAM, "The sound channel for sounds that can be delayed by an async load, i.e. aren't responses to particular events." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_STATIC, "The sound channel for constant/background sound that doesn't require any reaction." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE2, "An additional sound channel for voices. Used in TF2 for the announcer." ); + ScriptRegisterConstant( g_pScriptVM, CHAN_VOICE_BASE, "The sound channel used for network voice data (online voice communications)." ); + + ScriptRegisterConstant( g_pScriptVM, VOL_NORM, "The standard volume value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_NORM, "The standard pitch value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_LOW, "The standard low pitch value." ); + ScriptRegisterConstant( g_pScriptVM, PITCH_HIGH, "The standard high pitch value." ); + + ScriptRegisterConstant( g_pScriptVM, SNDLVL_NONE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_20dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_25dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_30dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_35dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_40dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_45dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_50dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_55dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_IDLE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_60dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_65dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_STATIC, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_70dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_NORM, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_75dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_80dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_TALKING, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_85dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_90dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_95dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_100dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_105dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_110dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_120dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_130dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_GUNFIRE, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_140dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_150dB, "A standard value used for a sound's sound level." ); + ScriptRegisterConstant( g_pScriptVM, SNDLVL_180dB, "A standard value used for a sound's sound level." ); + + ScriptRegisterConstant( g_pScriptVM, SND_CHANGE_VOL, "Indicates a sound is a volume change to an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_CHANGE_PITCH, "Indicates a sound is a pitch change to an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_STOP, "Indicates a sound is stopping an already-playing sound." ); + ScriptRegisterConstant( g_pScriptVM, SND_SPAWNING, "Indicates a sound is spawning, used in some cases for ambients. Not networked." ); + ScriptRegisterConstant( g_pScriptVM, SND_DELAY, "Indicates a sound has an initial delay." ); + ScriptRegisterConstant( g_pScriptVM, SND_STOP_LOOPING, "Stops all looping sounds on an entity." ); + ScriptRegisterConstant( g_pScriptVM, SND_SPEAKER, "Indicates a sound is being played again by a microphone through a speaker." ); + ScriptRegisterConstant( g_pScriptVM, SND_SHOULDPAUSE, "Forces a sound to pause if the game is paused." ); + ScriptRegisterConstant( g_pScriptVM, SND_IGNORE_PHONEMES, "Prevents the entity emitting this sound from using its phonemes (no lip-syncing)." ); + ScriptRegisterConstant( g_pScriptVM, SND_IGNORE_NAME, "Used to change all sounds emitted by an entity, regardless of name." ); + ScriptRegisterConstant( g_pScriptVM, SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL, "Prevents a sound from interrupting other sounds on a channel (if the channel supports interruption)." ); + + ScriptRegisterConstant( g_pScriptVM, GENDER_NONE, "A standard value used to represent no specific gender. Usually used for sounds." ); + ScriptRegisterConstant( g_pScriptVM, GENDER_MALE, "A standard value used to represent male gender. Usually used for sounds." ); + ScriptRegisterConstant( g_pScriptVM, GENDER_FEMALE, "A standard value used to represent female gender. Usually used for sounds." ); + #ifdef GAME_DLL // - // Sound Types, Contexts, and Channels + // AI Sounds // (QueryHearSound hook can use these) // ScriptRegisterConstant( g_pScriptVM, SOUND_NONE, "Sound type used in QueryHearSound hooks, etc." ); diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index e564d2b4..aab303d8 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -323,7 +323,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for acces DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) END_SCRIPTDESC(); -BEGIN_SCRIPTDESC_ROOT_NAMED( surfacedata_t, "surfacedata_t", "Handle for accessing surface data." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( scriptsurfacedata_t, "surfacedata_t", "Handle for accessing surface data." ) DEFINE_SCRIPTFUNC( GetFriction, "The surface's friction." ) DEFINE_SCRIPTFUNC( GetThickness, "The surface's thickness." ) @@ -342,16 +342,16 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( surfacedata_t, "surfacedata_t", "Handle for accessi DEFINE_SCRIPTFUNC( GetSoundStrain, "The surface's strain sound." ) END_SCRIPTDESC(); -const char* surfacedata_t::GetSoundStepLeft() { return physprops->GetString( sounds.stepleft ); } -const char* surfacedata_t::GetSoundStepRight() { return physprops->GetString( sounds.stepright ); } -const char* surfacedata_t::GetSoundImpactSoft() { return physprops->GetString( sounds.impactSoft ); } -const char* surfacedata_t::GetSoundImpactHard() { return physprops->GetString( sounds.impactHard ); } -const char* surfacedata_t::GetSoundScrapeSmooth() { return physprops->GetString( sounds.scrapeSmooth ); } -const char* surfacedata_t::GetSoundScrapeRough() { return physprops->GetString( sounds.scrapeRough ); } -const char* surfacedata_t::GetSoundBulletImpact() { return physprops->GetString( sounds.bulletImpact ); } -const char* surfacedata_t::GetSoundRolling() { return physprops->GetString( sounds.rolling ); } -const char* surfacedata_t::GetSoundBreak() { return physprops->GetString( sounds.breakSound ); } -const char* surfacedata_t::GetSoundStrain() { return physprops->GetString( sounds.strainSound ); } +const char* scriptsurfacedata_t::GetSoundStepLeft() { return physprops->GetString( sounds.stepleft ); } +const char* scriptsurfacedata_t::GetSoundStepRight() { return physprops->GetString( sounds.stepright ); } +const char* scriptsurfacedata_t::GetSoundImpactSoft() { return physprops->GetString( sounds.impactSoft ); } +const char* scriptsurfacedata_t::GetSoundImpactHard() { return physprops->GetString( sounds.impactHard ); } +const char* scriptsurfacedata_t::GetSoundScrapeSmooth() { return physprops->GetString( sounds.scrapeSmooth ); } +const char* scriptsurfacedata_t::GetSoundScrapeRough() { return physprops->GetString( sounds.scrapeRough ); } +const char* scriptsurfacedata_t::GetSoundBulletImpact() { return physprops->GetString( sounds.bulletImpact ); } +const char* scriptsurfacedata_t::GetSoundRolling() { return physprops->GetString( sounds.rolling ); } +const char* scriptsurfacedata_t::GetSoundBreak() { return physprops->GetString( sounds.breakSound ); } +const char* scriptsurfacedata_t::GetSoundStrain() { return physprops->GetString( sounds.strainSound ); } BEGIN_SCRIPTDESC_ROOT_NAMED( CSurfaceScriptAccessor, "csurface_t", "Handle for accessing csurface_t info." ) DEFINE_SCRIPTFUNC( Name, "The surface's name." ) @@ -506,16 +506,36 @@ FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) } //----------------------------------------------------------------------------- -// +// animevent_t //----------------------------------------------------------------------------- CAnimEventTInstanceHelper g_AnimEventTInstanceHelper; -BEGIN_SCRIPTDESC_ROOT( animevent_t, "Handle for accessing animevent_t info." ) +BEGIN_SCRIPTDESC_ROOT( scriptanimevent_t, "Handle for accessing animevent_t info." ) DEFINE_SCRIPT_INSTANCE_HELPER( &g_AnimEventTInstanceHelper ) + + DEFINE_SCRIPTFUNC( GetEvent, "Gets the event number." ) + DEFINE_SCRIPTFUNC( SetEvent, "Sets the event number." ) + + DEFINE_SCRIPTFUNC( GetOptions, "Gets the event's options/parameters." ) + DEFINE_SCRIPTFUNC( SetOptions, "Sets the event's options/parameters." ) + + DEFINE_SCRIPTFUNC( GetCycle, "Gets the cycle at which the event happens." ) + DEFINE_SCRIPTFUNC( SetCycle, "Sets the cycle at which the event happens." ) + + DEFINE_SCRIPTFUNC( GetEventTime, "Gets the time the event plays." ) + DEFINE_SCRIPTFUNC( SetEventTime, "Sets the time the event plays." ) + + DEFINE_SCRIPTFUNC( GetType, "Gets the event's type flags. See the 'AE_TYPE_' set of constants for valid flags." ) + DEFINE_SCRIPTFUNC( SetType, "Sets the event's type flags. See the 'AE_TYPE_' set of constants for valid flags." ) + + DEFINE_SCRIPTFUNC( GetSource, "Gets the event's source entity." ) + DEFINE_SCRIPTFUNC( SetSource, "Sets the event's source entity." ) END_SCRIPTDESC(); bool CAnimEventTInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) { + DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey ); + animevent_t *ani = ((animevent_t *)p); if (FStrEq( pszKey, "event" )) variant = ani->event; @@ -537,6 +557,8 @@ bool CAnimEventTInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_ bool CAnimEventTInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) { + DevWarning( "VScript animevent_t.%s: animevent_t metamethod members are deprecated! Use 'script_help animevent_t' to see the correct functions.\n", pszKey ); + animevent_t *ani = ((animevent_t *)p); if (FStrEq( pszKey, "event" )) ani->event = variant; @@ -560,10 +582,58 @@ bool CAnimEventTInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_ return true; } +//----------------------------------------------------------------------------- +// EmitSound_t +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT_NAMED( ScriptEmitSound_t, "EmitSound_t", "Handle for accessing EmitSound_t info." ) + DEFINE_SCRIPT_CONSTRUCTOR() + + DEFINE_SCRIPTFUNC( GetChannel, "Gets the sound's channel." ) + DEFINE_SCRIPTFUNC( SetChannel, "Gets the sound's channel." ) + + DEFINE_SCRIPTFUNC( GetSoundName, "Gets the sound's file path or soundscript name." ) + DEFINE_SCRIPTFUNC( SetSoundName, "Sets the sound's file path or soundscript name." ) + + DEFINE_SCRIPTFUNC( GetVolume, "Gets the sound's volume. (Note that this may not apply to soundscripts)" ) + DEFINE_SCRIPTFUNC( SetVolume, "Sets the sound's volume. (Note that this may not apply to soundscripts)" ) + + DEFINE_SCRIPTFUNC( GetSoundLevel, "Gets the sound's level in decibels. (Note that this may not apply to soundscripts)" ) + DEFINE_SCRIPTFUNC( SetSoundLevel, "Sets the sound's level in decibels. (Note that this may not apply to soundscripts)" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Gets the sound's flags. See the 'SND_' set of constants for more information." ) + DEFINE_SCRIPTFUNC( SetFlags, "Sets the sound's flags. See the 'SND_' set of constants for more information." ) + + DEFINE_SCRIPTFUNC( GetSpecialDSP, "Gets the sound's special DSP setting." ) + DEFINE_SCRIPTFUNC( SetSpecialDSP, "Sets the sound's special DSP setting." ) + + DEFINE_SCRIPTFUNC( HasOrigin, "Returns true if the sound has an origin override." ) + DEFINE_SCRIPTFUNC( GetOrigin, "Gets the sound's origin override." ) + DEFINE_SCRIPTFUNC( SetOrigin, "Sets the sound's origin override." ) + DEFINE_SCRIPTFUNC( ClearOrigin, "Clears the sound's origin override if it has one." ) + + DEFINE_SCRIPTFUNC( GetSoundTime, "Gets the time the sound will begin, relative to Time()." ) + DEFINE_SCRIPTFUNC( SetSoundTime, "Sets the time the sound will begin, relative to Time()." ) + + DEFINE_SCRIPTFUNC( GetEmitCloseCaption, "Gets whether or not the sound will emit closed captioning/subtitles." ) + DEFINE_SCRIPTFUNC( SetEmitCloseCaption, "Sets whether or not the sound will emit closed captioning/subtitles." ) + + DEFINE_SCRIPTFUNC( GetWarnOnMissingCloseCaption, "Gets whether or not the sound will send a message to the console if there is no corresponding closed captioning token." ) + DEFINE_SCRIPTFUNC( SetWarnOnMissingCloseCaption, "Sets whether or not the sound will send a message to the console if there is no corresponding closed captioning token." ) + + DEFINE_SCRIPTFUNC( GetWarnOnDirectWaveReference, "Gets whether or not the sound will send a message to the console if it references a direct sound file instead of a soundscript." ) + DEFINE_SCRIPTFUNC( SetWarnOnDirectWaveReference, "Sets whether or not the sound will send a message to the console if it references a direct sound file instead of a soundscript." ) + + DEFINE_SCRIPTFUNC( GetSpeakerEntity, "Gets the sound's original source if it is being transmitted by a microphone." ) + DEFINE_SCRIPTFUNC( SetSpeakerEntity, "Sets the sound's original source if it is being transmitted by a microphone." ) + + DEFINE_SCRIPTFUNC( GetSoundScriptHandle, "Gets the sound's script handle." ) + DEFINE_SCRIPTFUNC( SetSoundScriptHandle, "Sets the sound's script handle." ) +END_SCRIPTDESC(); + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptUserCmd, "CUserCmd", "Handle for accessing CUserCmd info." ) DEFINE_SCRIPTFUNC( GetCommandNumber, "For matching server and client commands for debugging." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetTickCount, "GetTickCount", "The tick the client created this command." ) @@ -596,6 +666,32 @@ BEGIN_SCRIPTDESC_ROOT( CUserCmd, "Handle for accessing CUserCmd info." ) DEFINE_SCRIPTFUNC( SetMouseY, "Sets mouse accum in y from create move." ) END_SCRIPTDESC(); +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \ + DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \ + DEFINE_SCRIPTFUNC( Set##name, "Set " desc ) + +BEGIN_SCRIPTDESC_ROOT_NAMED( Script_AI_EnemyInfo_t, "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 + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -744,6 +840,21 @@ static HSCRIPT ScriptCreateRope( HSCRIPT hStart, HSCRIPT hEnd, int iStartAttachm return ToHScript( pRope ); } +static void EmitSoundParamsOn( HSCRIPT hParams, HSCRIPT hEnt ) +{ + CBaseEntity *pEnt = ToEnt( hEnt ); + if (!pEnt) + return; + + ScriptEmitSound_t *pParams = (ScriptEmitSound_t*)g_pScriptVM->GetInstanceValue( hParams, GetScriptDescForClass( ScriptEmitSound_t ) ); + if (!pParams) + return; + + CPASAttenuationFilter filter( pEnt, pParams->m_pSoundName ); + + CBaseEntity::EmitSound( filter, pEnt->entindex(), *pParams ); +} + //----------------------------------------------------------------------------- // Simple particle effect dispatch //----------------------------------------------------------------------------- @@ -909,6 +1020,8 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateRope, "CreateRope", "Creates a single rope between two entities. Can optionally follow specific attachments." ); + ScriptRegisterFunction( g_pScriptVM, EmitSoundParamsOn, "Play EmitSound_t params on an entity." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatcherMatch, "Matcher_Match", "Compares a string to a query using Mapbase's matcher system, supporting wildcards, RS matchers, etc." ); ScriptRegisterFunction( g_pScriptVM, Matcher_NamesMatch, "Compares a string to a query using Mapbase's matcher system using wildcards only." ); ScriptRegisterFunction( g_pScriptVM, AppearsToBeANumber, "Checks if the given string appears to be a number." ); diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.h b/sp/src/game/shared/mapbase/vscript_funcs_shared.h index 0a38e5a1..c7ad9ca7 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.h +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.h @@ -11,13 +11,41 @@ #pragma once #endif +#include "npcevent.h" +#ifdef GAME_DLL +#include "ai_memory.h" +#endif + +//----------------------------------------------------------------------------- +// Exposes surfacedata_t to VScript +//----------------------------------------------------------------------------- +struct scriptsurfacedata_t : public surfacedata_t +{ + float GetFriction() { return physics.friction; } + float GetThickness() { return physics.thickness; } + + float GetJumpFactor() { return game.jumpFactor; } + char GetMaterialChar() { return game.material; } + + const char* GetSoundStepLeft(); + const char* GetSoundStepRight(); + const char* GetSoundImpactSoft(); + const char* GetSoundImpactHard(); + const char* GetSoundScrapeSmooth(); + const char* GetSoundScrapeRough(); + const char* GetSoundBulletImpact(); + const char* GetSoundRolling(); + const char* GetSoundBreak(); + const char* GetSoundStrain(); +}; + //----------------------------------------------------------------------------- // Exposes csurface_t to VScript //----------------------------------------------------------------------------- class CSurfaceScriptAccessor { public: - CSurfaceScriptAccessor( csurface_t &surf ) { m_surf = &surf; m_surfaceData = g_pScriptVM->RegisterInstance( physprops->GetSurfaceData( m_surf->surfaceProps ) ); } + CSurfaceScriptAccessor( csurface_t &surf ) { m_surf = &surf; m_surfaceData = g_pScriptVM->RegisterInstance( reinterpret_cast(physprops->GetSurfaceData( m_surf->surfaceProps )) ); } ~CSurfaceScriptAccessor() { delete m_surfaceData; } // cplane_t stuff @@ -124,10 +152,149 @@ private: //----------------------------------------------------------------------------- // Exposes animevent_t to VScript //----------------------------------------------------------------------------- +struct scriptanimevent_t : public animevent_t +{ + int GetEvent() { return event; } + void SetEvent( int nEvent ) { event = nEvent; } + + const char *GetOptions() { return options; } + void SetOptions( const char *pOptions ) { options = pOptions; } + + float GetCycle() { return cycle; } + void SetCycle( float flCycle ) { cycle = flCycle; } + + float GetEventTime() { return eventtime; } + void SetEventTime( float flEventTime ) { eventtime = flEventTime; } + + int GetType() { return type; } + void SetType( int nType ) { eventtime = type; } + + HSCRIPT GetSource() { return ToHScript( pSource ); } + void SetSource( HSCRIPT hSource ) + { + CBaseEntity *pEnt = ToEnt( hSource ); + if (pEnt) + pSource = pEnt->GetBaseAnimating(); + } +}; + class CAnimEventTInstanceHelper : public IScriptInstanceHelper { bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); }; +//----------------------------------------------------------------------------- +// Exposes EmitSound_t to VScript +//----------------------------------------------------------------------------- +struct ScriptEmitSound_t : public EmitSound_t +{ + int GetChannel() { return m_nChannel; } + void SetChannel( int nChannel ) { m_nChannel = nChannel; } + + const char *GetSoundName() { return m_pSoundName; } + void SetSoundName( const char *pSoundName ) { m_pSoundName = pSoundName; } + + float GetVolume() { return m_flVolume; } + void SetVolume( float flVolume ) { m_flVolume = flVolume; } + + int GetSoundLevel() { return m_SoundLevel; } + void SetSoundLevel( int iSoundLevel ) { m_SoundLevel = (soundlevel_t)iSoundLevel; } + + int GetFlags() { return m_nFlags; } + void SetFlags( int nFlags ) { m_nFlags = nFlags; } + + int GetSpecialDSP() { return m_nSpecialDSP; } + void SetSpecialDSP( int nSpecialDSP ) { m_nSpecialDSP = nSpecialDSP; } + + bool HasOrigin() { return m_pOrigin ? true : false; } + const Vector &GetOrigin() { return m_pOrigin ? *m_pOrigin : vec3_origin; } + void SetOrigin( const Vector &origin ) { static Vector tempOrigin; tempOrigin = origin; m_pOrigin = &tempOrigin; } + void ClearOrigin() { m_pOrigin = NULL; } + + float GetSoundTime() { return m_flSoundTime; } + void SetSoundTime( float flSoundTime ) { m_flSoundTime = flSoundTime; } + + float GetEmitCloseCaption() { return m_bEmitCloseCaption; } + void SetEmitCloseCaption( bool bEmitCloseCaption ) { m_bEmitCloseCaption = bEmitCloseCaption; } + + float GetWarnOnMissingCloseCaption() { return m_bWarnOnMissingCloseCaption; } + void SetWarnOnMissingCloseCaption( bool bWarnOnMissingCloseCaption ) { m_bWarnOnMissingCloseCaption = bWarnOnMissingCloseCaption; } + + float GetWarnOnDirectWaveReference() { return m_bWarnOnDirectWaveReference; } + void SetWarnOnDirectWaveReference( bool bWarnOnDirectWaveReference ) { m_bWarnOnDirectWaveReference = bWarnOnDirectWaveReference; } + + int GetSpeakerEntity() { return m_nSpeakerEntity; } + void SetSpeakerEntity( int nSpeakerEntity ) { m_nSpeakerEntity = nSpeakerEntity; } + + int GetSoundScriptHandle() { return m_hSoundScriptHandle; } + void SetSoundScriptHandle( int hSoundScriptHandle ) { m_hSoundScriptHandle = hSoundScriptHandle; } +}; + +//----------------------------------------------------------------------------- +// Exposes CUserCmd to VScript +//----------------------------------------------------------------------------- +class CScriptUserCmd : public CUserCmd +{ +public: + int GetCommandNumber() { return command_number; } + + int ScriptGetTickCount() { return tick_count; } + + const QAngle& GetViewAngles() { return viewangles; } + void SetViewAngles( const QAngle& val ) { viewangles = val; } + + float GetForwardMove() { return forwardmove; } + void SetForwardMove( float val ) { forwardmove = val; } + float GetSideMove() { return sidemove; } + void SetSideMove( float val ) { sidemove = val; } + float GetUpMove() { return upmove; } + void SetUpMove( float val ) { upmove = val; } + + int GetButtons() { return buttons; } + void SetButtons( int val ) { buttons = val; } + int GetImpulse() { return impulse; } + void SetImpulse( int val ) { impulse = val; } + + int GetWeaponSelect() { return weaponselect; } + void SetWeaponSelect( int val ) { weaponselect = val; } + int GetWeaponSubtype() { return weaponsubtype; } + void SetWeaponSubtype( int val ) { weaponsubtype = val; } + + int GetRandomSeed() { return random_seed; } + + int GetMouseX() { return mousedx; } + void SetMouseX( int val ) { mousedx = val; } + int GetMouseY() { return mousedy; } + void SetMouseY( int val ) { mousedy = val; } +}; + +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +// Exposes AI_EnemyInfo_t to VScript +//----------------------------------------------------------------------------- +struct Script_AI_EnemyInfo_t : public AI_EnemyInfo_t +{ + #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 + #endif diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index b2f829a5..009aa48c 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1006,6 +1006,7 @@ class CScriptReadWriteFile : public CAutoGameSystem public: static bool FileWrite( const char *szFile, const char *szInput ); static const char *FileRead( const char *szFile ); + static bool FileExists( const char *szFile ); // NOTE: These two functions are new with Mapbase and have no Valve equivalent static bool KeyValuesWrite( const char *szFile, HSCRIPT hInput ); @@ -1111,6 +1112,23 @@ const char *CScriptReadWriteFile::FileRead( const char *szFile ) } } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CScriptReadWriteFile::FileExists( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + return g_pFullFileSystem->FileExists( pszFullName, SCRIPT_RW_PATH_ID ); +} + //----------------------------------------------------------------------------- // Get the checksum of any file. Can be used to check the existence or validity of a file. // Returns unsigned int as hex string. @@ -3046,6 +3064,7 @@ void RegisterScriptSingletons() ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnRestore, "OnRestore", FIELD_VOID, "Called when the game is restored." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileWrite, "StringToFile", "Stores the string into the file" ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileExists, "FileExists", "Returns true if the file exists." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesWrite, "KeyValuesToFile", "Stores the CScriptKeyValues into the file" ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesRead, "FileToKeyValues", "Returns the CScriptKeyValues from the file, null if no file or file is too big." ); diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 7bd4862c..d9155f32 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -398,9 +398,10 @@ bool CWeaponCustomScripted::Reload( void ) return BaseClass::Reload(); } -void CWeaponCustomScripted::Reload_NPC( void ) +void CWeaponCustomScripted::Reload_NPC( bool bPlaySound ) { - SIMPLE_VOID_OVERRIDE( Reload_NPC, NULL ); + ScriptVariant_t pArgs[] = { bPlaySound }; + SIMPLE_VOID_OVERRIDE( Reload_NPC, pArgs ); BaseClass::Reload_NPC(); } diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index f1493ce0..924f1031 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -77,7 +77,7 @@ public: void FinishReload( void ); void AbortReload( void ); bool Reload( void ); - void Reload_NPC( void ); + void Reload_NPC( bool bPlaySound = true ); // Weapon firing void PrimaryAttack( void ); // do "+ATTACK" diff --git a/sp/src/game/shared/shareddefs.h b/sp/src/game/shared/shareddefs.h index d30843dc..11f8e8aa 100644 --- a/sp/src/game/shared/shareddefs.h +++ b/sp/src/game/shared/shareddefs.h @@ -1048,4 +1048,16 @@ enum }; #endif // TF_DLL || TF_CLIENT_DLL +#ifdef MAPBASE +// Developer commentary types +enum +{ + COMMENTARY_TYPE_AUDIO, // Play commentary audio (default) + + COMMENTARY_TYPE_TEXT, // Display text data + COMMENTARY_TYPE_IMAGE, // Display an image + COMMENTARY_TYPE_SCENE, // Play a VCD file +}; +#endif + #endif // SHAREDDEFS_H diff --git a/sp/src/game/shared/usercmd.h b/sp/src/game/shared/usercmd.h index 6b87f888..04ff44ed 100644 --- a/sp/src/game/shared/usercmd.h +++ b/sp/src/game/shared/usercmd.h @@ -127,39 +127,6 @@ public: impulse = 0; } -#ifdef MAPBASE_VSCRIPT // These functions are needed for exposing CUserCmd to VScript. - int GetCommandNumber() { return command_number; } - - int ScriptGetTickCount() { return tick_count; } - - const QAngle& GetViewAngles() { return viewangles; } - void SetViewAngles( const QAngle& val ) { viewangles = val; } - - float GetForwardMove() { return forwardmove; } - void SetForwardMove( float val ) { forwardmove = val; } - float GetSideMove() { return sidemove; } - void SetSideMove( float val ) { sidemove = val; } - float GetUpMove() { return upmove; } - void SetUpMove( float val ) { upmove = val; } - - int GetButtons() { return buttons; } - void SetButtons( int val ) { buttons = val; } - int GetImpulse() { return impulse; } - void SetImpulse( int val ) { impulse = val; } - - int GetWeaponSelect() { return weaponselect; } - void SetWeaponSelect( int val ) { weaponselect = val; } - int GetWeaponSubtype() { return weaponsubtype; } - void SetWeaponSubtype( int val ) { weaponsubtype = val; } - - int GetRandomSeed() { return random_seed; } - - int GetMouseX() { return mousedx; } - void SetMouseX( int val ) { mousedx = val; } - int GetMouseY() { return mousedy; } - void SetMouseY( int val ) { mousedy = val; } -#endif - // For matching server and client commands for debugging int command_number; diff --git a/sp/src/public/vphysics_interface.h b/sp/src/public/vphysics_interface.h index d6e61935..1852ab8c 100644 --- a/sp/src/public/vphysics_interface.h +++ b/sp/src/public/vphysics_interface.h @@ -961,30 +961,6 @@ struct surfacedata_t surfacegameprops_t game; // Game data / properties surfacesoundhandles_t soundhandles; - -#ifdef MAPBASE_VSCRIPT - // These functions are for the VScript class description. - - float GetFriction() { return physics.friction; } - float GetThickness() { return physics.thickness; } - - float GetJumpFactor() { return game.jumpFactor; } - char GetMaterialChar() { return game.material; } - -#if defined(CLIENT_DLL) || defined(GAME_DLL) - const char* GetSoundStepLeft(); - const char* GetSoundStepRight(); - const char* GetSoundImpactSoft(); - const char* GetSoundImpactHard(); - const char* GetSoundScrapeSmooth(); - const char* GetSoundScrapeRough(); - const char* GetSoundBulletImpact(); - const char* GetSoundRolling(); - const char* GetSoundBreak(); - const char* GetSoundStrain(); -#endif - -#endif }; #define VPHYSICS_SURFACEPROPS_INTERFACE_VERSION "VPhysicsSurfaceProps001" diff --git a/sp/src/responserules/runtime/response_system.cpp b/sp/src/responserules/runtime/response_system.cpp index 156bd45f..d294316d 100644 --- a/sp/src/responserules/runtime/response_system.cpp +++ b/sp/src/responserules/runtime/response_system.cpp @@ -192,6 +192,9 @@ CResponseSystem::CResponseSystem() : token[0] = 0; m_bUnget = false; m_bCustomManagable = false; +#ifdef MAPBASE + m_bInProspective = false; +#endif BuildDispatchTables(); } @@ -1008,11 +1011,13 @@ int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, } if ( slot != -1 ) + { #ifdef MAPBASE - // Don't mark responses as used in prospective mode - if (m_bInProspective == false) + // Don't mark responses as used in prospective mode + if (m_bInProspective == false) #endif - g->MarkResponseUsed( slot ); + g->MarkResponseUsed( slot ); + } // Revert fake depletion of unavailable choices RevertFakedDepletes( g ); diff --git a/sp/src/responserules/runtime/response_system.h b/sp/src/responserules/runtime/response_system.h index b675e816..a862d761 100644 --- a/sp/src/responserules/runtime/response_system.h +++ b/sp/src/responserules/runtime/response_system.h @@ -58,6 +58,9 @@ namespace ResponseRules bool IsCustomManagable() { return m_bCustomManagable; } +#ifdef MAPBASE + virtual +#endif void Clear(); void DumpDictionary( const char *pszName ); diff --git a/sp/src/vgui2/vgui_controls/AnimationController.cpp b/sp/src/vgui2/vgui_controls/AnimationController.cpp index 6da33a38..886b4291 100644 --- a/sp/src/vgui2/vgui_controls/AnimationController.cpp +++ b/sp/src/vgui2/vgui_controls/AnimationController.cpp @@ -31,6 +31,11 @@ using namespace vgui; static CUtlSymbolTable g_ScriptSymbols(0, 128, true); +#ifdef MAPBASE +// Allows animation sequences to be overridden by map-specific files +extern bool g_bUsingCustomHudAnimations; +#endif + // singleton accessor for animation controller for use by the vgui controls namespace vgui { @@ -317,11 +322,35 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) return false; } - int seqIndex; + int seqIndex = -1; UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token); - // Create a new sequence - seqIndex = m_Sequences.AddToTail(); +#ifdef MAPBASE + if (g_bUsingCustomHudAnimations) + { + // look through for the sequence + for (seqIndex = 0; seqIndex < m_Sequences.Count(); seqIndex++) + { + if (m_Sequences[seqIndex].name == nameIndex) + break; + } + + if (seqIndex >= m_Sequences.Count()) + seqIndex = -1; + else + { + // Clear some stuff + m_Sequences[seqIndex].cmdList.RemoveAll(); + } + } + + if (seqIndex == -1) +#endif + { + // Create a new sequence + seqIndex = m_Sequences.AddToTail(); + } + AnimSequence_t &seq = m_Sequences[seqIndex]; seq.name = nameIndex; seq.duration = 0.0f; diff --git a/sp/src/vgui2/vgui_controls/Panel.cpp b/sp/src/vgui2/vgui_controls/Panel.cpp index 6054fd29..dd49f97a 100644 --- a/sp/src/vgui2/vgui_controls/Panel.cpp +++ b/sp/src/vgui2/vgui_controls/Panel.cpp @@ -64,6 +64,13 @@ static char *CopyString( const char *in ) return n; } +#ifdef MAPBASE +ConVar vgui_mapbase_custom_schemes( "vgui_mapbase_custom_schemes", "1" ); + +// This is used in mapbase_shared.cpp +HScheme g_iCustomClientSchemeOverride; +#endif + #if defined( VGUI_USEDRAGDROP ) //----------------------------------------------------------------------------- // Purpose: @@ -1612,17 +1619,31 @@ void Panel::DeletePanel() //----------------------------------------------------------------------------- HScheme Panel::GetScheme() { + HScheme iScheme; + if (m_iScheme) { - return m_iScheme; // return our internal scheme + iScheme = m_iScheme; // return our internal scheme } - - if (GetVParent()) // recurse down the heirarchy + else if (GetVParent()) // recurse down the heirarchy { - return ipanel()->GetScheme(GetVParent()); + iScheme = ipanel()->GetScheme(GetVParent()); + } + else + { + iScheme = scheme()->GetDefaultScheme(); } - return scheme()->GetDefaultScheme(); +#ifdef MAPBASE + // If a custom client scheme is available, use the custom scheme. + // TODO: Need a better way to detect that this panel actually uses ClientScheme.res + if (g_iCustomClientSchemeOverride != 0 && iScheme == scheme()->GetScheme( "ClientScheme" ) && vgui_mapbase_custom_schemes.GetBool()) + { + return g_iCustomClientSchemeOverride; + } +#endif + + return iScheme; } //----------------------------------------------------------------------------- diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index fa4c15d2..cd5fe6f6 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -2669,44 +2669,28 @@ void SquirrelVM::RegisterEnum(ScriptEnumDesc_t* pEnumDesc) if (!pEnumDesc) return; + sq_newtableex(vm_, pEnumDesc->m_ConstantBindings.Count()); + sq_pushconsttable(vm_); + sq_pushstring(vm_, pEnumDesc->m_pszScriptName, -1); - - // Check if class name is already taken - if (sq_get(vm_, -2) == SQ_OK) - { - HSQOBJECT obj; - sq_resetobject(&obj); - sq_getstackobj(vm_, -1, &obj); - if (!sq_isnull(obj)) - { - sq_pop(vm_, 2); - return; - } - } - - sq_pop(vm_, 1); - - // HACKHACK: I have no idea how to declare enums with the current API. - // For now, we'll just cram everything into a script buffer and compile it. (Blixibon) - char szScript[2048]; - V_snprintf( szScript, sizeof(szScript), "enum %s {\n", pEnumDesc->m_pszScriptName ); + sq_push(vm_, -3); + sq_rawset(vm_, -3); for (int i = 0; i < pEnumDesc->m_ConstantBindings.Count(); ++i) { auto& scriptConstant = pEnumDesc->m_ConstantBindings[i]; + sq_pushstring(vm_, scriptConstant.m_pszScriptName, -1); + PushVariant(vm_, scriptConstant.m_data); + sq_rawset(vm_, -4); + char szValue[64]; GetVariantScriptString( scriptConstant.m_data, szValue, sizeof(szValue) ); - - V_snprintf( szScript, sizeof(szScript), "%s%s = %s\n", szScript, scriptConstant.m_pszScriptName, szValue ); - RegisterConstantDocumentation(vm_, &scriptConstant, szValue, pEnumDesc); } - V_strcat_safe( szScript, "}" ); - - Run( szScript ); + sq_pop(vm_, 2); RegisterEnumDocumentation(vm_, pEnumDesc); }